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
master
Ivan 4 years ago committed by GitHub
parent a7ab672b7d
commit f9b4217af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -40,7 +40,7 @@ go install
## Usage ## Usage
`smug <command> <project>[:window name] [-w window name]`. `smug <command> <project>[:window name] [-w window name]... [--attach]`.
### Examples ### Examples

@ -39,8 +39,12 @@ func main() {
switch options.Command { switch options.Command {
case "start": case "start":
fmt.Println("Starting a new session...") if len(options.Windows) == 0 {
err = smug.Start(*config, options.Windows) fmt.Println("Starting a new session...")
} else {
fmt.Println("Starting new windows...")
}
err = smug.Start(*config, options.Windows, options.Attach)
if err != nil { if err != nil {
fmt.Println("Oops, an error occurred! Rolling back...") fmt.Println("Oops, an error occurred! Rolling back...")
smug.Stop(*config, options.Windows) smug.Stop(*config, options.Windows)

@ -9,7 +9,7 @@ import (
const usage = `Smug - tmux session manager. const usage = `Smug - tmux session manager.
Usage: Usage:
smug <command> <project> [-w <window>]... smug <command> <project> [-w <window>]... [--attach]
Options: Options:
-w List of windows to start. If session exists, those windows will be attached to current session. -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 Command string
Project string Project string
Windows []string Windows []string
Attach bool
} }
func ParseOptions(p docopt.Parser, argv []string) (Options, error) { func ParseOptions(p docopt.Parser, argv []string) (Options, error) {
arguments, err := p.ParseArgs(usage, argv, "") arguments, err := p.ParseArgs(usage, argv, "")
if err != nil { if err != nil {
return Options{}, err return Options{}, err
} }
cmd, err := arguments.String("<command>") cmd, err := arguments.String("<command>")
if err != nil { if err != nil {
return Options{}, err return Options{}, err
} }
project, err := arguments.String("<project>") project, err := arguments.String("<project>")
if err != nil {
return Options{}, err
}
attach, err := arguments.Bool("--attach")
if err != nil { if err != nil {
return Options{}, err return Options{}, err
} }
@ -57,5 +60,5 @@ func ParseOptions(p docopt.Parser, argv []string) (Options, error) {
windows = arguments["-w"].([]string) windows = arguments["-w"].([]string)
} }
return Options{cmd, project, windows}, nil return Options{cmd, project, windows, attach}, nil
} }

@ -13,15 +13,19 @@ var usageTestTable = []struct {
}{ }{
{ {
[]string{"start", "smug"}, []string{"start", "smug"},
Options{"start", "smug", []string{}}, Options{"start", "smug", []string{}, false},
}, },
{ {
[]string{"start", "smug", "-wfoo"}, []string{"start", "smug", "-wfoo"},
Options{"start", "smug", []string{"foo"}}, Options{"start", "smug", []string{"foo"}, false},
}, },
{ {
[]string{"start", "smug:foo,bar"}, []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},
}, },
} }

@ -49,6 +49,16 @@ func (smug Smug) execShellCommands(commands []string, path string) error {
return nil 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 { func (smug Smug) Stop(config Config, windows []string) error {
if len(windows) == 0 { if len(windows) == 0 {
@ -72,7 +82,7 @@ func (smug Smug) Stop(config Config, windows []string) error {
return nil 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 ses string
var err error var err error
@ -85,15 +95,27 @@ func (smug Smug) Start(config Config, windows []string) error {
return err 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 { if err != nil {
return err return err
} }
} else { } else {
ses = config.Session + ":" 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)) { if (len(windows) == 0 && w.Manual) || (len(windows) > 0 && !Contains(windows, w.Name)) {
continue continue
} }
@ -103,9 +125,24 @@ func (smug Smug) Start(config Config, windows []string) error {
windowRoot = filepath.Join(sessionRoot, w.Root) windowRoot = filepath.Join(sessionRoot, w.Root)
} }
window, err := smug.tmux.NewWindow(ses, w.Name, windowRoot, w.Commands) var window string
if err != nil {
return err 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 { for _, p := range w.Panes {
@ -132,25 +169,7 @@ func (smug Smug) Start(config Config, windows []string) error {
} }
if len(windows) == 0 { if len(windows) == 0 {
windows, err := smug.tmux.ListWindows(ses) smug.switchOrAttach(ses, windows, attach)
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
}
} }
return nil return nil

@ -23,11 +23,8 @@ var testTable = []struct {
"tmux has-session -t ses", "tmux has-session -t ses",
"/bin/sh -c command1", "/bin/sh -c command1",
"/bin/sh -c command2", "/bin/sh -c command2",
"tmux new -Pd -s ses", "tmux new -Pd -s ses -n -c root",
"tmux list-windows -t ses: -F #{window_index}", "tmux attach -d -t ses:",
"tmux kill-window -t ses:ses:",
"tmux move-window -r",
"tmux attach -t ses:ses:",
}, },
[]string{ []string{
"tmux kill-session -t ses", "tmux kill-session -t ses",
@ -62,14 +59,10 @@ var testTable = []struct {
}, },
[]string{ []string{
"tmux has-session -t ses", "tmux has-session -t ses",
"tmux new -Pd -s ses", "tmux new -Pd -s ses -n win1 -c root",
"tmux neww -Pd -t ses: -n win1 -c root", "tmux split-window -Pd -t ses:win1 -c root -h",
"tmux split-window -Pd -t ses: -c root -h",
"tmux select-layout -t ses:win1 main-horizontal", "tmux select-layout -t ses:win1 main-horizontal",
"tmux list-windows -t ses: -F #{window_index}", "tmux attach -d -t ses:",
"tmux kill-window -t ses:ses:",
"tmux move-window -r",
"tmux attach -t ses:ses:",
}, },
[]string{ []string{
"/bin/sh -c stop1", "/bin/sh -c stop1",
@ -95,8 +88,7 @@ var testTable = []struct {
}, },
[]string{ []string{
"tmux has-session -t ses", "tmux has-session -t ses",
"tmux new -Pd -s ses", "tmux new -Pd -s ses -n win2 -c root",
"tmux neww -Pd -t ses: -n win2 -c root",
"tmux select-layout -t ses:win2 even-horizontal", "tmux select-layout -t ses:win2 even-horizontal",
}, },
[]string{ []string{
@ -131,7 +123,7 @@ func TestStartSession(t *testing.T) {
tmux := Tmux{commander} tmux := Tmux{commander}
smug := Smug{tmux, commander} smug := Smug{tmux, commander}
err := smug.Start(params.config, params.windows) err := smug.Start(params.config, params.windows, false)
if err != nil { if err != nil {
t.Fatalf("error %v", err) t.Fatalf("error %v", err)
} }

@ -23,8 +23,8 @@ type Tmux struct {
commander Commander commander Commander
} }
func (tmux Tmux) NewSession(name string) (string, error) { func (tmux Tmux) NewSession(name string, root string, windowName string) (string, error) {
cmd := exec.Command("tmux", "new", "-Pd", "-s", name) cmd := exec.Command("tmux", "new", "-Pd", "-s", name, "-n", windowName, "-c", root)
return tmux.commander.Exec(cmd) return tmux.commander.Exec(cmd)
} }
@ -40,22 +40,10 @@ func (tmux Tmux) KillWindow(target string) error {
return err 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) cmd := exec.Command("tmux", "neww", "-Pd", "-t", target, "-n", name, "-c", root)
window, err := tmux.commander.Exec(cmd) return 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
} }
func (tmux Tmux) SendKeys(target string, command string) error { 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 { 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.Stdin = stdin
cmd.Stdout = stdout 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) return tmux.commander.ExecSilently(cmd)
} }
func (tmux Tmux) RenumberWindows() error { func (tmux Tmux) RenumberWindows(target string) error {
cmd := exec.Command("tmux", "move-window", "-r") cmd := exec.Command("tmux", "move-window", "-r")
_, err := tmux.commander.Exec(cmd) _, err := tmux.commander.Exec(cmd)
return err return err
@ -127,3 +115,8 @@ func (tmux Tmux) ListWindows(target string) ([]string, error) {
return strings.Split(output, "\n"), nil 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)
}

Loading…
Cancel
Save