From a19339cdaa94db45206d0656ccad57020b4830c9 Mon Sep 17 00:00:00 2001 From: Douglas <32344964+NotTheDr01ds@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:57:46 -0400 Subject: [PATCH] Move custom-completion auto-generate scripts to `needs-update` (#1076) A user in Discord recently tried to run the auto-generate script to create completions from ` --help` and had issues. In examining the scripts, I found that they used `build-string`, which was deprecated and removed over 2 years ago. In order to prevent future confusion until (and if) these files are updated, I'm moving them to a `needs-update` directory in the repo. Any other outdated files can be moved there as well. --- custom-completions/auto-generate/README.md | 105 +------------- needs-update/README.md | 4 + .../auto-generate/README.md | 101 ++++++++++++++ .../auto-generate/parse-fish.nu | 132 ++++++++++++++++++ .../auto-generate/parse-help.nu | 79 +++++++++++ 5 files changed, 322 insertions(+), 99 deletions(-) create mode 100644 needs-update/README.md create mode 100644 needs-update/custom-completions/auto-generate/README.md create mode 100644 needs-update/custom-completions/auto-generate/parse-fish.nu create mode 100644 needs-update/custom-completions/auto-generate/parse-help.nu diff --git a/custom-completions/auto-generate/README.md b/custom-completions/auto-generate/README.md index d616424..2a7237c 100644 --- a/custom-completions/auto-generate/README.md +++ b/custom-completions/auto-generate/README.md @@ -1,101 +1,8 @@ -# auto-generate completions +# Note -- basic helpers to parse --help information from cli commands and export nu completions source -- basic helpers tp parse .fish complete files and export nu completions source +Completions in this directory were generated via the auto-generate scripts. +These scripts are currently outdated and will not work in recent releases +of Nushell. The scripts themselves have been moved to `/need-update/custom-completions/auto-generate` +until they are updated. -# parse-fish - -## current -- only parses out simple complete's with no complete's boolean arguments like -f -- does not map fish autocomplete helpers to nu-complete helps (a nu library of autocomplete utils would be neat) - -## usage - -be in a directory with one or more .fish completion scripts - -A good one is - -`git clone https://github.com/fish-shell/fish-shell` -`cd fish-shell/share/completions` - -To build all .fish files in the current directory `build-completions-from-pwd` - -```nu -build-completions-from-pwd -ls *.nu -``` - -To build a single .fish file and choose the output file -```nu -build-completion cargo.fish cargo.nu -``` -# parse-help - -## current limitations - -- Only flags are parsed, arguments are not parsed and ...args is injected at the end to catch all -- Some examples of `--flags` in descriptions can throw off the regex and get included in the parsed flags -- `` (types) to flags are parsed, but not added to the nu shell completion type hints - -## usage - -generate and save source to a file - -```nu -source parse-help.nu -cargo --help | parse-help | make-completion cargo | save cargo.nu -``` - -## example - -This can be saved to a file and sourced. Example of output - -```nu -extern "cargo" [ - --version(-V) #Print version info and exit - --list #List installed commands - --explain #Run `rustc --explain CODE` - --verbose(-v) #Use verbose output (-vv very verbose/build.rs output) - --quiet(-q) #Do not print cargo log messages - --color #Coloring: auto, always, never - --frozen #Require Cargo.lock and cache are up to date - --locked #Require Cargo.lock is up to date - --offline #Run without accessing the network - --config #Override a configuration value (unstable) - --help(-h) #Print help information - ...args -] - -extern "nu" [ - --help(-h) #Display this help message - --stdin #redirect the stdin - --login(-l) #start as a login shell - --interactive(-i) #start as an interactive shell - --version(-v) #print the version - --perf(-p) #start and print performance metrics during startup - --testbin #run internal test binary - --commands(-c) #run the given commands and then exit - --config #start with an alternate config file - --env-config #start with an alternate environment config file - --log-level #log level for performance logs - --threads(-t) #threads to use for parallel commands - ...args -] -``` - -Which outputs like so on tab completion for `cargo --` -``` -❯ | cargo -- ---color Coloring: auto, always, never ---config Override a configuration value (unstable) ---explain Run `rustc --explain CODE` ---frozen Require Cargo.lock and cache are up to date ---help Display this help message ---help Print help information ---list List installed commands ---locked Require Cargo.lock is up to date ---offline Run without accessing the network ---quiet Do not print cargo log messages ---verbose Use verbose output (-vv very verbose/build.rs output) ---version Print version info and exit -``` \ No newline at end of file +Completions in this directory should still work in recent Nushell releases. diff --git a/needs-update/README.md b/needs-update/README.md new file mode 100644 index 0000000..038983b --- /dev/null +++ b/needs-update/README.md @@ -0,0 +1,4 @@ +# Warning + +Files in this directory have been confirmed as not currently working in recent Nushell releases +and will need updates due to breaking changes. diff --git a/needs-update/custom-completions/auto-generate/README.md b/needs-update/custom-completions/auto-generate/README.md new file mode 100644 index 0000000..d616424 --- /dev/null +++ b/needs-update/custom-completions/auto-generate/README.md @@ -0,0 +1,101 @@ +# auto-generate completions + +- basic helpers to parse --help information from cli commands and export nu completions source +- basic helpers tp parse .fish complete files and export nu completions source + +# parse-fish + +## current +- only parses out simple complete's with no complete's boolean arguments like -f +- does not map fish autocomplete helpers to nu-complete helps (a nu library of autocomplete utils would be neat) + +## usage + +be in a directory with one or more .fish completion scripts + +A good one is + +`git clone https://github.com/fish-shell/fish-shell` +`cd fish-shell/share/completions` + +To build all .fish files in the current directory `build-completions-from-pwd` + +```nu +build-completions-from-pwd +ls *.nu +``` + +To build a single .fish file and choose the output file +```nu +build-completion cargo.fish cargo.nu +``` +# parse-help + +## current limitations + +- Only flags are parsed, arguments are not parsed and ...args is injected at the end to catch all +- Some examples of `--flags` in descriptions can throw off the regex and get included in the parsed flags +- `` (types) to flags are parsed, but not added to the nu shell completion type hints + +## usage + +generate and save source to a file + +```nu +source parse-help.nu +cargo --help | parse-help | make-completion cargo | save cargo.nu +``` + +## example + +This can be saved to a file and sourced. Example of output + +```nu +extern "cargo" [ + --version(-V) #Print version info and exit + --list #List installed commands + --explain #Run `rustc --explain CODE` + --verbose(-v) #Use verbose output (-vv very verbose/build.rs output) + --quiet(-q) #Do not print cargo log messages + --color #Coloring: auto, always, never + --frozen #Require Cargo.lock and cache are up to date + --locked #Require Cargo.lock is up to date + --offline #Run without accessing the network + --config #Override a configuration value (unstable) + --help(-h) #Print help information + ...args +] + +extern "nu" [ + --help(-h) #Display this help message + --stdin #redirect the stdin + --login(-l) #start as a login shell + --interactive(-i) #start as an interactive shell + --version(-v) #print the version + --perf(-p) #start and print performance metrics during startup + --testbin #run internal test binary + --commands(-c) #run the given commands and then exit + --config #start with an alternate config file + --env-config #start with an alternate environment config file + --log-level #log level for performance logs + --threads(-t) #threads to use for parallel commands + ...args +] +``` + +Which outputs like so on tab completion for `cargo --` +``` +❯ | cargo -- +--color Coloring: auto, always, never +--config Override a configuration value (unstable) +--explain Run `rustc --explain CODE` +--frozen Require Cargo.lock and cache are up to date +--help Display this help message +--help Print help information +--list List installed commands +--locked Require Cargo.lock is up to date +--offline Run without accessing the network +--quiet Do not print cargo log messages +--verbose Use verbose output (-vv very verbose/build.rs output) +--version Print version info and exit +``` \ No newline at end of file diff --git a/needs-update/custom-completions/auto-generate/parse-fish.nu b/needs-update/custom-completions/auto-generate/parse-fish.nu new file mode 100644 index 0000000..7615d19 --- /dev/null +++ b/needs-update/custom-completions/auto-generate/parse-fish.nu @@ -0,0 +1,132 @@ +# a .fish complete file usually looks like a like +# `complete -c command -n '__fish_seen_subcommand_from arg' -a arg -l long -s short -d 'description' +# attempt to loosely pasrse it and convert to nu completions + +# parse every .fish file in the current directory and make a .nu completions file of it +def build-completions-from-pwd [] { + ls *.fish | par-each { |f| + let out = ($f.name | str replace ".fish" ".nu") + print $"building nushell completions from ($f.name)" + build-completion $f.name $out + } +} + +# build a completion form a .fish file and generate a .nu file +def build-completion [fish_file: path, nu_file: path] { + open $fish_file | parse-fish | make-commands-completion | str join "\n\n" | save $nu_file +} + +# parse a .fish file based on autogenerated complete syntax +# returns a table of flags to arguments +# currently only handles complete's args that don't use boolean flags (e.g. -f) +def parse-fish [] { + let data = ( + $in | tokenize-complete-lines + | where (($it | length) mod 2) == 1 # currently we only support complete args that all have args (pairs). including 'complete' this means an odd number of tokens + | each { |tokens| $tokens | pair-args } # turn the tokens into a list of pairs + | flatten # merge them all into a top level label + ) + # default every column in the table to "" to make processing easier + # some values having null often breaks nu or requires lots of checking + $data | columns | reduce -f $data { |c, acc| + $acc | default "" $c + } + | default "" a + | cleanup_subcommands # clean garbage subcommands +} + +# tokenize each line of the fish file into a list of tokens +# make use of detect columns -n which with one like properly tokenizers arguments including across quotes +def tokenize-complete-lines [] { + lines + | each { |line| + $line + | where $line starts-with complete + | str replace -a "\\\\'" "" # remove escaped quotes ' which break detect columns + | str replace -a "-f " "" # remove -f which is a boolean flag we don't support yet + | detect columns -n + | transpose -i tokens # turn columns into items, each is a token + } + | where ($it | length) > 0 # remove any empty lines + | get tokens # get the list of tokens +} + +# turn a list of tokens for a line into a record of {flag: arg} +def pair-args [] { + where $it != complete # drop complete command as we don't need it + | window 2 -s 2 # group by ordered pairs, using window 2 -s 2 instead of group 2 to automatically drop any left overs + | each { |pair| + [ + {$"($pair.0 | str trim -c '-')": ($pair.1 | unquote)} # turn into a [{ :}] removing quotes + ] + } + | reduce { |it, acc| $acc | merge $it } # merge the list of records into one big record +} + +def unquote [] { + str trim -c "\'" # trim ' + | str trim -c "\"" # trim " +} + +# remove any entries which contain things in subcommands that may be fish functions or incorrect parses +def cleanup_subcommands [] { + where (not ($it.a | str contains "$")) and (not ($it.a | str starts-with "-")) and (not ($it.a starts-with "(")) +} + +# from a parsed fish table, create the completion for it's command and sub commands +def make-commands-completion [] { + let fishes = $in + $fishes + | get c # c is the command name + | uniq # is cloned on every complete line + | each { |command| + $fishes | where c == $command | make-subcommands-completion $command + } +} + +# make the action nu completion string from subcommand and args +# subcommand can be empty which will be the root command +def make-subcommands-completion [parents: list] { + let quote = '"' # " + let fishes = $in + $fishes + | group-by a # group by sub command (a flag) + | transpose name args # turn it into a table of name to arguments + | each {|subcommand| + build-string ( + if ('d' in ($subcommand.args | columns)) and ($subcommand.args.d != "") { + build-string "# " ($subcommand.args.d.0) "\n" # (sub)command description + }) "extern " $quote ($parents | str join " ") ( + if $subcommand.name != "" { + build-string " " $subcommand.name # sub command if present + }) $quote " [\n" ( + $fishes + | if ('n' in ($in | columns)) { + if ($subcommand.name != "") { + where ($it.n | str contains $subcommand.name) # for subcommand -> any where n matches `__fish_seen_subcommand_from arg` for the subcommand name + } else { + where ($it.n == "__fish_use_subcommand") and ($it.a == "") # for root command -> any where n == __fish_use_subcommand and a is empty. otherwise a means a subcommand + } + } else { + $fishes # catch all + } + | build-flags + | str join "\n" + ) "\n\t...args\n]" + } +} + +# build the list of flag string in nu syntax +def build-flags [] { + each { |subargs| + if ('l' in ($subargs | columns)) and ($subargs.l != "") { + build-string "\t--" $subargs.l (build-string + (if ('s' in ($subargs | columns)) and ($subargs.s != "") { + build-string "(-" $subargs.s ")" + }) (if ('d' in ($subargs | columns)) and ($subargs.d != "") { + build-string "\t\t\t\t\t# " $subargs.d + }) + ) + } + } +} diff --git a/needs-update/custom-completions/auto-generate/parse-help.nu b/needs-update/custom-completions/auto-generate/parse-help.nu new file mode 100644 index 0000000..d8c3bee --- /dev/null +++ b/needs-update/custom-completions/auto-generate/parse-help.nu @@ -0,0 +1,79 @@ + + +# parses a input string in --help format and returns a table of parsed flags +def parse-help [] { + # help format ' -s, --long description ' + $in | parse -r '\s\s+(?:-(?P\w)[,\s]+)?(?:--(?P[\w-]+))\s*(?:<(?P.*)>)?\s*(?P.*)?' +} + +# takes a table of parsed help commands in format [short? long format? description] +def make-completion [command_name: string] { + build-string "extern \"" $command_name "\" [\n" ($in | each { |it| + build-string "\t--" $it.long (if ($it.short | is-empty) == false { + build-string "(-" $it.short ")" + }) (if ($it.description | is-empty) == false { + build-string "\t\t# " $it.description + }) + } | str join "\n") "\n\t...args\n]" +} + +module tests { + def test-cargo [] { + let expect = [ + [short long format description]; + [V version '' 'Print version info and exit'] + ['' list '' 'List installed commands'] + ['' explain 'CODE' 'Run `rustc --explain CODE`'] + [v verbose '' 'Use verbose output (-vv very verbose/build.rs output)'] + [q quiet '' 'Do not print cargo log messages'] + ['' color 'WHEN' 'Coloring: auto, always, never'] + ['' frozen '' 'Require Cargo.lock and cache are up to date'] + ['' locked '' 'Require Cargo.lock is up to date'] + ['' offline '' 'Run without accessing the network'] + ['' config 'KEY=VALUE' 'Override a configuration value (unstable)'] + [h help '' 'Print help information'] + ] + + let result = (cargo --help | parse-help) + + if $result == $expect { + "passed" + } else { + $result + } + } + + def test-nu [] { + let expect = [ + [short long format description]; + [h help '' 'Display this help message'] + ['' stdin '' 'redirect the stdin'] + [l login '' 'start as a login shell'] + [i interactive '' 'start as an interactive shell'] + [v version '' 'print the version'] + [p perf '' 'start and print performance metrics during startup'] + ['' testbin 'String' 'run internal test binary'] + [c commands 'String' 'run the given commands and then exit'] + ['' config 'String' 'start with an alternate config file'] + ['' env-config 'String' 'start with an alternate environment config file'] + ['' log-level 'String' 'log level for performance logs'] + ['t' threads 'Int' 'threads to use for parallel commands'] + ] + + let result = (nu --help | parse-help) + + if $result == $expect { + "passed" + } else { + $result + } + } + + export def run-tests [] { + [ + [test result]; + ['cargo' (do { test-cargo })] + ['nu' (do { test-nu })] + ] + } +}