package cmd import ( "fmt" "strings" "git.jnss.me/joakim/opal/internal/engine" "github.com/spf13/cobra" ) // taskFilterCompletion provides dynamic completions for task filter arguments. // Suggests +tag, project:name, and attribute value completions from the database. func taskFilterCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { // If typing a key:value, complete the value part if idx := strings.IndexByte(toComplete, ':'); idx >= 0 { key := toComplete[:idx] if engine.ValidAttributeKeys[key] { return attributeValueCompletions(key, toComplete), cobra.ShellCompDirectiveNoFileComp } } // If toComplete is a prefix of an attribute key, return only key: // completions with NoSpace so the cursor stays after the colon. if toComplete != "" && !strings.HasPrefix(toComplete, "+") && !strings.HasPrefix(toComplete, "-") { var keyCompletions []string for key := range engine.ValidAttributeKeys { if strings.HasPrefix(key, toComplete) { keyCompletions = append(keyCompletions, fmt.Sprintf("%s:", key)) } } if len(keyCompletions) > 0 { return keyCompletions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace } } var completions []string tags, err := engine.GetAllTags() if err == nil { for _, tag := range tags { completions = append(completions, fmt.Sprintf("+%s", tag)) } } projects, err := engine.GetAllProjects() if err == nil { for _, proj := range projects { completions = append(completions, fmt.Sprintf("project:%s", proj)) } } // Add known attribute keys for key := range engine.ValidAttributeKeys { completions = append(completions, fmt.Sprintf("%s:", key)) } return completions, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace } // attributeValueCompletions returns key:value completions for a known attribute key. // Cobra filters by prefix automatically, so we return all values prefixed with "key:". func attributeValueCompletions(key, toComplete string) []string { var values []string switch key { case "status": values = []string{"pending", "completed", "deleted", "recurring"} case "priority": values = []string{"H", "M", "L"} case "project": projects, err := engine.GetAllProjects() if err == nil { values = projects } case "due", "wait", "scheduled", "until": values = []string{ "today", "tomorrow", "yesterday", "now", "eod", "sow", "eow", "som", "eom", "mon", "tue", "wed", "thu", "fri", "sat", "sun", } case "recur": values = []string{"daily", "weekly", "monthly", "yearly", "1d", "1w", "2w", "1m", "1y"} } completions := make([]string, 0, len(values)) for _, v := range values { completions = append(completions, fmt.Sprintf("%s:%s", key, v)) } return completions } // rootValidArgsFunction provides completions for root-level arguments, // enabling flexible syntax like "opal 1 de" to complete to "delete". func rootValidArgsFunction(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { // Delegate to taskFilterCompletion first — if toComplete is a partial // attribute key, it returns early with NoSpace and we should honour that. filterCompletions, directive := taskFilterCompletion(cmd, args, toComplete) var completions []string // Suggest command names for _, name := range commandNames { completions = append(completions, name) } // Suggest report names for _, name := range reportNames { completions = append(completions, name) } completions = append(completions, filterCompletions...) return completions, directive } func init() { // Root command completions for flexible syntax (e.g., "opal 1 de") rootCmd.ValidArgsFunction = rootValidArgsFunction // Register dynamic completions for commands that accept filters addCmd.ValidArgsFunction = taskFilterCompletion doneCmd.ValidArgsFunction = taskFilterCompletion deleteCmd.ValidArgsFunction = taskFilterCompletion modifyCmd.ValidArgsFunction = taskFilterCompletion startCmd.ValidArgsFunction = taskFilterCompletion stopCmd.ValidArgsFunction = taskFilterCompletion editCmd.ValidArgsFunction = taskFilterCompletion infoCmd.ValidArgsFunction = taskFilterCompletion annotateCmd.ValidArgsFunction = taskFilterCompletion denotateCmd.ValidArgsFunction = taskFilterCompletion logCmd.ValidArgsFunction = taskFilterCompletion }