From f9b4217af3413b6626a520f9c2e3a0c45cbc96ca Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 24 Dec 2020 01:38:39 +0200 Subject: [PATCH] Allow creating sessions without nesting (#16) * Allow creating sessions without nesting * New `--attach` flag allows you to switch a client when you're inside tmux session * If the session already exists, Smug won't throw an error. Instead, it will attach you to an existing session --- README.md | 2 +- main.go | 8 ++++-- options.go | 11 +++++--- options_test.go | 10 ++++--- smug.go | 69 +++++++++++++++++++++++++++++++------------------ smug_test.go | 22 +++++----------- tmux.go | 29 ++++++++------------- 7 files changed, 83 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 8743635..d1fbf79 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ go install ## Usage -`smug [:window name] [-w window name]`. +`smug [:window name] [-w window name]... [--attach]`. ### Examples diff --git a/main.go b/main.go index 2339d1f..5c10965 100644 --- a/main.go +++ b/main.go @@ -39,8 +39,12 @@ func main() { switch options.Command { case "start": - fmt.Println("Starting a new session...") - err = smug.Start(*config, options.Windows) + if len(options.Windows) == 0 { + fmt.Println("Starting a new session...") + } else { + fmt.Println("Starting new windows...") + } + err = smug.Start(*config, options.Windows, options.Attach) if err != nil { fmt.Println("Oops, an error occurred! Rolling back...") smug.Stop(*config, options.Windows) diff --git a/options.go b/options.go index f23a0b4..cde6dac 100644 --- a/options.go +++ b/options.go @@ -9,7 +9,7 @@ import ( const usage = `Smug - tmux session manager. Usage: - smug [-w ]... + smug [-w ]... [--attach] Options: -w List of windows to start. If session exists, those windows will be attached to current session. @@ -26,23 +26,26 @@ type Options struct { Command string Project string Windows []string + Attach bool } func ParseOptions(p docopt.Parser, argv []string) (Options, error) { arguments, err := p.ParseArgs(usage, argv, "") - if err != nil { return Options{}, err } cmd, err := arguments.String("") - if err != nil { return Options{}, err } project, err := arguments.String("") + if err != nil { + return Options{}, err + } + attach, err := arguments.Bool("--attach") if err != nil { return Options{}, err } @@ -57,5 +60,5 @@ func ParseOptions(p docopt.Parser, argv []string) (Options, error) { windows = arguments["-w"].([]string) } - return Options{cmd, project, windows}, nil + return Options{cmd, project, windows, attach}, nil } diff --git a/options_test.go b/options_test.go index b0cfc8a..024768d 100644 --- a/options_test.go +++ b/options_test.go @@ -13,15 +13,19 @@ var usageTestTable = []struct { }{ { []string{"start", "smug"}, - Options{"start", "smug", []string{}}, + Options{"start", "smug", []string{}, false}, }, { []string{"start", "smug", "-wfoo"}, - Options{"start", "smug", []string{"foo"}}, + Options{"start", "smug", []string{"foo"}, false}, }, { []string{"start", "smug:foo,bar"}, - Options{"start", "smug", []string{"foo", "bar"}}, + Options{"start", "smug", []string{"foo", "bar"}, false}, + }, + { + []string{"start", "smug", "--attach"}, + Options{"start", "smug", []string{}, true}, }, } diff --git a/smug.go b/smug.go index 2cae07e..43f37b5 100644 --- a/smug.go +++ b/smug.go @@ -49,6 +49,16 @@ func (smug Smug) execShellCommands(commands []string, path string) error { return nil } +func (smug Smug) switchOrAttach(ses string, windows []string, attach bool) error { + insideTmuxSession := os.Getenv("TERM") == "screen" + if insideTmuxSession && attach { + return smug.tmux.SwitchClient(ses) + } else if !insideTmuxSession { + return smug.tmux.Attach(ses, os.Stdin, os.Stdout, os.Stderr) + } + return nil +} + func (smug Smug) Stop(config Config, windows []string) error { if len(windows) == 0 { @@ -72,7 +82,7 @@ func (smug Smug) Stop(config Config, windows []string) error { return nil } -func (smug Smug) Start(config Config, windows []string) error { +func (smug Smug) Start(config Config, windows []string, attach bool) error { var ses string var err error @@ -85,15 +95,27 @@ func (smug Smug) Start(config Config, windows []string) error { return err } - ses, err = smug.tmux.NewSession(config.Session) + var defaultWindowName string + if len(windows) > 0 { + defaultWindowName = windows[0] + } else if len(config.Windows) > 0 { + defaultWindowName = config.Windows[0].Name + } + + ses, err = smug.tmux.NewSession(config.Session, sessionRoot, defaultWindowName) if err != nil { return err } } else { ses = config.Session + ":" + if len(windows) == 0 { + smug.switchOrAttach(ses, windows, attach) + return nil + } } - for _, w := range config.Windows { + var createdWindows []string + for wIndex, w := range config.Windows { if (len(windows) == 0 && w.Manual) || (len(windows) > 0 && !Contains(windows, w.Name)) { continue } @@ -103,9 +125,24 @@ func (smug Smug) Start(config Config, windows []string) error { windowRoot = filepath.Join(sessionRoot, w.Root) } - window, err := smug.tmux.NewWindow(ses, w.Name, windowRoot, w.Commands) - if err != nil { - return err + var window string + + if (wIndex == 0 || len(createdWindows) == 0) && !sessionExists { + window = ses + w.Name + } else { + + window, err = smug.tmux.NewWindow(ses, w.Name, windowRoot) + if err != nil { + return err + } + createdWindows = append(createdWindows, window) + } + + for _, c := range w.Commands { + err = smug.tmux.SendKeys(window, c) + if err != nil { + return err + } } for _, p := range w.Panes { @@ -132,25 +169,7 @@ func (smug Smug) Start(config Config, windows []string) error { } if len(windows) == 0 { - windows, err := smug.tmux.ListWindows(ses) - if err != nil { - return err - } - - err = smug.tmux.KillWindow(ses + windows[0]) - if err != nil { - return err - } - - err = smug.tmux.RenumberWindows() - if err != nil { - return err - } - - err = smug.tmux.Attach(ses + windows[0], os.Stdin, os.Stdout, os.Stderr) - if err != nil { - return err - } + smug.switchOrAttach(ses, windows, attach) } return nil diff --git a/smug_test.go b/smug_test.go index 4b6aea7..47144ae 100644 --- a/smug_test.go +++ b/smug_test.go @@ -23,11 +23,8 @@ var testTable = []struct { "tmux has-session -t ses", "/bin/sh -c command1", "/bin/sh -c command2", - "tmux new -Pd -s ses", - "tmux list-windows -t ses: -F #{window_index}", - "tmux kill-window -t ses:ses:", - "tmux move-window -r", - "tmux attach -t ses:ses:", + "tmux new -Pd -s ses -n -c root", + "tmux attach -d -t ses:", }, []string{ "tmux kill-session -t ses", @@ -62,14 +59,10 @@ var testTable = []struct { }, []string{ "tmux has-session -t ses", - "tmux new -Pd -s ses", - "tmux neww -Pd -t ses: -n win1 -c root", - "tmux split-window -Pd -t ses: -c root -h", + "tmux new -Pd -s ses -n win1 -c root", + "tmux split-window -Pd -t ses:win1 -c root -h", "tmux select-layout -t ses:win1 main-horizontal", - "tmux list-windows -t ses: -F #{window_index}", - "tmux kill-window -t ses:ses:", - "tmux move-window -r", - "tmux attach -t ses:ses:", + "tmux attach -d -t ses:", }, []string{ "/bin/sh -c stop1", @@ -95,8 +88,7 @@ var testTable = []struct { }, []string{ "tmux has-session -t ses", - "tmux new -Pd -s ses", - "tmux neww -Pd -t ses: -n win2 -c root", + "tmux new -Pd -s ses -n win2 -c root", "tmux select-layout -t ses:win2 even-horizontal", }, []string{ @@ -131,7 +123,7 @@ func TestStartSession(t *testing.T) { tmux := Tmux{commander} smug := Smug{tmux, commander} - err := smug.Start(params.config, params.windows) + err := smug.Start(params.config, params.windows, false) if err != nil { t.Fatalf("error %v", err) } diff --git a/tmux.go b/tmux.go index f283c66..cb23050 100644 --- a/tmux.go +++ b/tmux.go @@ -23,8 +23,8 @@ type Tmux struct { commander Commander } -func (tmux Tmux) NewSession(name string) (string, error) { - cmd := exec.Command("tmux", "new", "-Pd", "-s", name) +func (tmux Tmux) NewSession(name string, root string, windowName string) (string, error) { + cmd := exec.Command("tmux", "new", "-Pd", "-s", name, "-n", windowName, "-c", root) return tmux.commander.Exec(cmd) } @@ -40,22 +40,10 @@ func (tmux Tmux) KillWindow(target string) error { return err } -func (tmux Tmux) NewWindow(target string, name string, root string, commands []string) (string, error) { +func (tmux Tmux) NewWindow(target string, name string, root string) (string, error) { cmd := exec.Command("tmux", "neww", "-Pd", "-t", target, "-n", name, "-c", root) - window, err := tmux.commander.Exec(cmd) - if err != nil { - return "", err - } - - for _, c := range commands { - err = tmux.SendKeys(window, c) - if err != nil { - return "", err - } - } - - return window, nil + return tmux.commander.Exec(cmd) } func (tmux Tmux) SendKeys(target string, command string) error { @@ -65,7 +53,7 @@ func (tmux Tmux) SendKeys(target string, command string) error { } func (tmux Tmux) Attach(target string, stdin *os.File, stdout *os.File, stderr *os.File) error { - cmd := exec.Command("tmux", "attach", "-t", target) + cmd := exec.Command("tmux", "attach", "-d", "-t", target) cmd.Stdin = stdin cmd.Stdout = stdout @@ -74,7 +62,7 @@ func (tmux Tmux) Attach(target string, stdin *os.File, stdout *os.File, stderr * return tmux.commander.ExecSilently(cmd) } -func (tmux Tmux) RenumberWindows() error { +func (tmux Tmux) RenumberWindows(target string) error { cmd := exec.Command("tmux", "move-window", "-r") _, err := tmux.commander.Exec(cmd) return err @@ -127,3 +115,8 @@ func (tmux Tmux) ListWindows(target string) ([]string, error) { return strings.Split(output, "\n"), nil } + +func (tmux Tmux) SwitchClient(target string) error { + cmd := exec.Command("tmux", "switch-client", "-t", target) + return tmux.commander.ExecSilently(cmd) +}