From 56684e4d66f92224e570ba1a16ec0f57a37b5cdd Mon Sep 17 00:00:00 2001 From: Jon Hadfield Date: Sun, 7 Jan 2024 18:23:24 +0000 Subject: [PATCH] fix completion. --- README.md | 8 +++-- autocomplete/bash_autocomplete | 30 ++++++++++++++---- autocomplete/powershell_autocomplete.ps1 | 9 ++++++ autocomplete/zsh_autocomplete | 20 ++++++++++++ cmd/sncli/add.go | 7 +++-- cmd/sncli/get.go | 24 ++++++++++++-- cmd/sncli/main.go | 8 ++++- cmd/sncli/note.go | 17 +++++++--- cmd/sncli/register.go | 5 ++- cmd/sncli/resync.go | 3 ++ cmd/sncli/session.go | 13 ++++++-- cmd/sncli/stats.go | 5 +++ cmd/sncli/tag.go | 17 +++++++--- cmd/sncli/task.go | 40 +++++++++++++++++++++--- cmd/sncli/wipe.go | 11 ++++++- 15 files changed, 186 insertions(+), 31 deletions(-) create mode 100644 autocomplete/powershell_autocomplete.ps1 create mode 100644 autocomplete/zsh_autocomplete diff --git a/README.md b/README.md index 8e7e98f..b5032db 100644 --- a/README.md +++ b/README.md @@ -120,12 +120,14 @@ then add the following to ~/.bash_profile: `` #### installing completion script ([found here](https://github.com/jonhadfield/sn-cli/tree/master/autocomplete/bash_autocomplete)) ##### macOS -`` -$ cp bash_autocomplete /usr/local/etc/bash_completion.d/sn -`` +``` +$ cp bash_autocomplete /usr/local/etc/bash_completion.d/sn +$ echo "source /usr/local/etc/bash_completion.d/sn" | tee -a ~/.bashrc +``` ##### Linux `` $ cp bash_autocomplete /etc/bash_completion.d/sn +$ echo "source /etc/bash_completion.d/sn" | tee -a ~/.bashrc `` ##### autocomplete commands diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index a5c12ef..fea6ee3 100644 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -2,16 +2,34 @@ : ${PROG:=$(basename ${BASH_SOURCE})} +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +_cli_init_completion() { + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + _cli_bash_autocomplete() { - local cur opts base + if [[ "${COMP_WORDS[0]}" != "source" ]]; then + local cur opts base words COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + _cli_init_completion -n "=:" || return + fi + words=("${words[@]:0:$cword}") + if [[ "$cur" == "-"* ]]; then + requestComp="${words[*]} ${cur} --generate-bash-completion" + else + requestComp="${words[*]} --generate-bash-completion" + fi + opts=$(eval "${requestComp}" 2>/dev/null) + COMPREPLY=($(compgen -W "${opts}" -- ${cur})) return 0 + fi } -complete -F _cli_bash_autocomplete $PROG - +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG unset PROG - diff --git a/autocomplete/powershell_autocomplete.ps1 b/autocomplete/powershell_autocomplete.ps1 new file mode 100644 index 0000000..cfffbf4 --- /dev/null +++ b/autocomplete/powershell_autocomplete.ps1 @@ -0,0 +1,9 @@ +$fn = $($MyInvocation.MyCommand.Name) +$name = $fn -replace "(.*)\.ps1$", '$1' +Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { + param($commandName, $wordToComplete, $cursorPosition) + $other = "$wordToComplete --generate-bash-completion" + Invoke-Expression $other | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } diff --git a/autocomplete/zsh_autocomplete b/autocomplete/zsh_autocomplete new file mode 100644 index 0000000..b519666 --- /dev/null +++ b/autocomplete/zsh_autocomplete @@ -0,0 +1,20 @@ +#compdef $PROG + +_cli_zsh_autocomplete() { + local -a opts + local cur + cur=${words[-1]} + if [[ "$cur" == "-"* ]]; then + opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") + else + opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}") + fi + + if [[ "${opts[1]}" != "" ]]; then + _describe 'values' opts + else + _files + fi +} + +compdef _cli_zsh_autocomplete $PROG diff --git a/cmd/sncli/add.go b/cmd/sncli/add.go index d48ed4c..48a8af9 100644 --- a/cmd/sncli/add.go +++ b/cmd/sncli/add.go @@ -11,11 +11,10 @@ func cmdAdd() *cli.Command { Name: "add", Usage: "add items", BashComplete: func(c *cli.Context) { - addTasks := []string{"tag", "note"} if c.NArg() > 0 { return } - for _, t := range addTasks { + for _, t := range []string{"tag", "note"} { fmt.Println(t) } }, @@ -27,7 +26,9 @@ func cmdAdd() *cli.Command { if c.NArg() > 0 { return } - fmt.Println("--title", "--parent", "--parent-uuid") + for _, t := range []string{"--title", "--parent", "--parent-uuid"} { + fmt.Println(t) + } }, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/sncli/get.go b/cmd/sncli/get.go index f2b1ca6..09a98f7 100644 --- a/cmd/sncli/get.go +++ b/cmd/sncli/get.go @@ -18,7 +18,7 @@ func cmdGet() *cli.Command { Name: "get", Usage: "get items", BashComplete: func(c *cli.Context) { - addTasks := []string{"tag", "note", "settings"} + addTasks := []string{"tag", "note"} if c.NArg() > 0 { return } @@ -177,6 +177,17 @@ func cmdGet() *cli.Command { Name: "tag", Aliases: []string{"tags"}, Usage: "get tags", + BashComplete: func(c *cli.Context) { + tagTasks := []string{ + "--title", "--uuid", "--regex", "--match-all", "--count", "--output", + } + if c.NArg() > 0 { + return + } + for _, t := range tagTasks { + fmt.Println(t) + } + }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "title", @@ -217,6 +228,15 @@ func cmdGet() *cli.Command { Name: "note", Aliases: []string{"notes"}, Usage: "get notes", + BashComplete: func(c *cli.Context) { + addTasks := []string{"--title", "--text", "--tag", "--uuid", "--editor", "--include-trash", "--count"} + if c.NArg() > 0 { + return + } + for _, t := range addTasks { + fmt.Println(t) + } + }, Flags: []cli.Flag{ &cli.StringFlag{ Name: "title", @@ -244,7 +264,7 @@ func cmdGet() *cli.Command { }, &cli.BoolFlag{ Name: "count", - Usage: "useStdOut countonly", + Usage: "number of notes", }, &cli.StringFlag{ Name: "output", diff --git a/cmd/sncli/main.go b/cmd/sncli/main.go index 1b12c30..be2df6d 100644 --- a/cmd/sncli/main.go +++ b/cmd/sncli/main.go @@ -112,7 +112,13 @@ func appSetup() (app *cli.App) { app.HelpName = "-" app.Usage = "Standard Notes CLI" app.Description = "" - + app.BashComplete = func(c *cli.Context) { + for _, cmd := range c.App.Commands { + if !cmd.Hidden { + fmt.Fprintln(c.App.Writer, cmd.Name) + } + } + } app.Flags = []cli.Flag{ &cli.BoolFlag{Name: "debug", Value: viper.GetBool("debug")}, &cli.StringFlag{Name: "server", Value: viper.GetString("server")}, diff --git a/cmd/sncli/note.go b/cmd/sncli/note.go index 6b1a35b..c3b36a7 100644 --- a/cmd/sncli/note.go +++ b/cmd/sncli/note.go @@ -509,6 +509,12 @@ func outputNotes(c *cli.Context, count bool, output string, getNoteConfig sncli. return nil } + if count { + _, _ = fmt.Fprintf(c.App.Writer, fmt.Sprintf("%d\n", len(rawNotes))) + + return nil + } + var numResults int var notesYAML []sncli.NoteYAML @@ -518,7 +524,7 @@ func outputNotes(c *cli.Context, count bool, output string, getNoteConfig sncli. for _, rt := range rawNotes { numResults++ - if !count && sncli.StringInSlice(output, yamlAbbrevs, false) { + if sncli.StringInSlice(output, yamlAbbrevs, false) { noteContentOrgStandardNotesSNDetailYAML := sncli.OrgStandardNotesSNDetailYAML{ ClientUpdatedAt: rt.(*items.Note).Content.GetAppData().OrgStandardNotesSN.ClientUpdatedAt, } @@ -549,7 +555,7 @@ func outputNotes(c *cli.Context, count bool, output string, getNoteConfig sncli. }) } - if !count && strings.ToLower(output) == "json" { + if strings.ToLower(output) == "json" { noteContentOrgStandardNotesSNDetailJSON := sncli.OrgStandardNotesSNDetailJSON{ ClientUpdatedAt: rt.(*items.Note).Content.GetAppData().OrgStandardNotesSN.ClientUpdatedAt, Pinned: rt.(*items.Note).Content.GetAppData().OrgStandardNotesSN.Pinned, @@ -590,13 +596,16 @@ func outputNotes(c *cli.Context, count bool, output string, getNoteConfig sncli. } output = c.String("output") + var bOutput []byte + switch strings.ToLower(output) { case "json": bOutput, err = json.MarshalIndent(notesJSON, "", " ") case "yaml": bOutput, err = yaml.Marshal(notesYAML) } + if len(bOutput) > 0 { if output == "json" { fmt.Print("{\n \"items\": ") @@ -656,10 +665,10 @@ func processAddNotes(c *cli.Context, opts configOptsOutput) (err error) { } if err = AddNoteInput.Run(); err != nil { - return fmt.Errorf("failed to add note. %+v", err) + return fmt.Errorf("failed to add note: %+v", err) } - _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprintf("%s: %s", msgNoteAdded, title)) + _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprintf("%s: %s\n", msgNoteAdded, title)) return nil } diff --git a/cmd/sncli/register.go b/cmd/sncli/register.go index 5336ef0..92dc9dc 100644 --- a/cmd/sncli/register.go +++ b/cmd/sncli/register.go @@ -17,7 +17,10 @@ func cmdRegister() *cli.Command { if c.NArg() > 0 { return } - fmt.Println("--email") + + for _, t := range []string{"--email"} { + fmt.Println(t) + } }, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/sncli/resync.go b/cmd/sncli/resync.go index eddae01..4e725a1 100644 --- a/cmd/sncli/resync.go +++ b/cmd/sncli/resync.go @@ -11,6 +11,9 @@ func cmdResync() *cli.Command { return &cli.Command{ Name: "resync", Usage: "purge cache and resync content", + BashComplete: func(c *cli.Context) { + return + }, Action: func(c *cli.Context) error { opts := getOpts(c) diff --git a/cmd/sncli/session.go b/cmd/sncli/session.go index a420b59..fbadf29 100644 --- a/cmd/sncli/session.go +++ b/cmd/sncli/session.go @@ -31,7 +31,15 @@ func cmdSession() *cli.Command { Required: false, }, }, - Hidden: false, + BashComplete: func(c *cli.Context) { + if c.NArg() > 0 { + return + } + + for _, t := range []string{"--add", "--remove", "--status", "--session-key"} { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { opts := getOpts(c) @@ -81,12 +89,13 @@ func processSession(c *cli.Context, opts configOptsOutput) (err error) { if sStatus { var msg string + msg, err = session.SessionStatus(sessKey, nil) if err != nil { return err } - _, _ = fmt.Fprint(c.App.Writer, msg) + _, _ = fmt.Fprint(c.App.Writer, msg+"\n") } return err diff --git a/cmd/sncli/stats.go b/cmd/sncli/stats.go index 2b1cc63..f772bf0 100644 --- a/cmd/sncli/stats.go +++ b/cmd/sncli/stats.go @@ -11,6 +11,11 @@ func cmdStats() *cli.Command { return &cli.Command{ Name: "stats", Usage: "show statistics", + BashComplete: func(c *cli.Context) { + if c.NArg() > 0 { + return + } + }, Action: func(c *cli.Context) error { opts := getOpts(c) diff --git a/cmd/sncli/tag.go b/cmd/sncli/tag.go index bfb7772..5a86d48 100644 --- a/cmd/sncli/tag.go +++ b/cmd/sncli/tag.go @@ -425,7 +425,7 @@ func processAddTags(c *cli.Context, opts configOptsOutput) (err error) { var msg string // present results if len(ato.Added) > 0 { - _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprint(msgTagAdded+": ", strings.Join(ato.Added, ", "))) + _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprint(msgTagAdded+": ", strings.Join(ato.Added, ", "), "\n")) return err } @@ -436,7 +436,7 @@ func processAddTags(c *cli.Context, opts configOptsOutput) (err error) { msg += "\n" } - _, _ = fmt.Fprintf(c.App.Writer, color.Yellow.Sprint(msgTagAlreadyExists+": "+strings.Join(ato.Existing, ", "))) + _, _ = fmt.Fprintf(c.App.Writer, color.Yellow.Sprint(msgTagAlreadyExists+": "+strings.Join(ato.Existing, ", "), "\n")) } _, _ = fmt.Fprintf(c.App.Writer, "%s\n", msg) @@ -545,7 +545,6 @@ func cmdTag() *cli.Command { return &cli.Command{ Name: "tag", Usage: "tag items", - Flags: []cli.Flag{ &cli.StringFlag{ Name: "find-title", @@ -572,6 +571,16 @@ func cmdTag() *cli.Command { Usage: "ignore case when matching", }, }, + BashComplete: func(c *cli.Context) { + if c.NArg() > 0 { + return + } + for _, t := range []string{ + "--find-title", "--find-text", "--find-tag", "--title", "--purge", "--ignore-case", + } { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { opts := getOpts(c) @@ -579,7 +588,7 @@ func cmdTag() *cli.Command { return err } - _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprintf("%s", msgTagSuccess)) + _, _ = fmt.Fprintf(c.App.Writer, color.Green.Sprint(msgTagSuccess, "\n")) return nil }, diff --git a/cmd/sncli/task.go b/cmd/sncli/task.go index 55aa396..f56ef08 100644 --- a/cmd/sncli/task.go +++ b/cmd/sncli/task.go @@ -35,7 +35,16 @@ const ( func cmdTask() *cli.Command { return &cli.Command{ Name: "task", - Usage: "task", + Usage: "manage checklist tasks", + BashComplete: func(c *cli.Context) { + addTasks := []string{"add", "list", "show", "complete", "reopen", "delete"} + if c.NArg() > 0 { + return + } + for _, t := range addTasks { + fmt.Println(t) + } + }, Subcommands: []*cli.Command{ cmdTaskAddTask(), cmdTaskComplete(), @@ -87,7 +96,15 @@ func cmdTaskAddTask() *cli.Command { &cli.StringFlag{Name: flagGroupName, Aliases: []string{"g"}, Value: viper.GetString(txtDefaultGroup)}, &cli.StringFlag{Name: flagTitleName, Required: true}, }, - + BashComplete: func(c *cli.Context) { + addTasks := []string{"--title", "--list", "--group"} + if c.NArg() > 0 { + return + } + for _, t := range addTasks { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { if c.String(flagTasklistName) == "" && c.String(flagUUIDName) == "" { return fmt.Errorf("either --%s or --%s must be specified", flagTasklistName, flagUUIDName) @@ -201,7 +218,15 @@ func cmdTaskDelete() *cli.Command { &cli.StringFlag{Name: flagGroupName, Aliases: []string{"g"}, Value: viper.GetString(txtDefaultGroup)}, &cli.StringFlag{Name: flagTitleName, Aliases: []string{flagTaskName}}, }, - + BashComplete: func(c *cli.Context) { + addTasks := []string{"--title", "--list", "--group"} + if c.NArg() > 0 { + return + } + for _, t := range addTasks { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { opts := getOpts(c) @@ -261,7 +286,14 @@ func cmdTaskComplete() *cli.Command { &cli.StringFlag{Name: flagTitleName}, &cli.StringFlag{Name: flagUUIDName}, }, - + BashComplete: func(c *cli.Context) { + if c.NArg() > 0 { + return + } + for _, t := range []string{"--title", "--list", "--group", "--uuid"} { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { if c.String(flagTasklistName) == "" && c.String(flagUUIDName) == "" { return fmt.Errorf("either --%s or --%s must be specified", flagTasklistName, flagUUIDName) diff --git a/cmd/sncli/wipe.go b/cmd/sncli/wipe.go index e9aa62b..8580e6e 100644 --- a/cmd/sncli/wipe.go +++ b/cmd/sncli/wipe.go @@ -16,13 +16,22 @@ func cmdWipe() *cli.Command { Flags: []cli.Flag{ &cli.BoolFlag{ Name: "yes", - Usage: "ignore warning", + Usage: "accpet warning", }, &cli.BoolFlag{ Name: "everything", Usage: "wipe settings also", }, }, + BashComplete: func(c *cli.Context) { + if c.NArg() > 0 { + return + } + + for _, t := range []string{"--yes", "--everything"} { + fmt.Println(t) + } + }, Action: func(c *cli.Context) error { opts := getOpts(c)