Skip to content

optimize fish_completer path lookups #1963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions cookbook/external_completers.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ let fish_completer = {|spans|
fish --command $"complete '--do-complete=($spans | str replace --all "'" "\\'" | str join ' ')'"
| from tsv --flexible --noheaders --no-infer
| rename value description
| update value {
if ($in | path exists) {$'"($in | path expand --no-symlink | str replace --all "\"" "\\\"" )"'} else {$in}
| update value {|row|
let value = $row.value
let need_quote = ['\' ',' '[' ']' '(' ')' ' ' '\t' "'" '"' "`"] | any {$in in $value}
if ($need_quote and ($value | path exists)) {
let expanded_path = if ($value starts-with ~) {$value | path expand --no-symlink} else {$value}
$'"($expanded_path | str replace --all "\"" "\\\"")"'
} else {$value}
}
}
```
Expand All @@ -34,7 +39,7 @@ 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.
- The output of the fish completer does not contain a header (name of the columns), so we add `--noheaders` to prevent `from tsv` from treating the first row as headers and later give the columns their names using `rename`.
- `--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.
- Since fish only supports POSIX style escapes for file paths (`file\ name.txt`, etc.), file paths completed by fish will not be quoted or escaped properly on external commands. Nushell does not parse POSIX escapes, so we need to do this conversion manually such as by testing if the items are valid paths as shown in the example. This simple approach is imperfect, but it should cover 99.9% of use cases.
- Since fish only supports POSIX style escapes for file paths (`file\ name.txt`, etc.), file paths completed by fish will not be quoted or escaped properly on external commands. Nushell does not parse POSIX escapes, so we need to do this conversion manually such as by testing if the items are valid paths as shown in the example. To minimize the overhead of path lookups, we first check the string for common escape characters. If the string needs escaping, and it is a path on the filesystem, then the value is double-quoted. Also before double-quoting the file path we expand any ~ at the beginning of the path, so that completions continue to work. This simple approach is imperfect, but it should cover 99.9% of use cases.

### Multiple completer

Expand Down