From 0df47d2ce3467eaeb7e310e7c1b705038271f2ab Mon Sep 17 00:00:00 2001 From: Marcus Ramberg Date: Wed, 3 Dec 2025 00:35:51 +0100 Subject: [PATCH] core: add dynamic completion for more commands (#889) --- core/cmd/dms/commands_brightness.go | 62 ++++++++++++++++------------- core/cmd/dms/commands_common.go | 44 ++++++++++++++++++++ core/cmd/dms/commands_dank16.go | 3 ++ core/cmd/dms/commands_dpms.go | 25 +++++++++++- core/cmd/dms/commands_keybinds.go | 9 ++++- core/cmd/dms/commands_open.go | 3 ++ 6 files changed, 116 insertions(+), 30 deletions(-) diff --git a/core/cmd/dms/commands_brightness.go b/core/cmd/dms/commands_brightness.go index 88c287a3..e53b01d0 100644 --- a/core/cmd/dms/commands_brightness.go +++ b/core/cmd/dms/commands_brightness.go @@ -28,7 +28,14 @@ var brightnessSetCmd = &cobra.Command{ Short: "Set brightness for a device", Long: "Set brightness percentage (0-100) for a specific device", Args: cobra.ExactArgs(2), - Run: runBrightnessSet, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + includeDDC, _ := cmd.Flags().GetBool("ddc") + return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp + }, + Run: runBrightnessSet, } var brightnessGetCmd = &cobra.Command{ @@ -36,7 +43,14 @@ var brightnessGetCmd = &cobra.Command{ Short: "Get brightness for a device", Long: "Get current brightness percentage for a specific device", Args: cobra.ExactArgs(1), - Run: runBrightnessGet, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + includeDDC, _ := cmd.Flags().GetBool("ddc") + return getBrightnessDevices(includeDDC), cobra.ShellCompDirectiveNoFileComp + }, + Run: runBrightnessGet, } func init() { @@ -105,9 +119,7 @@ Global Flags: brightnessCmd.AddCommand(brightnessListCmd, brightnessSetCmd, brightnessGetCmd) } -func runBrightnessList(cmd *cobra.Command, args []string) { - includeDDC, _ := cmd.Flags().GetBool("ddc") - +func getAllBrightnessDevices(includeDDC bool) []brightness.Device { allDevices := []brightness.Device{} sysfs, err := brightness.NewSysfsBackend() @@ -138,6 +150,13 @@ func runBrightnessList(cmd *cobra.Command, args []string) { } } + return allDevices +} + +func runBrightnessList(cmd *cobra.Command, args []string) { + includeDDC, _ := cmd.Flags().GetBool("ddc") + allDevices := getAllBrightnessDevices(includeDDC) + if len(allDevices) == 0 { fmt.Println("No brightness devices found") return @@ -261,31 +280,20 @@ func runBrightnessSet(cmd *cobra.Command, args []string) { log.Fatalf("Failed to set brightness for device: %s", deviceID) } +func getBrightnessDevices(includeDDC bool) []string { + allDevices := getAllBrightnessDevices(includeDDC) + + var deviceIDs []string + for _, device := range allDevices { + deviceIDs = append(deviceIDs, device.ID) + } + return deviceIDs +} + func runBrightnessGet(cmd *cobra.Command, args []string) { deviceID := args[0] includeDDC, _ := cmd.Flags().GetBool("ddc") - - allDevices := []brightness.Device{} - - sysfs, err := brightness.NewSysfsBackend() - if err == nil { - devices, err := sysfs.GetDevices() - if err == nil { - allDevices = append(allDevices, devices...) - } - } - - if includeDDC { - ddc, err := brightness.NewDDCBackend() - if err == nil { - defer ddc.Close() - time.Sleep(100 * time.Millisecond) - devices, err := ddc.GetDevices() - if err == nil { - allDevices = append(allDevices, devices...) - } - } - } + allDevices := getAllBrightnessDevices(includeDDC) for _, device := range allDevices { if device.ID == deviceID { diff --git a/core/cmd/dms/commands_common.go b/core/cmd/dms/commands_common.go index 69418cf7..14619b53 100644 --- a/core/cmd/dms/commands_common.go +++ b/core/cmd/dms/commands_common.go @@ -119,6 +119,12 @@ var pluginsInstallCmd = &cobra.Command{ Short: "Install a plugin by ID", Long: "Install a DMS plugin from the registry using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", Args: cobra.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return getAvailablePluginIDs(), cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { if err := installPluginCLI(args[0]); err != nil { log.Fatalf("Error installing plugin: %v", err) @@ -131,6 +137,12 @@ var pluginsUninstallCmd = &cobra.Command{ Short: "Uninstall a plugin by ID", Long: "Uninstall a DMS plugin using its ID (e.g., 'myPlugin'). Plugin names with spaces are also supported for backward compatibility.", Args: cobra.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return getInstalledPluginIDs(), cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { if err := uninstallPluginCLI(args[0]); err != nil { log.Fatalf("Error uninstalling plugin: %v", err) @@ -303,6 +315,38 @@ func installPluginCLI(idOrName string) error { return nil } +func getAvailablePluginIDs() []string { + registry, err := plugins.NewRegistry() + if err != nil { + return nil + } + + pluginList, err := registry.List() + if err != nil { + return nil + } + + var ids []string + for _, p := range pluginList { + ids = append(ids, p.ID) + } + return ids +} + +func getInstalledPluginIDs() []string { + manager, err := plugins.NewManager() + if err != nil { + return nil + } + + installed, err := manager.ListInstalled() + if err != nil { + return nil + } + + return installed +} + func uninstallPluginCLI(idOrName string) error { manager, err := plugins.NewManager() if err != nil { diff --git a/core/cmd/dms/commands_dank16.go b/core/cmd/dms/commands_dank16.go index 36f0e596..83479e5c 100644 --- a/core/cmd/dms/commands_dank16.go +++ b/core/cmd/dms/commands_dank16.go @@ -27,6 +27,9 @@ func init() { dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format") dank16Cmd.Flags().String("background", "", "Custom background color") dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag") + _ = dank16Cmd.RegisterFlagCompletionFunc("contrast", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"dps", "wcag"}, cobra.ShellCompDirectiveNoFileComp + }) } func runDank16(cmd *cobra.Command, args []string) { diff --git a/core/cmd/dms/commands_dpms.go b/core/cmd/dms/commands_dpms.go index 41d602ef..77eff1ab 100644 --- a/core/cmd/dms/commands_dpms.go +++ b/core/cmd/dms/commands_dpms.go @@ -16,14 +16,26 @@ var dpmsOnCmd = &cobra.Command{ Use: "on [output]", Short: "Turn display(s) on", Args: cobra.MaximumNArgs(1), - Run: runDPMSOn, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp + }, + Run: runDPMSOn, } var dpmsOffCmd = &cobra.Command{ Use: "off [output]", Short: "Turn display(s) off", Args: cobra.MaximumNArgs(1), - Run: runDPMSOff, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return getDPMSOutputs(), cobra.ShellCompDirectiveNoFileComp + }, + Run: runDPMSOff, } var dpmsListCmd = &cobra.Command{ @@ -71,6 +83,15 @@ func runDPMSOff(cmd *cobra.Command, args []string) { } } +func getDPMSOutputs() []string { + client, err := newDPMSClient() + if err != nil { + return nil + } + defer client.Close() + return client.ListOutputs() +} + func runDPMSList(cmd *cobra.Command, args []string) { client, err := newDPMSClient() if err != nil { diff --git a/core/cmd/dms/commands_keybinds.go b/core/cmd/dms/commands_keybinds.go index 6191b419..38fd5755 100644 --- a/core/cmd/dms/commands_keybinds.go +++ b/core/cmd/dms/commands_keybinds.go @@ -30,7 +30,14 @@ var keybindsShowCmd = &cobra.Command{ Short: "Show keybinds for a provider", Long: "Display keybinds/cheatsheet for the specified provider", Args: cobra.ExactArgs(1), - Run: runKeybindsShow, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + registry := keybinds.GetDefaultRegistry() + return registry.List(), cobra.ShellCompDirectiveNoFileComp + }, + Run: runKeybindsShow, } func init() { diff --git a/core/cmd/dms/commands_open.go b/core/cmd/dms/commands_open.go index a4c567b2..e6a730e1 100644 --- a/core/cmd/dms/commands_open.go +++ b/core/cmd/dms/commands_open.go @@ -45,6 +45,9 @@ func init() { openCmd.Flags().StringVar(&openMimeType, "mime", "", "MIME type for filtering applications") openCmd.Flags().StringSliceVar(&openCategories, "category", []string{}, "Application categories to filter (e.g., WebBrowser, Office, Graphics)") openCmd.Flags().StringVar(&openRequestType, "type", "url", "Request type (url, file, or custom)") + _ = openCmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{"url", "file", "custom"}, cobra.ShellCompDirectiveNoFileComp + }) } // mimeTypeToCategories maps MIME types to desktop file categories