summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Verbeek <jan.verbeek@posteo.nl>2023-12-11 07:03:49 +0100
committerAndrew Gallant <jamslam@gmail.com>2024-01-06 10:39:35 -0500
commite0a85678e112759f56f9e07d04a29f2eed6eb348 (patch)
tree4c29e7163b42f6530672016da28059a1bf49b54e
parent23af5fb043f8e625294e210446090f17dbd7e3bf (diff)
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
-rw-r--r--CHANGELOG.md10
-rw-r--r--crates/core/flags/complete/encodings.sh29
-rw-r--r--crates/core/flags/complete/fish.rs70
-rw-r--r--crates/core/flags/complete/mod.rs2
-rw-r--r--crates/core/flags/complete/rg.zsh26
-rw-r--r--crates/core/flags/complete/zsh.rs2
-rw-r--r--crates/core/flags/defs.rs23
-rw-r--r--crates/core/flags/mod.rs21
8 files changed, 131 insertions, 52 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 366f904e..e6d56e7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+14.1.0 (TBD)
+============
+This is a minor release with a few small new features and bug fixes.
+
+Feature enhancements:
+
+* [FEATURE #2684](https://github.com/BurntSushi/ripgrep/issues/2684):
+ Improve completions for the `fish` shell.
+
+
14.0.3 (2023-11-28)
===================
This is a patch release with a bug fix for the `--sortr` flag.
diff --git a/crates/core/flags/complete/encodings.sh b/crates/core/flags/complete/encodings.sh
new file mode 100644
index 00000000..14fc4a85
--- /dev/null
+++ b/crates/core/flags/complete/encodings.sh
@@ -0,0 +1,29 @@
+# This is impossible to read, but these encodings rarely if ever change, so
+# it probably does not matter. They are derived from the list given here:
+# https://encoding.spec.whatwg.org/#concept-encoding-get
+#
+# The globbing here works in both fish and zsh (though they expand it in
+# different orders). It may work in other shells too.
+
+{{,us-}ascii,arabic,chinese,cyrillic,greek{,8},hebrew,korean}
+logical visual mac {,cs}macintosh x-mac-{cyrillic,roman,ukrainian}
+866 ibm{819,866} csibm866
+big5{,-hkscs} {cn-,cs}big5 x-x-big5
+cp{819,866,125{0,1,2,3,4,5,6,7,8}} x-cp125{0,1,2,3,4,5,6,7,8}
+csiso2022{jp,kr} csiso8859{6,8}{e,i}
+csisolatin{1,2,3,4,5,6,9} csisolatin{arabic,cyrillic,greek,hebrew}
+ecma-{114,118} asmo-708 elot_928 sun_eu_greek
+euc-{jp,kr} x-euc-jp cseuckr cseucpkdfmtjapanese
+{,x-}gbk csiso58gb231280 gb18030 {,cs}gb2312 gb_2312{,-80} hz-gb-2312
+iso-2022-{cn,cn-ext,jp,kr}
+iso8859{,-}{1,2,3,4,5,6,7,8,9,10,11,13,14,15}
+iso-8859-{1,2,3,4,5,6,7,8,9,10,11,{6,8}-{e,i},13,14,15,16} iso_8859-{1,2,3,4,5,6,7,8,9,15}
+iso_8859-{1,2,6,7}:1987 iso_8859-{3,4,5,8}:1988 iso_8859-9:1989
+iso-ir-{58,100,101,109,110,126,127,138,144,148,149,157}
+koi{,8,8-r,8-ru,8-u,8_r} cskoi8r
+ks_c_5601-{1987,1989} ksc{,_}5691 csksc56011987
+latin{1,2,3,4,5,6} l{1,2,3,4,5,6,9}
+shift{-,_}jis csshiftjis {,x-}sjis ms_kanji ms932
+utf{,-}8 utf-16{,be,le} unicode-1-1-utf-8
+windows-{31j,874,949,125{0,1,2,3,4,5,6,7,8}} dos-874 tis-620 ansi_x3.4-1968
+x-user-defined auto none
diff --git a/crates/core/flags/complete/fish.rs b/crates/core/flags/complete/fish.rs
index e55d72e3..f8f7133b 100644
--- a/crates/core/flags/complete/fish.rs
+++ b/crates/core/flags/complete/fish.rs
@@ -2,17 +2,13 @@
Provides completions for ripgrep's CLI for the fish shell.
*/
-use crate::flags::defs::FLAGS;
+use crate::flags::{defs::FLAGS, CompletionType};
-const TEMPLATE: &'static str =
- "complete -c rg -n '__fish_use_subcommand' !SHORT! !LONG! !DOC!\n";
-const TEMPLATE_CHOICES: &'static str =
- "complete -c rg -n '__fish_use_subcommand' !SHORT! !LONG! !DOC! -r -f -a '!CHOICES!'\n";
+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.
-///
-/// Note that these completions are based on what was produced for ripgrep <=13
-/// using Clap 2.x. Improvements on this are welcome.
pub(crate) fn generate() -> String {
let mut out = String::new();
for flag in FLAGS.iter() {
@@ -20,25 +16,49 @@ pub(crate) fn generate() -> String {
None => "".to_string(),
Some(byte) => format!("-s {}", char::from(byte)),
};
- let long = format!("-l '{}'", flag.name_long().replace("'", "\\'"));
- let doc = format!("-d '{}'", flag.doc_short().replace("'", "\\'"));
- let template = if flag.doc_choices().is_empty() {
- TEMPLATE.to_string()
- } else {
- TEMPLATE_CHOICES
- .replace("!CHOICES!", &flag.doc_choices().join(" "))
- };
- out.push_str(
- &template
- .replace("!SHORT!", &short)
- .replace("!LONG!", &long)
- .replace("!DOC!", &doc),
- );
+ 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() {
- let long = format!("-l '{}'", negated.replace("'", "\\'"));
out.push_str(
- &TEMPLATE
- .replace("!SHORT!", "")
+ &TEMPLATE_NEGATED
+ .replace("!NEGATED!", &negated)
+ .replace("!SHORT!", &short)
.replace("!LONG!", &long)
.replace("!DOC!", &doc),
);
diff --git a/crates/core/flags/complete/mod.rs b/crates/core/flags/complete/mod.rs
index 7d2fb606..b531bf12 100644
--- a/crates/core/flags/complete/mod.rs
+++ b/crates/core/flags/complete/mod.rs
@@ -2,6 +2,8 @@
Modules for generating completions for various shells.
*/
+static ENCODINGS: &'static str = include_str!("encodings.sh");
+
pub(super) mod bash;
pub(super) mod fish;
pub(super) mod powershell;
diff --git a/crates/core/flags/complete/rg.zsh b/crates/core/flags/complete/rg.zsh
index 7ca89008..e9731a16 100644
--- a/crates/core/flags/complete/rg.zsh
+++ b/crates/core/flags/complete/rg.zsh
@@ -413,32 +413,8 @@ _rg_encodings() {
local -a expl
local -aU _encodings
- # This is impossible to read, but these encodings rarely if ever change, so it
- # probably doesn't matter. They are derived from the list given here:
- # https://encoding.spec.whatwg.org/#concept-encoding-get
_encodings=(
- {{,us-}ascii,arabic,chinese,cyrillic,greek{,8},hebrew,korean}
- logical visual mac {,cs}macintosh x-mac-{cyrillic,roman,ukrainian}
- 866 ibm{819,866} csibm866
- big5{,-hkscs} {cn-,cs}big5 x-x-big5
- cp{819,866,125{0..8}} x-cp125{0..8}
- csiso2022{jp,kr} csiso8859{6,8}{e,i}
- csisolatin{{1..6},9} csisolatin{arabic,cyrillic,greek,hebrew}
- ecma-{114,118} asmo-708 elot_928 sun_eu_greek
- euc-{jp,kr} x-euc-jp cseuckr cseucpkdfmtjapanese
- {,x-}gbk csiso58gb231280 gb18030 {,cs}gb2312 gb_2312{,-80} hz-gb-2312
- iso-2022-{cn,cn-ext,jp,kr}
- iso8859{,-}{{1..11},13,14,15}
- iso-8859-{{1..11},{6,8}-{e,i},13,14,15,16} iso_8859-{{1..9},15}
- iso_8859-{1,2,6,7}:1987 iso_8859-{3,4,5,8}:1988 iso_8859-9:1989
- iso-ir-{58,100,101,109,110,126,127,138,144,148,149,157}
- koi{,8,8-r,8-ru,8-u,8_r} cskoi8r
- ks_c_5601-{1987,1989} ksc{,_}5691 csksc56011987
- latin{1..6} l{{1..6},9}
- shift{-,_}jis csshiftjis {,x-}sjis ms_kanji ms932
- utf{,-}8 utf-16{,be,le} unicode-1-1-utf-8
- windows-{31j,874,949,125{0..8}} dos-874 tis-620 ansi_x3.4-1968
- x-user-defined auto none
+!ENCODINGS!
)
_wanted encodings expl encoding compadd -a "$@" - _encodings
diff --git a/crates/core/flags/complete/zsh.rs b/crates/core/flags/complete/zsh.rs
index 59876f18..7475c0c1 100644
--- a/crates/core/flags/complete/zsh.rs
+++ b/crates/core/flags/complete/zsh.rs
@@ -19,5 +19,5 @@ long as it meets criteria 3 and 4 above.
/// Generate completions for zsh.
pub(crate) fn generate() -> String {
- include_str!("rg.zsh").to_string()
+ include_str!("rg.zsh").replace("!ENCODINGS!", super::ENCODINGS.trim_end())
}
diff --git a/crates/core/flags/defs.rs b/crates/core/flags/defs.rs
index a0ace969..0abf0c92 100644
--- a/crates/core/flags/defs.rs
+++ b/crates/core/flags/defs.rs
@@ -34,6 +34,8 @@ use crate::flags::{
#[cfg(test)]
use crate::flags::parse::parse_low_raw;
+use super::CompletionType;
+
/// A list of all flags in ripgrep via implementations of `Flag`.
///
/// The order of these flags matter. It determines the order of the flags in
@@ -1582,6 +1584,9 @@ The encoding detection that ripgrep uses can be reverted to its automatic mode
via the \flag-negate{encoding} flag.
"
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Encoding
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
let value = match v {
@@ -1977,6 +1982,9 @@ When \flag{file} or \flag{regexp} is used, then ripgrep treats all positional
arguments as files or directories to search.
"
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Filename
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
let path = PathBuf::from(v.unwrap_value());
@@ -2808,6 +2816,9 @@ to calling \fBgethostname\fP. On Windows, this corresponds to calling
ripgrep uses your system's hostname for producing hyperlinks.
"#
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Executable
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
let path = PathBuf::from(v.unwrap_value());
@@ -3141,6 +3152,9 @@ If you are looking for a way to include or exclude files and directories
directly on the command line, then use \flag{glob} instead.
"
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Filename
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
let path = PathBuf::from(v.unwrap_value());
@@ -5410,6 +5424,9 @@ format, then \fBpzstd\fP is used to decompress the contents to stdout.
This overrides the \flag{search-zip} flag.
"#
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Executable
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
let path = match v {
@@ -6781,6 +6798,9 @@ any rules found in ignore files.
To see the list of available file types, use the \flag{type-list} flag.
"#
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Filetype
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
args.type_changes.push(TypeChange::Select {
@@ -7000,6 +7020,9 @@ will only search files that are unrecognized by its type definitions.
To see the list of available file types, use the \flag{type-list} flag.
"#
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Filetype
+ }
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
args.type_changes.push(TypeChange::Negate {
diff --git a/crates/core/flags/mod.rs b/crates/core/flags/mod.rs
index e2b0ccb8..54aec572 100644
--- a/crates/core/flags/mod.rs
+++ b/crates/core/flags/mod.rs
@@ -70,7 +70,7 @@ mod parse;
/// value. Flags that accept multiple values are an unsupported abberation.
trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
/// Returns true if this flag is a switch. When a flag is a switch, the
- /// CLI parser will look for a value after the flag is seen.
+ /// CLI parser will not look for a value after the flag is seen.
fn is_switch(&self) -> bool;
/// A short single byte name for this flag. This returns `None` by default,
@@ -150,6 +150,10 @@ trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
&[]
}
+ fn completion_type(&self) -> CompletionType {
+ CompletionType::Other
+ }
+
/// Given the parsed value (which might just be a switch), this should
/// update the state in `args` based on the value given for this flag.
///
@@ -228,6 +232,21 @@ impl Category {
}
}
+/// The kind of argument a flag accepts, to be used for shell completions.
+#[derive(Clone, Copy, Debug)]
+enum CompletionType {
+ /// No special category. is_switch() and doc_choices() may apply.
+ Other,
+ /// A path to a file.
+ Filename,
+ /// A command in $PATH.
+ Executable,
+ /// The name of a file type, as used by e.g. --type.
+ Filetype,
+ /// The name of an encoding_rs encoding, as used by --encoding.
+ Encoding,
+}
+
/// Represents a value parsed from the command line.
///
/// This doesn't include the corresponding flag, but values come in one of