forked from nushell/nushell.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Expand external completer docs (nushell#984)
* First iteration of external completer explanation * Add cookbook with completers * Add recipe for autocompleting aliases workaround * Move examples to the cookbook
- Loading branch information
1 parent
e5e3404
commit dd55e97
Showing
2 changed files
with
176 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
--- | ||
title: External Completers | ||
--- | ||
|
||
# External Completers | ||
|
||
## Completers | ||
|
||
### Carapace completer | ||
|
||
```nu | ||
let carapace_completer = {|spans| | ||
carapace $spans.0 nushell $spans | from json | ||
} | ||
``` | ||
|
||
### Fish completer | ||
|
||
This completer will use [the fish shell](https://fishshell.com/) to handle completions. Fish handles out of the box completions for many popular tools and commands. | ||
|
||
```nu | ||
let fish_completer = {|spans| | ||
fish --command $'complete "--do-complete=($spans | str join " ")"' | ||
| $"value(char tab)description(char newline)" + $in | ||
| from tsv --flexible --no-infer | ||
} | ||
``` | ||
|
||
A couple of things to note on this command: | ||
|
||
- The fish completer will return lines of text, each one holding the `value` and `description` separated by a tab. The `description` can be missing, and in that case there won't be a tab after the `value`. If that happens, `from tsv` will fail, so we add the `--flexible` flag. | ||
- `$"value(char tab)description(char newline)" + $in` exists to fix another edge case. Even with the `--flexible` flag, if the first line of the input doesn't have a second column the parser will skip that column for **all** the input. This is fixed adding a header to the input before-hand. | ||
- `--no-infer` is optional. `from tsv` will infer the data type of the result, so a numeric value like some git hashes will be inferred as a number. `--no-infer` will keep everything as a string. It doesn't make a difference in practice but it will print a more consistent output if the completer is ran on it's own. | ||
|
||
### Zoxide completer | ||
|
||
[Zoxide](https://github.com/ajeetdsouza/zoxide) allows easily jumping between visited folders in the system. It's possible to autocomplete matching folders with this completer: | ||
|
||
```nu | ||
let zoxide_completer = {|spans| | ||
$spans | skip 1 | zoxide query -l $in | lines | where {|x| $x != $env.PWD} | ||
} | ||
``` | ||
|
||
This completer is not usable for allmost every other command, so it's recommended to add it as an override in the [multiple completer](#multiple-completer): | ||
|
||
```nu | ||
{ | ||
z: $zoxide_completer | ||
zi: $zoxide_completer | ||
} | ||
``` | ||
|
||
> **Note** | ||
> Zoxide sets an alias (`z` by default) that calls the `__zoxide_z` function. | ||
> If [alias completions](#alias-completions) are supported, the following snippet can be used instead: | ||
> | ||
> ```nu | ||
> { | ||
> __zoxide_z: $zoxide_completer | ||
> __zoxide_zi: $zoxide_completer | ||
> } | ||
> ``` | ||
### Multiple completer | ||
|
||
Sometimes, a single external completer is not flexible enough. Luckily, as many as needed can be combined into a single one. The following example uses `$default_completer` for all commands except the ones explicitly defined in the record: | ||
|
||
```nu | ||
let multiple_completers = {|spans| | ||
{ | ||
ls: $ls_completer | ||
git: $git_completer | ||
} | get -i $spans.0 | default $default_completer | do $in $spans | ||
} | ||
``` | ||
|
||
> **Note** | ||
> In the example above, `$spans.0` is the command being run at the time. The completer will try to `get` the desired completer in the record, and fallback to `$default_completer`. | ||
> | ||
> - If we try to autocomplete `git <tab>`, `spans` will be `[git ""]`. `{ ... } | get -i git` will return the `$git_completer` | ||
> - If we try to autocomplete `other_command <tab>`, `spans` will be `[other_command ""]`. `{ ... } | get -i other_command` will return null, and `default` will return the default completer. | ||
## Troubleshooting | ||
|
||
### Alias completions | ||
|
||
Nushell currently has a [bug where autocompletions won't work for aliases](https://github.com/nushell/nushell/issues/8483). This can be worked around adding the following snippet at the beginning of the completer: | ||
|
||
```nu | ||
# if the current command is an alias, get it's expansion | ||
let expanded_alias = (scope aliases | where name == $spans.0 | get -i 0 | get -i expansion) | ||
# overwrite | ||
let spans = (if $expanded_alias != null { | ||
# put the first word of the expanded alias first in the span | ||
$spans | skip 1 | prepend ($expanded_alias | split words) | ||
} else { $spans }) | ||
``` | ||
|
||
This code will take the first span, find the first alias that matches it, and replace the beginning of the command with the alias expansion. | ||
|
||
### `ERR unknown shorthand flag` using carapace | ||
|
||
Carapace will return this error when a non-supported flag is provided. For example, with `cargo -1`: | ||
|
||
| value | description | | ||
| ----- | --------------------------------- | | ||
| -1ERR | unknown shorthand flag: "1" in -1 | | ||
| -1\_ | | | ||
|
||
The solution to this involves manually checking the value to filter it out: | ||
|
||
```nu | ||
let carapace_completer = {|spans: list<string>| | ||
carapace $spans.0 nushell $spans | ||
| from json | ||
| if ($in | default [] | where value =~ '^-.*ERR$' | is-empty) { $in } else { null } | ||
} | ||
``` | ||
|
||
## Putting it all together | ||
|
||
This is an example of how an external completer definition might look like: | ||
|
||
```nu | ||
let fish_completer = ... | ||
let carapace_completer = {|spans: list<string>| | ||
carapace $spans.0 nushell $spans | ||
| from json | ||
| if ($in | default [] | where value =~ '^-.*ERR$' | is-empty) { $in } else { null } | ||
} | ||
# This completer will use carapace by default | ||
let external_completer = {|spans| | ||
let expanded_alias = (scope aliases | where name == $spans.0 | get -i 0 | get -i expansion) | ||
let spans = (if $expanded_alias != null { | ||
$spans | skip 1 | prepend ($expanded_alias | split words) | ||
} else { $spans }) | ||
{ | ||
# carapace completions are incorrect for nu | ||
nu: $fish_completer | ||
# fish completes commits and branch names in a nicer way | ||
git: $fish_completer | ||
# carapace doesn't have completions for asdf | ||
asdf: $fish_completer | ||
} | get -i $spans.0 | default $carapace_completer | do $in $spans | ||
} | ||
$env.config = { | ||
# ... | ||
completions: { | ||
external: { | ||
enabled: true | ||
completer: $external_completer | ||
} | ||
} | ||
# ... | ||
} | ||
``` |