1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-08-01 06:37:46 +00:00

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 `<command> --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.
This commit is contained in:
Douglas 2025-03-29 09:57:46 -04:00 committed by GitHub
parent e956708244
commit a19339cdaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 322 additions and 99 deletions

View file

@ -1,101 +1,8 @@
# auto-generate completions # Note
- basic helpers to parse --help information from cli commands and export nu completions source Completions in this directory were generated via the auto-generate scripts.
- basic helpers tp parse .fish complete files and export nu completions source These scripts are currently outdated and will not work in recent releases
of Nushell. The scripts themselves have been moved to `<repo_root>/need-update/custom-completions/auto-generate`
until they are updated.
# parse-fish Completions in this directory should still work in recent Nushell releases.
## 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
- `<format>` (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
```

4
needs-update/README.md Normal file
View file

@ -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.

View file

@ -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
- `<format>` (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
```

View file

@ -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 [{<flag> :<arg>}] 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
})
)
}
}
}

View file

@ -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 <format> description '
$in | parse -r '\s\s+(?:-(?P<short>\w)[,\s]+)?(?:--(?P<long>[\w-]+))\s*(?:<(?P<format>.*)>)?\s*(?P<description>.*)?'
}
# 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 })]
]
}
}