Expand description
§completers
[!WARNING] This crate is still a prototype, and is subject to BREAKING changes without notice.
A tiny Rust-native shell completion solution.
§💡 Examples
See examples
for a few examples of how to use this crate.
§📖 Usage
§Rust Part
§Candidates
First, define a completion handler function that takes a Completion
struct as an argument and returns a vector of completion candidates:
use completers::Completion;
fn handler(_completion: Completion) -> Vec<String> {
vec![]
}
Then, call handle_completion
BEFORE any other command that writes to stdout in your main function:
use completers::{Completion, handle_completion};
fn main() {
handle_completion(handler);
// Other logic
}
§Delegate
To delegate completion, we should first match against Completion::init()
:
use completers::{CompletersError, Completion};
use std::process::exit;
fn main() -> Result<(), CompletersError> {
match Completion::init() {
Ok(Some(completion)) => {
delegate_completion(completion)?;
}
Ok(None) => {
// No completion request, do nothing
}
Err(e) => {
eprintln!("Error: {e}");
exit(1);
}
};
// Do your job
Ok(())
}
Then, construct or mutate the Completion
object in the delegate function. We’ll delegate to cargo build --example
for example:
/// Delegates completion to `cargo build --example`, exit if successful.
fn delegate_completion(mut comp: Completion) -> Result<(), CompletersError> {
let old_words_count = comp.words.len();
comp.words.remove(0); // Discard program name
let mut new_words = vec![
"cargo".to_string(),
"build".to_string(),
"--example".to_string(),
];
new_words.append(&mut comp.words);
comp.words = new_words;
comp.word_index += comp.words.len();
comp.word_index -= old_words_count;
comp.line = comp.words.join(" ");
comp.cursor_index = comp
.words
.iter()
.take(comp.word_index)
.map(|word| word.len())
.sum::<usize>()
+ comp.word_index
+ comp.words[comp.word_index].len();
comp.delegate();
Ok(())
}
§Shell Part
§Bash
[!NOTE] By using
completers
, we assume that you’ve gotbash-completion
installed. Some features such as completion delegate won’t work without it.
Generate and evaluate the shell code via:
source <(COMPLETE=bash my_binary)
You should be able to complete your commands now. To enable completion across all your terminal sessions, you can add the above code to your completions directory, like:
mkdir -p ~/.local/share/bash-completion/completions # Create the directory if it doesn't exist
echo 'source <(COMPLETE=bash my_binary)' > ~/.local/share/bash-completion/completions/my_binary
You can also use /usr/share/bash-completion/completions/
as the directory, if you want the completion to be available system-wise.
§Nushell
Instructions for lazy loading completions will be updated once nushell/nushell#4874 is resolved. For now, you’ll have to:
COMPLETE=nu my_binary o> somewhere/handy.nu
And then source
the file from your ($nu.default-config-dir)/config.nu
:
source somewhere/handy.nu
Note that completions will only work for my_binary
, and not ./my_binary
or /path/to/my_binary
.
§The completers
Binary
Currently, the completers
binary does nothing.
§⚙️ Details
See CAVEATS.md
for a list of known problems, and see MECHANISM.md
for a detailed explanation of how this works, in case you’re curious.
§🎉 Credits
clap
, whose code and API is used as a reference. Whenclap
’s Rust-Native Completion Engine is stablized, this crate will be deprecated in favor of it.complete-alias
, whose shell code helped a lot.
§✅ TODO
- Escape special characters in generated shell code & completion candidates
-
Completion delegation
- Need to consider how to design the API
- Prototypes available in
prototype
- Extensibility (API?)
Structs§
- Completion
- A completion request. ref.
Enums§
- Completers
Error - Possible errors that can occur.
- Completion
Type - The type of completion attempted. ref.
- Shell
Code Error - Possible errors that can occur when generating shell code.
Functions§
- handle_
completion - Helper function for handling completion requests with candidates. To delegate, you’ll have to
match
Completion::init()
for yourself.