feat: accept multiple session names for rm/kill

This commit is contained in:
2026-03-23 23:13:55 +02:00
parent d8d915ec19
commit 296e13549e
4 changed files with 80 additions and 38 deletions

View File

@@ -7,10 +7,9 @@ import (
)
var killCmd = &cobra.Command{
Use: "kill [session]",
Use: "kill [session...]",
Aliases: []string{"k"},
Short: "Kill a running tmux session (current session if no arg)",
Args: cobra.MaximumNArgs(1),
Short: "Kill running tmux sessions (current session if no arg)",
RunE: runKill,
ValidArgsFunction: completeRunningSessions,
}
@@ -19,24 +18,26 @@ func runKill(cmd *cobra.Command, args []string) error {
opts := GetOpts()
if len(args) > 0 {
sessionName := args[0]
// Check if session exists
if !tmux.SessionExists(opts, sessionName) {
return NewUserError("tmux session '" + sessionName + "' does not exist")
var errs []error
for _, sessionName := range args {
if !tmux.SessionExists(opts, sessionName) {
errs = append(errs, NewUserError("tmux session '"+sessionName+"' does not exist"))
continue
}
if err := tmux.KillSession(opts, sessionName); err != nil {
errs = append(errs, err)
}
}
return tmux.KillSession(opts, sessionName)
return joinErrors(errs)
}
// No arg - kill current session
return exec.RunCommand(opts, "tmux kill-session")
}
// completeRunningSessions returns running session names for shell completion
// completeRunningSessions returns running session names for shell completion,
// excluding sessions already provided as arguments.
func completeRunningSessions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Don't complete if we already have an argument
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return tmux.GetSessionNames(), cobra.ShellCompDirectiveNoFileComp
all := tmux.GetSessionNames()
return filterUsed(all, args), cobra.ShellCompDirectiveNoFileComp
}

View File

@@ -11,12 +11,12 @@ import (
var removeConfigFile string
var removeCmd = &cobra.Command{
Use: "remove <key>",
Use: "remove <key...>",
Aliases: []string{"rm"},
Short: "Remove a tmux workspace from the config file",
Args: cobra.ExactArgs(1),
Short: "Remove tmux workspaces from the config file",
Args: cobra.MinimumNArgs(1),
RunE: runRemove,
ValidArgsFunction: completeSessionNames,
ValidArgsFunction: completeSessionNamesMulti,
}
func init() {
@@ -25,30 +25,32 @@ func init() {
func runRemove(cmd *cobra.Command, args []string) error {
opts := GetOpts()
key := args[0]
// Verify the key exists
allConfig, err := config.GetTmuxConfig()
if err != nil {
return err
}
_, actualKey, exists := allConfig.Get(key)
if !exists {
return NewUserError("tmux config item '" + key + "' not found")
var errs []error
for _, key := range args {
_, actualKey, exists := allConfig.Get(key)
if !exists {
errs = append(errs, NewUserError("tmux config item '"+key+"' not found"))
continue
}
err = config.RemoveConfigFromFile(actualKey, removeConfigFile, opts.Dry)
if err != nil {
errs = append(errs, err)
continue
}
if !opts.Dry {
fmt.Printf("Removed tmux config item '%s'\n", key)
}
exec.Log(opts, "Removed config item:", key)
}
err = config.RemoveConfigFromFile(actualKey, removeConfigFile, opts.Dry)
if err != nil {
return err
}
if !opts.Dry {
fmt.Printf("Removed tmux config item '%s'\n", key)
}
// Log action in verbose/dry mode
exec.Log(opts, "Removed config item:", key)
return nil
return joinErrors(errs)
}

View File

@@ -9,7 +9,7 @@ func TestRemoveCmd_Exists(t *testing.T) {
t.Error("expected removeCmd to not be nil")
}
if removeCmd.Use != "remove <key>" {
if removeCmd.Use != "remove <key...>" {
t.Errorf("unexpected Use: %q", removeCmd.Use)
}
}

View File

@@ -1,6 +1,7 @@
package cli
import (
"errors"
"fmt"
"os"
@@ -41,6 +42,11 @@ func NewUserError(message string) *UserError {
return &UserError{Message: message}
}
// joinErrors combines multiple errors into one, returning nil if there are none.
func joinErrors(errs []error) error {
return errors.Join(errs...)
}
// rootCmd represents the base command
var rootCmd = &cobra.Command{
Use: "tx [session]",
@@ -79,6 +85,39 @@ func completeSessionNames(cmd *cobra.Command, args []string, toComplete string)
return names, cobra.ShellCompDirectiveNoFileComp
}
// completeSessionNamesMulti returns session names excluding already-provided args.
func completeSessionNamesMulti(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
cfg, err := config.GetTmuxConfig()
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}
var names []string
for name, item := range cfg {
if name != config.ConfigKey {
names = append(names, name)
names = append(names, item.Aliases...)
}
}
return filterUsed(names, args), cobra.ShellCompDirectiveNoFileComp
}
// filterUsed returns items from candidates that are not already in used.
func filterUsed(candidates []string, used []string) []string {
seen := make(map[string]bool, len(used))
for _, u := range used {
seen[u] = true
}
var result []string
for _, c := range candidates {
if !seen[c] {
result = append(result, c)
}
}
return result
}
// buildFzfItems creates fzf items from a config file
func buildFzfItems(cfg config.ConfigFile) []fzf.Item {
items := make([]fzf.Item, 0, len(cfg))