1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-07-30 21:57:44 +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
- 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 `<repo_root>/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
- `<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
```
Completions in this directory should still work in recent Nushell releases.

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 })]
]
}
}