package main import ( "fmt" "os" "os/exec" "path/filepath" "strings" "syscall" "github.com/ktr0731/go-fuzzyfinder" "gopkg.in/yaml.v3" ) type Window struct { Path string `yaml:"path"` Name string `yaml:"name"` Program string `yaml:"program"` } func (w Window) ExpandedPath() string { expanded, err := expandTilde(w.Path) if err != nil { panic(err) } return expanded } type Config map[string][]Window const ( tmux = "tmux" mkdir = "mkdir" ) func main() { _, err := exec.LookPath(tmux) if err != nil { panic(err) } _, err = exec.LookPath(mkdir) if err != nil { panic(err) } homeDir, err := os.UserHomeDir() if err != nil { panic(err) } configFilePath := filepath.Join(homeDir, ".sesh.yaml") f, err := os.Open(configFilePath) if err != nil { panic(err) } var config Config err = yaml.NewDecoder(f).Decode(&config) if err != nil { panic(err) } seshNames := make([]string, 0, len(config)) for seshName := range config { seshNames = append(seshNames, seshName) } i, err := fuzzyfinder.Find(seshNames, func(i int) string { return seshNames[i] }) if err != nil { return } seshName := seshNames[i] if HasSession(seshName) { TurnIntoTmux(seshName) } else { sesh := config[seshName] if len(sesh) == 0 { fmt.Println("no windows in session") return } for _, window := range sesh { // TODO: use go stdandard library instead exec.Command(mkdir, "-p", window.ExpandedPath()).Run() } for _, window := range sesh { NewWindowInSession(seshName, window) } exec.Command(tmux, "select-window", "-t", fmt.Sprintf("%s:^", seshName)).Run() TurnIntoTmux(seshName) } } func HasSession(seshName string) bool { res := exec.Command(tmux, "has-session", "-t", seshName).Run() return res == nil } func expandTilde(path string) (string, error) { if !strings.HasPrefix(path, "~") { return path, nil } userHomeDir, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(userHomeDir, path[1:]), nil } func NewWindowInSession(seshName string, window Window) { if seshName == "" { panic("missing seshName") } var args = []string{} if HasSession(seshName) { args = append(args, "new-window", "-t", seshName) } else { args = append(args, "new-session", "-d", "-s", seshName) } if window.ExpandedPath() != "" { args = append(args, "-c", window.ExpandedPath()) } if window.Name != "" { args = append(args, "-n", window.Name) } cmd := exec.Command(tmux, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin err := cmd.Run() if err != nil { panic(err) } if window.Program != "" { var carriageReturn = string([]byte{13}) cmd = exec.Command(tmux, "send-keys", "-t", fmt.Sprintf("%s:$", seshName), window.Program, carriageReturn) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin err := cmd.Run() if err != nil { panic(err) } } } func TurnIntoTmux(sessionName string) { _, isInTmux := os.LookupEnv("TMUX") tmuxPath, err := exec.LookPath(tmux) if err != nil { panic(err) } if isInTmux { err = syscall.Exec(tmuxPath, []string{tmuxPath, "switch-client", "-t", sessionName}, os.Environ()) } else { err = syscall.Exec(tmuxPath, []string{tmuxPath, "attach", "-t", sessionName}, os.Environ()) } if err != nil { panic(err) } }