Updated interaction between custom CLI syntax and Cobra flags
This commit is contained in:
+62
-27
@@ -12,9 +12,10 @@ import (
|
|||||||
|
|
||||||
// ParsedArgs represents preprocessed command arguments
|
// ParsedArgs represents preprocessed command arguments
|
||||||
type ParsedArgs struct {
|
type ParsedArgs struct {
|
||||||
Command string
|
Command string
|
||||||
Filters []string
|
Filters []string
|
||||||
Modifiers []string
|
Modifiers []string
|
||||||
|
CmdArgIndex int // position of command in os.Args[1:], -1 if not found
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context key for parsed args
|
// Context key for parsed args
|
||||||
@@ -84,28 +85,29 @@ func Execute() error {
|
|||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
firstArg := os.Args[1]
|
firstArg := os.Args[1]
|
||||||
if firstArg == "-h" || firstArg == "--help" || firstArg == "help" {
|
if firstArg == "-h" || firstArg == "--help" || firstArg == "help" {
|
||||||
// Let Cobra handle help - skip preprocessing
|
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preprocess arguments BEFORE Cobra routing
|
// Preprocess arguments (read-only scan — os.Args is never mutated)
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
parsed := preprocessArgs(os.Args[1:])
|
parsed := preprocessArgs(os.Args[1:])
|
||||||
|
|
||||||
// Store in context for commands to use
|
|
||||||
ctx := context.WithValue(context.Background(), parsedArgsKey, parsed)
|
ctx := context.WithValue(context.Background(), parsedArgsKey, parsed)
|
||||||
rootCmd.SetContext(ctx)
|
rootCmd.SetContext(ctx)
|
||||||
|
|
||||||
// Rewrite os.Args for Cobra based on parsed command
|
// Build clean args for Cobra via SetArgs (os.Args stays untouched).
|
||||||
// This allows Cobra to route to the correct command
|
if parsed.CmdArgIndex >= 0 {
|
||||||
if parsed.Command != "list" || len(parsed.Filters) > 0 || len(parsed.Modifiers) > 0 {
|
i := parsed.CmdArgIndex + 1 // offset for binary name in os.Args
|
||||||
// Reconstruct args: [command, ...filters, ...modifiers]
|
cmdAndAfter := os.Args[i:] // command + subcommands + their flags
|
||||||
newArgs := []string{os.Args[0], parsed.Command}
|
preCmdFlags := collectFlags(os.Args[1:i]) // persistent flags before command
|
||||||
newArgs = append(newArgs, parsed.Filters...)
|
|
||||||
newArgs = append(newArgs, parsed.Modifiers...)
|
cobraArgs := make([]string, 0, len(cmdAndAfter)+len(preCmdFlags))
|
||||||
os.Args = newArgs
|
cobraArgs = append(cobraArgs, cmdAndAfter...)
|
||||||
|
cobraArgs = append(cobraArgs, preCmdFlags...)
|
||||||
|
rootCmd.SetArgs(cobraArgs)
|
||||||
}
|
}
|
||||||
|
// CmdArgIndex == -1: no command found, don't call SetArgs.
|
||||||
|
// Cobra processes os.Args naturally → root command → default report.
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
@@ -127,9 +129,10 @@ func getParsedArgs(cmd *cobra.Command) *ParsedArgs {
|
|||||||
func preprocessArgs(args []string) *ParsedArgs {
|
func preprocessArgs(args []string) *ParsedArgs {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return &ParsedArgs{
|
return &ParsedArgs{
|
||||||
Command: "list", // Default command
|
Command: "list", // Default command
|
||||||
Filters: []string{},
|
Filters: []string{},
|
||||||
Modifiers: []string{},
|
Modifiers: []string{},
|
||||||
|
CmdArgIndex: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,9 +170,10 @@ func preprocessArgs(args []string) *ParsedArgs {
|
|||||||
// If no command found, treat as filters for default list command
|
// If no command found, treat as filters for default list command
|
||||||
if cmdIdx == -1 {
|
if cmdIdx == -1 {
|
||||||
return &ParsedArgs{
|
return &ParsedArgs{
|
||||||
Command: "list",
|
Command: "list",
|
||||||
Filters: stripFlags(args),
|
Filters: stripFlags(args),
|
||||||
Modifiers: []string{},
|
Modifiers: []string{},
|
||||||
|
CmdArgIndex: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,16 +187,18 @@ func preprocessArgs(args []string) *ParsedArgs {
|
|||||||
// Determine how to interpret right args
|
// Determine how to interpret right args
|
||||||
if commandsWithModifiers[cmdName] {
|
if commandsWithModifiers[cmdName] {
|
||||||
return &ParsedArgs{
|
return &ParsedArgs{
|
||||||
Command: cmdName,
|
Command: cmdName,
|
||||||
Filters: leftArgs,
|
Filters: leftArgs,
|
||||||
Modifiers: rightArgs,
|
Modifiers: rightArgs,
|
||||||
|
CmdArgIndex: cmdIdx,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
allFilters := append(leftArgs, rightArgs...)
|
allFilters := append(leftArgs, rightArgs...)
|
||||||
return &ParsedArgs{
|
return &ParsedArgs{
|
||||||
Command: cmdName,
|
Command: cmdName,
|
||||||
Filters: allFilters,
|
Filters: allFilters,
|
||||||
Modifiers: []string{},
|
Modifiers: []string{},
|
||||||
|
CmdArgIndex: cmdIdx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,6 +214,35 @@ func stripFlags(args []string) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collectFlags extracts flag arguments (with their values) from a slice.
|
||||||
|
// Uses Cobra's persistent flag registry to determine if a flag takes a value.
|
||||||
|
func collectFlags(args []string) []string {
|
||||||
|
var flags []string
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
if !strings.HasPrefix(args[i], "-") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
flags = append(flags, args[i])
|
||||||
|
// If flag uses = syntax, value is already included
|
||||||
|
if strings.Contains(args[i], "=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Check if this flag takes a value argument
|
||||||
|
if i+1 < len(args) {
|
||||||
|
name := strings.TrimLeft(args[i], "-")
|
||||||
|
f := rootCmd.PersistentFlags().Lookup(name)
|
||||||
|
if f == nil && len(name) == 1 {
|
||||||
|
f = rootCmd.PersistentFlags().ShorthandLookup(name)
|
||||||
|
}
|
||||||
|
if f != nil && f.Value.Type() != "bool" {
|
||||||
|
i++
|
||||||
|
flags = append(flags, args[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Add persistent flags for directory overrides
|
// Add persistent flags for directory overrides
|
||||||
rootCmd.PersistentFlags().StringVar(&configDirFlag, "config-dir", "",
|
rootCmd.PersistentFlags().StringVar(&configDirFlag, "config-dir", "",
|
||||||
|
|||||||
@@ -2,3 +2,39 @@
|
|||||||
`Buy milk due:8d wait:5d` still showing up
|
`Buy milk due:8d wait:5d` still showing up
|
||||||
|
|
||||||
# Missing uncomplete feat
|
# Missing uncomplete feat
|
||||||
|
Undo / uncomplete fails in web-ui. task still has checked box and strikethrough.
|
||||||
|
|
||||||
|
# Cycling priority - Web
|
||||||
|
Trying to edit a task priority results in following console error:
|
||||||
|
GQLaRcBw.js:1 PUT https://opal.jnss.me/api/tasks/8814798c-97af-4134-9786-47e027d164c8 400 (Bad Request)
|
||||||
|
window.fetch @ GQLaRcBw.js:1
|
||||||
|
n @ nlPNz7OE.js:1
|
||||||
|
update @ nlPNz7OE.js:1
|
||||||
|
updateTask @ 2.qiXA3Mmu.js:1
|
||||||
|
X @ 2.qiXA3Mmu.js:3
|
||||||
|
Q @ 2.qiXA3Mmu.js:1
|
||||||
|
(anonymous) @ idxpmzXF.js:1
|
||||||
|
Qe @ jWcw5lls.js:1
|
||||||
|
n @ idxpmzXF.js:1
|
||||||
|
nlPNz7OE.js:1 API Error [/tasks/8814798c-97af-4134-9786-47e027d164c8]: Error: HTTP 400:
|
||||||
|
at n (nlPNz7OE.js:1:1556)
|
||||||
|
at async Object.updateTask (2.qiXA3Mmu.js:1:4502)
|
||||||
|
at async X (2.qiXA3Mmu.js:3:3519)
|
||||||
|
at async HTMLDivElement.Q (2.qiXA3Mmu.js:1:58579)
|
||||||
|
n @ nlPNz7OE.js:1
|
||||||
|
await in n
|
||||||
|
update @ nlPNz7OE.js:1
|
||||||
|
updateTask @ 2.qiXA3Mmu.js:1
|
||||||
|
X @ 2.qiXA3Mmu.js:3
|
||||||
|
Q @ 2.qiXA3Mmu.js:1
|
||||||
|
(anonymous) @ idxpmzXF.js:1
|
||||||
|
Qe @ jWcw5lls.js:1
|
||||||
|
n @ idxpmzXF.js:1
|
||||||
|
2.qiXA3Mmu.js:3 Failed to update task: Error: HTTP 400:
|
||||||
|
at n (nlPNz7OE.js:1:1556)
|
||||||
|
at async Object.updateTask (2.qiXA3Mmu.js:1:4502)
|
||||||
|
at async X (2.qiXA3Mmu.js:3:3519)
|
||||||
|
at async HTMLDivElement.Q (2.qiXA3Mmu.js:1:58579)
|
||||||
|
|
||||||
|
# Ambiguity complete and details tap.
|
||||||
|
Pressing the task description completes the task. only the checkbox click should complete task, otherwise open details view. to complete swipe left
|
||||||
|
|||||||
Reference in New Issue
Block a user