mirror of
https://github.com/chenasraf/tx.git
synced 2026-05-17 17:28:08 +00:00
feat: accept multiple session names for rm/kill
This commit is contained in:
@@ -7,10 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var killCmd = &cobra.Command{
|
var killCmd = &cobra.Command{
|
||||||
Use: "kill [session]",
|
Use: "kill [session...]",
|
||||||
Aliases: []string{"k"},
|
Aliases: []string{"k"},
|
||||||
Short: "Kill a running tmux session (current session if no arg)",
|
Short: "Kill running tmux sessions (current session if no arg)",
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
RunE: runKill,
|
RunE: runKill,
|
||||||
ValidArgsFunction: completeRunningSessions,
|
ValidArgsFunction: completeRunningSessions,
|
||||||
}
|
}
|
||||||
@@ -19,24 +18,26 @@ func runKill(cmd *cobra.Command, args []string) error {
|
|||||||
opts := GetOpts()
|
opts := GetOpts()
|
||||||
|
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
sessionName := args[0]
|
var errs []error
|
||||||
// Check if session exists
|
for _, sessionName := range args {
|
||||||
if !tmux.SessionExists(opts, sessionName) {
|
if !tmux.SessionExists(opts, sessionName) {
|
||||||
return NewUserError("tmux session '" + sessionName + "' does not exist")
|
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
|
// No arg - kill current session
|
||||||
return exec.RunCommand(opts, "tmux kill-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) {
|
func completeRunningSessions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
// Don't complete if we already have an argument
|
all := tmux.GetSessionNames()
|
||||||
if len(args) > 0 {
|
return filterUsed(all, args), cobra.ShellCompDirectiveNoFileComp
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmux.GetSessionNames(), cobra.ShellCompDirectiveNoFileComp
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import (
|
|||||||
var removeConfigFile string
|
var removeConfigFile string
|
||||||
|
|
||||||
var removeCmd = &cobra.Command{
|
var removeCmd = &cobra.Command{
|
||||||
Use: "remove <key>",
|
Use: "remove <key...>",
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
Short: "Remove a tmux workspace from the config file",
|
Short: "Remove tmux workspaces from the config file",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
RunE: runRemove,
|
RunE: runRemove,
|
||||||
ValidArgsFunction: completeSessionNames,
|
ValidArgsFunction: completeSessionNamesMulti,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -25,30 +25,32 @@ func init() {
|
|||||||
|
|
||||||
func runRemove(cmd *cobra.Command, args []string) error {
|
func runRemove(cmd *cobra.Command, args []string) error {
|
||||||
opts := GetOpts()
|
opts := GetOpts()
|
||||||
key := args[0]
|
|
||||||
|
|
||||||
// Verify the key exists
|
|
||||||
allConfig, err := config.GetTmuxConfig()
|
allConfig, err := config.GetTmuxConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, actualKey, exists := allConfig.Get(key)
|
var errs []error
|
||||||
if !exists {
|
for _, key := range args {
|
||||||
return NewUserError("tmux config item '" + key + "' not found")
|
_, 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)
|
return joinErrors(errs)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func TestRemoveCmd_Exists(t *testing.T) {
|
|||||||
t.Error("expected removeCmd to not be nil")
|
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)
|
t.Errorf("unexpected Use: %q", removeCmd.Use)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -41,6 +42,11 @@ func NewUserError(message string) *UserError {
|
|||||||
return &UserError{Message: message}
|
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
|
// rootCmd represents the base command
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "tx [session]",
|
Use: "tx [session]",
|
||||||
@@ -79,6 +85,39 @@ func completeSessionNames(cmd *cobra.Command, args []string, toComplete string)
|
|||||||
return names, cobra.ShellCompDirectiveNoFileComp
|
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
|
// buildFzfItems creates fzf items from a config file
|
||||||
func buildFzfItems(cfg config.ConfigFile) []fzf.Item {
|
func buildFzfItems(cfg config.ConfigFile) []fzf.Item {
|
||||||
items := make([]fzf.Item, 0, len(cfg))
|
items := make([]fzf.Item, 0, len(cfg))
|
||||||
|
|||||||
Reference in New Issue
Block a user