1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-03-23 04:34:39 +02:00
Jan Verbeek e0a85678e1 complete/fish: improve shell completions for fish
- Stop using `-n __fish_use_subcommand`. This had the effect of
ignoring options if a positional argument has already been given, but
that's not how ripgrep works.

- Only suggest negation options if the option they're negating is
passed (e.g., only complete `--no-pcre2` if `--pcre2` is present). The
zsh completions already do this.

- Take into account whether an option takes an argument. If an option
is not a switch then it won't suggest further options until the
argument is given, e.g. `-C<tab>` won't suggest options but `-i<tab>`
will.

- Suggest correct arguments for options. We already completed a fixed
set of choices where available, but now we go further:

  - Filenames are only suggested for options that take filenames.

  - `--pre` and `--hostname-bin` suggest binaries from `$PATH`.

  - `-t`/`--type`/&c use `--type-list` for suggestions, like in zsh,
  with a preview of the glob patterns.

  - `--encoding` uses a hardcoded list extracted from the zsh
  completions. This has been refactored into a separate file, and the
  range globs (`{1..5}`) replaced by comma globs (`{1,2,3,4,5}`) since
  those work in both shells. I verified that this produces the same
  list as before in zsh, and the same list in fish (albeit in a
  different order).

PR #2684
2024-01-06 10:39:35 -05:00

69 lines
2.3 KiB
Rust

/*!
Provides completions for ripgrep's CLI for the fish shell.
*/
use crate::flags::{defs::FLAGS, CompletionType};
const TEMPLATE: &'static str = "complete -c rg !SHORT! -l !LONG! -d '!DOC!'";
const TEMPLATE_NEGATED: &'static str =
"complete -c rg -l !NEGATED! -n '__fish_contains_opt !SHORT! !LONG!' -d '!DOC!'\n";
/// Generate completions for Fish.
pub(crate) fn generate() -> String {
let mut out = String::new();
for flag in FLAGS.iter() {
let short = match flag.name_short() {
None => "".to_string(),
Some(byte) => format!("-s {}", char::from(byte)),
};
let long = flag.name_long();
let doc = flag.doc_short().replace("'", "\\'");
let mut completion = TEMPLATE
.replace("!SHORT!", &short)
.replace("!LONG!", &long)
.replace("!DOC!", &doc);
match flag.completion_type() {
CompletionType::Filename => {
completion.push_str(" -r -F");
}
CompletionType::Executable => {
completion.push_str(" -r -f -a '(__fish_complete_command)'");
}
CompletionType::Filetype => {
completion.push_str(
" -r -f -a '(rg --type-list | string replace : \\t)'",
);
}
CompletionType::Encoding => {
completion.push_str(" -r -f -a '");
completion.push_str(super::ENCODINGS);
completion.push_str("'");
}
CompletionType::Other if !flag.doc_choices().is_empty() => {
completion.push_str(" -r -f -a '");
completion.push_str(&flag.doc_choices().join(" "));
completion.push_str("'");
}
CompletionType::Other if !flag.is_switch() => {
completion.push_str(" -r -f");
}
CompletionType::Other => (),
}
completion.push('\n');
out.push_str(&completion);
if let Some(negated) = flag.name_negated() {
out.push_str(
&TEMPLATE_NEGATED
.replace("!NEGATED!", &negated)
.replace("!SHORT!", &short)
.replace("!LONG!", &long)
.replace("!DOC!", &doc),
);
}
}
out
}