Skip to content

Commit 6e43f37

Browse files
✨ Allow session windows to be configured (#237)
* Makefile: Used go env command to get output location * Config: Edited types for window configs * Tmux: added starting a new windows * feat: Loaded windows into config * Tests: Added mock NewWindow implementation. TODO: implement * fix: renamed toml name of windows (was startup_script because of copy paste) * fix: typo in tmux wrapper where NewWindow would run new-session * typo: in lister/config.go error message * fix: Windows not matching properly with sessions when listing configuration * fix: windows being detached when created * fix: array of window confs initialized with an element causing extra window to be created * model: removed windows from SeshSessions as not needed * feat: Starting session at first window * mock: generated mock tmux files * feat: if window path is not set, use path of the session * Update README with multiple windows config * typo: Fixed some typos in README * connector/tmux.go: add error checks * fix indentation * moved loading window configs to connection logic * removed disable_startup_script for window configuration * add window name to NewWndow function * fix: moved new window logic to startup package --------- Co-authored-by: Josh Medeski <[email protected]>
1 parent b766fd7 commit 6e43f37

File tree

9 files changed

+222
-19
lines changed

9 files changed

+222
-19
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ test:
66
@go test -cover -bench=. -benchmem -race ./... -coverprofile=coverage.out
77

88
build:
9-
@go build -ldflags ${BUILD_FLAGS} -o $(shell echo $$GOPATH)/bin/sesh
9+
@go build -ldflags ${BUILD_FLAGS} -o $(shell go env GOPATH)/bin/sesh

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,30 @@ startup_command = "nvim tmux.conf"
295295
preview_command = "bat --color=always ~/c/dotfiles/.config/tmux/tmux.conf"
296296
```
297297

298+
### Multiple windows
299+
300+
If you want your session to have multiple windows you can define windows in your configuration. You can then use these window layouts in your sessions. These windows can be reused as many times as you want and you can add as many windows to each session as you want.
301+
302+
Note: If you do not specify a path in the window, it will use the session's path.
303+
304+
```toml
305+
[[session]]
306+
name = "Downloads 📥"
307+
path = "~/Downloads"
308+
startup_command = "ls"
309+
310+
[[session]]
311+
name = "tmux config"
312+
path = "~/c/dotfiles/.config/tmux"
313+
startup_command = "nvim tmux.conf"
314+
preview_command = "bat --color=always ~/c/dotfiles/.config/tmux/tmux.conf"
315+
windows = [ "git" ]
316+
317+
[[window]]
318+
name = "git"
319+
startup_script = "git pull"
320+
```
321+
298322
### Listing Configurations
299323

300324
Session configurations will load by default if no flags are provided (the return after tmux sessions and before zoxide results). If you want to explicitly list them, you can use the `-c` flag.

lister/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"github.com/joshmedeski/sesh/v2/model"
77
)
88

9-
func configKey(name string) string {
9+
func ConfigKey(name string) string {
1010
return fmt.Sprintf("config:%s", name)
1111
}
1212

@@ -15,7 +15,7 @@ func listConfig(l *RealLister) (model.SeshSessions, error) {
1515
directory := make(model.SeshSessionMap)
1616
for _, session := range l.config.SessionConfigs {
1717
if session.Name != "" {
18-
key := configKey(session.Name)
18+
key := ConfigKey(session.Name)
1919
orderedIndex = append(orderedIndex, key)
2020
path, err := l.home.ExpandHome(session.Path)
2121
if err != nil {
@@ -34,6 +34,7 @@ func listConfig(l *RealLister) (model.SeshSessions, error) {
3434
PreviewCommand: session.PreviewCommand,
3535
DisableStartupCommand: session.DisableStartCommand,
3636
Tmuxinator: session.Tmuxinator,
37+
WindowNames: session.Windows,
3738
}
3839
}
3940
}
@@ -44,7 +45,7 @@ func listConfig(l *RealLister) (model.SeshSessions, error) {
4445
}
4546

4647
func (l *RealLister) FindConfigSession(name string) (model.SeshSession, bool) {
47-
key := configKey(name)
48+
key := ConfigKey(name)
4849
sessions, _ := listConfig(l)
4950
if session, exists := sessions.Directory[key]; exists {
5051
return session, exists

model/config.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@ type (
66
DefaultSessionConfig DefaultSessionConfig `toml:"default_session"`
77
Blacklist []string `toml:"blacklist"`
88
SessionConfigs []SessionConfig `toml:"session"`
9-
SortOrder []string `toml:"sort_order"`
9+
SortOrder []string `toml:"sort_order"`
10+
WindowConfigs []WindowConfig `toml:"window"`
1011
}
1112

1213
DefaultSessionConfig struct {
1314
// TODO: mention breaking change in v2 release notes
1415
// StartupScript string `toml:"startup_script"`
15-
StartupCommand string `toml:"startup_command"`
16-
Tmuxp string `toml:"tmuxp"`
17-
Tmuxinator string `toml:"tmuxinator"`
18-
PreviewCommand string `toml:"preview_command"`
16+
StartupCommand string `toml:"startup_command"`
17+
Tmuxp string `toml:"tmuxp"`
18+
Tmuxinator string `toml:"tmuxinator"`
19+
PreviewCommand string `toml:"preview_command"`
20+
Windows []string `toml:"windows"`
1921
}
2022

2123
SessionConfig struct {
@@ -24,4 +26,10 @@ type (
2426
DisableStartCommand bool `toml:"disable_startup_command"`
2527
DefaultSessionConfig
2628
}
29+
30+
WindowConfig struct {
31+
Name string `toml:"name"`
32+
StartupScript string `toml:"startup_script"`
33+
Path string `toml:"path"`
34+
}
2735
)

model/sesh_session.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,22 @@ type (
99
}
1010

1111
SeshSessionMap map[string]SeshSession
12+
SeshWindowMap map[string]WindowConfig
1213

1314
SeshSession struct {
1415
Src string // The source of the session (config, tmux, zoxide, tmuxinator)
1516
Name string // The display name
1617
Path string // The absolute directory path
1718

18-
StartupCommand string // The command to run when the session is started
19-
PreviewCommand string // The command to run when the session is previewed
20-
DisableStartupCommand bool // Ignore the default startup command if present
21-
Tmuxinator string // Name of the tmuxinator config
22-
Attached int // Whether the session is currently attached
23-
Windows int // The number of windows in the session
24-
Score float64 // The score of the session (from Zoxide)
19+
StartupCommand string // The command to run when the session is started
20+
PreviewCommand string // The command to run when the session is previewed
21+
DisableStartupCommand bool // Ignore the default startup command if present
22+
Tmuxinator string // Name of the tmuxinator config
23+
Attached int // Whether the session is currently attached
24+
Windows int // The number of windows in the session
25+
WindowConfigs []WindowConfig // The windows used in session config
26+
WindowNames []string // The names of the windows in session config
27+
Score float64 // The score of the session (from Zoxide)
2528
}
2629

2730
SeshSrcs struct {

seshcli/seshcli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func App(version string) cli.App {
6060
// core dependencies
6161
ls := ls.NewLs(config, shell)
6262
lister := lister.NewLister(config, home, tmux, zoxide, tmuxinator)
63-
startup := startup.NewStartup(config, lister, tmux)
63+
startup := startup.NewStartup(config, lister, tmux, home)
6464
namer := namer.NewNamer(path, git, home)
6565
connector := connector.NewConnector(config, dir, home, lister, namer, startup, tmux, zoxide, tmuxinator)
6666
icon := icon.NewIcon(config)

startup/startup.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package startup
33
import (
44
"fmt"
55

6+
"github.com/joshmedeski/sesh/v2/home"
67
"github.com/joshmedeski/sesh/v2/lister"
78
"github.com/joshmedeski/sesh/v2/model"
89
"github.com/joshmedeski/sesh/v2/tmux"
@@ -16,10 +17,11 @@ type RealStartup struct {
1617
lister lister.Lister
1718
tmux tmux.Tmux
1819
config model.Config
20+
home home.Home
1921
}
2022

21-
func NewStartup(config model.Config, lister lister.Lister, tmux tmux.Tmux) Startup {
22-
return &RealStartup{lister, tmux, config}
23+
func NewStartup(config model.Config, lister lister.Lister, tmux tmux.Tmux, home home.Home) Startup {
24+
return &RealStartup{lister, tmux, config, home}
2325
}
2426

2527
func (s *RealStartup) Exec(session model.SeshSession) (string, error) {
@@ -28,6 +30,48 @@ func (s *RealStartup) Exec(session model.SeshSession) (string, error) {
2830
defaultConfigStrategy,
2931
}
3032

33+
windows := make(model.SeshWindowMap)
34+
for _, window := range s.config.WindowConfigs {
35+
key := lister.ConfigKey(window.Name)
36+
var path string = ""
37+
var err error = nil
38+
if window.Path != "" {
39+
path, err = s.home.ExpandHome(window.Path)
40+
if err != nil {
41+
return "", fmt.Errorf("couldn't expand home: %q", err)
42+
}
43+
}
44+
45+
windows[key] = model.WindowConfig{
46+
Name: window.Name,
47+
Path: path,
48+
StartupScript: window.StartupScript,
49+
}
50+
}
51+
52+
for _, window := range session.WindowNames {
53+
windowConfig, ok := windows[lister.ConfigKey(window)]
54+
if !ok {
55+
return "", fmt.Errorf("window %s is not defined in config", window)
56+
}
57+
if windowConfig.Path == "" {
58+
path, err := s.home.ExpandHome(session.Path)
59+
if err != nil {
60+
return "", fmt.Errorf("couldn't expand home: %q", err)
61+
}
62+
windowConfig.Path = path
63+
}
64+
65+
// create the new window
66+
if ret, err := s.tmux.NewWindow(windowConfig.Path, windowConfig.Name); err != nil {
67+
return ret, err
68+
}
69+
if ret, err := s.tmux.SendKeys(session.Name, windowConfig.StartupScript); err != nil {
70+
return ret, err
71+
}
72+
}
73+
s.tmux.NextWindow()
74+
3175
for _, strategy := range strategies {
3276
if command, err := strategy(s, session); err != nil {
3377
return "", fmt.Errorf("failed to determine startup command: %w", err)
@@ -36,5 +80,6 @@ func (s *RealStartup) Exec(session model.SeshSession) (string, error) {
3680
return fmt.Sprintf("executing startup command: %s", command), nil
3781
}
3882
}
83+
3984
return "", nil // no command to run
4085
}

tmux/mock_Tmux.go

Lines changed: 112 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tmux/tmux.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import (
99
type Tmux interface {
1010
ListSessions() ([]*model.TmuxSession, error)
1111
NewSession(sessionName string, startDir string) (string, error)
12+
NewWindow(startDir string, name string) (string, error)
1213
IsAttached() bool
1314
AttachSession(targetSession string) (string, error)
1415
SendKeys(name string, command string) (string, error)
1516
SwitchClient(targetSession string) (string, error)
1617
CapturePane(targetSession string) (string, error)
18+
NextWindow() (string, error)
1719
SwitchOrAttach(name string, opts model.ConnectOpts) (string, error)
1820
}
1921

@@ -42,10 +44,18 @@ func (t *RealTmux) NewSession(sessionName string, startDir string) (string, erro
4244
return t.shell.Cmd("tmux", "new-session", "-d", "-s", sessionName, "-c", startDir)
4345
}
4446

47+
func (t *RealTmux) NewWindow(startDir string, name string) (string, error) {
48+
return t.shell.Cmd("tmux", "new-window", "-n", name, "-c", startDir)
49+
}
50+
4551
func (t *RealTmux) CapturePane(targetSession string) (string, error) {
4652
return t.shell.Cmd("tmux", "capture-pane", "-e", "-p", "-t", targetSession)
4753
}
4854

55+
func (t *RealTmux) NextWindow() (string, error) {
56+
return t.shell.Cmd("tmux", "next-window")
57+
}
58+
4959
func (t *RealTmux) IsAttached() bool {
5060
return len(t.os.Getenv("TMUX")) > 0
5161
}

0 commit comments

Comments
 (0)