# completers
[](https://github.com/PRO-2684/completers/blob/main/LICENSE)
[](https://github.com/PRO-2684/completers/blob/main/.github/workflows/release.yml)
[](https://github.com/PRO-2684/completers/releases)
[](https://github.com/PRO-2684/completers/releases)
[](https://crates.io/crates/completers)
[](https://crates.io/crates/completers)
[](https://docs.rs/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`](./examples/README.md) 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:
```rust
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:
```rust
use completers::{Completion, handle_completion};
fn main() {
handle_completion(handler);
// Other logic
}
#
# fn handler(_completion: Completion) -> Vec<String> {
# vec![]
# }
```
#### Delegate
To delegate completion, we should first match against [`Completion::init()`]:
```rust
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(())
}
#
# fn delegate_completion(mut comp: Completion) -> Result<(), CompletersError> {
# Ok(())
# }
```
Then, construct or mutate the [`Completion`] object in the delegate function. We'll delegate to `cargo build --example` for example:
```rust
# use completers::{CompletersError, Completion};
# use std::process::exit;
#
/// 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 got [`bash-completion`](https://github.com/scop/bash-completion) installed. Some features such as completion delegate won't work without it.
Generate and evaluate the shell code via:
```bash
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:
```bash
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:
```nu
COMPLETE=nu my_binary o> somewhere/handy.nu
```
And then `source` the file from your `($nu.default-config-dir)/config.nu`:
```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`](doc/CAVEATS.md) for a list of known problems, and see [`MECHANISM.md`](doc/MECHANISM.md) for a detailed explanation of how this works, in case you're curious.
## 🎉 Credits
- [`clap`](https://github.com/clap-rs/clap), whose code and API is used as a reference. When `clap`'s [Rust-Native Completion Engine](https://github.com/clap-rs/clap/issues/3166) is stablized, this crate will be deprecated in favor of it.
- [`complete-alias`](https://github.com/cykerway/complete-alias), whose shell code helped a lot.
## ✅ TODO
- [ ] Escape special characters in generated shell code & completion candidates
- [x] Completion delegation
- Need to consider how to design the API
- Prototypes available in [`prototype`](./prototype)
- [ ] Extensibility (API?)