Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A couple commits grouped into a single PR #13

Merged
merged 2 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions tmux/properties.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package tmux

import (
"fmt"
)

// properties is a helper function to make implementing property lookups for
// different tmux entities a little easier.
// keys are all the properties being fetched.
Expand All @@ -17,7 +13,7 @@ func properties[T ~string](keys []T, fn func([]string) ([]string, error)) (map[T

props, err := fn(keyStrings)
if err != nil {
return nil, fmt.Errorf("failed to fetch properties %s: %w", keyStrings, err)
return nil, err
}
res := make(map[T]string, len(keys))
for i, prop := range props {
Expand Down
7 changes: 7 additions & 0 deletions tmux/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,10 @@ func (srv *Server) resolveTargetSession(s TargetSession) (string, error) {
func (srv *Server) Kill() error {
return srv.command("kill-server").Run()
}

func (srv *Server) SetOption(opt Option, val string) error {
if err := srv.command("set-option", "-s", string(opt), val).Run(); err != nil {
return fmt.Errorf("set-option -s %q %q: %w", opt, val, err)
}
return nil
}
17 changes: 16 additions & 1 deletion tmux/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (s *Session) Property(prop SessionProperty) (string, error) {
// Properties fetches properties about a session.
func (s *Session) Properties(props ...SessionProperty) (map[SessionProperty]string, error) {
res, err := properties(props, func(keys []string) ([]string, error) {
stdout, err := s.Server.command("display-message", "-t", s.ID, "-p", strings.Join(keys, "\n")).RunStdout()
stdout, err := s.DisplayMessage(strings.Join(keys, "\n"))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -104,3 +104,18 @@ func (s *Session) Kill() error {
}
return nil
}

func (s *Session) SetOption(opt Option, val string) error {
if err := s.Server.command("set-option", "-t", s.ID, string(opt), val).Run(); err != nil {
return fmt.Errorf("set-option -t %s %q %q: %w", s.ID, opt, val, err)
}
return nil
}

func (s *Session) DisplayMessage(msg string) (string, error) {
stdout, err := s.Server.command("display-message", "-t", s.ID, "-p", msg).RunStdout()
if err != nil {
return "", fmt.Errorf("display-message -t %s %q: %w", s.ID, msg, err)
}
return stdout, nil
}
43 changes: 36 additions & 7 deletions tmux/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log/slog"
"maps"
"slices"
"strconv"
"strings"

"github.com/JeffFaer/tmux-vcs-sync/api"
Expand All @@ -17,7 +18,7 @@ type State struct {
// tmux sessions in srv with their associated repositories.
sessions map[SessionName]*tmux.Session
// An index of unqualified repo names that exist in sessions.
unqualifiedRepos map[string]bool
unqualifiedRepos map[string]int
// Representative examples of each api.Repository in sessions.
repos map[RepoName]api.Repository
}
Expand All @@ -31,7 +32,7 @@ func New(srv *tmux.Server) (*State, error) {
st := &State{
srv: srv,
sessions: make(map[SessionName]*tmux.Session),
unqualifiedRepos: make(map[string]bool),
unqualifiedRepos: make(map[string]int),
repos: make(map[RepoName]api.Repository),
}
// An index from directory to api.Repository.
Expand Down Expand Up @@ -66,15 +67,15 @@ func New(srv *tmux.Server) (*State, error) {

parsed := ParseSessionName(repo, name)
st.sessions[parsed] = sesh
st.unqualifiedRepos[parsed.Repo] = true
st.unqualifiedRepos[parsed.Repo]++
st.repos[parsed.RepoName] = repo
logger.Info("Found repository in tmux session.", "name", parsed)
}
return st, nil
}

func (st *State) sessionNameString(n SessionName) string {
if len(st.unqualifiedRepos) > 1 || (len(st.unqualifiedRepos) == 1 && !st.unqualifiedRepos[n.Repo]) {
if len(st.unqualifiedRepos) > 1 || (len(st.unqualifiedRepos) == 1 && st.unqualifiedRepos[n.Repo] == 0) {
return n.RepoString()
}
return n.WorkUnitString()
Expand Down Expand Up @@ -119,7 +120,7 @@ func (st *State) NewSession(repo api.Repository, workUnitName string) (*tmux.Ses
}

st.sessions[name] = sesh
st.unqualifiedRepos[name.Repo] = true
st.unqualifiedRepos[name.Repo]++
st.repos[name.RepoName] = repo
if err := st.updateSessionNames(); err != nil {
slog.Warn("Failed to update tmux session names.", "error", err)
Expand Down Expand Up @@ -200,10 +201,17 @@ func (st *State) PruneSessions() error {
}

for _, sesh := range toRemove {
slog.Info("Killing session.", "session_id", sesh.ID, "name", invalidSessions[sesh])
n := invalidSessions[sesh]
slog.Info("Killing session.", "session_id", sesh.ID, "name", n)
if err := sesh.Kill(); err != nil {
return err
}
delete(st.sessions, n)
st.unqualifiedRepos[n.Repo]--
if st.unqualifiedRepos[n.Repo] == 0 {
delete(st.unqualifiedRepos, n.Repo)
delete(st.repos, n.RepoName)
}
}

if err := st.updateSessionNames(); err != nil {
Expand All @@ -215,17 +223,38 @@ func (st *State) PruneSessions() error {
func (st *State) updateSessionNames() error {
var errs []error
for k, sesh := range st.sessions {
name, err := sesh.Property(tmux.SessionName)
props, err := sesh.Properties(tmux.SessionName, tmux.StatusLeft.SessionProperty(), tmux.StatusLeftLength.SessionProperty())
if err != nil {
errs = append(errs, err)
continue
}
name := props[tmux.SessionName]
statusLeft := props[tmux.StatusLeft.SessionProperty()]
statusLeftLength := props[tmux.StatusLeftLength.SessionProperty()]
if want := st.sessionNameString(k); name != want {
if err := sesh.Rename(want); err != nil {
errs = append(errs, err)
continue
}
}

msg, err := sesh.DisplayMessage(statusLeft)
if err != nil {
errs = append(errs, err)
continue
}
wantLength := len(msg)
got, err := strconv.Atoi(statusLeftLength)
if err != nil {
errs = append(errs, fmt.Errorf("tmux session %s has invalid %s value %q: %w", sesh.ID, tmux.StatusLeftLength, statusLeftLength, err))
continue
}
if wantLength > got {
if err := sesh.SetOption(tmux.StatusLeftLength, strconv.Itoa(wantLength)); err != nil {
errs = append(errs, err)
continue
}
}
}
return errors.Join(errs...)
}
Expand Down
11 changes: 11 additions & 0 deletions tmux/tmux.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,14 @@ func init() {
panic(err)
}
}

type Option string

const (
StatusLeft Option = "status-left"
StatusLeftLength Option = "status-left-length"
)

func (opt Option) SessionProperty() SessionProperty {
return SessionProperty(fmt.Sprintf("#{%s}", opt))
}
Loading