mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-28 13:51:41 +00:00
feat(zsh): add tv cli options/arguments/subcommands autocompletion (#665)
## 📺 PR Description update `init` shell generation to include autocompleting options for `tv` itself. <img width="817" height="497" alt="image" src="https://github.com/user-attachments/assets/e65bc0aa-410e-43ef-86a5-2d6e01c6c1df" /> <img width="837" height="186" alt="image" src="https://github.com/user-attachments/assets/6d9665f6-0664-4e6e-bce3-e6a970c79524" /> I've only tested this on macOS (arm64) with `zsh` and `bash` so far: <img width="893" height="349" alt="image" src="https://github.com/user-attachments/assets/dd66ecec-fee3-4713-a68c-f69e961a4ba6" /> ## Checklist <!-- a quick pass through the following items to make sure you haven't forgotten anything --> - [x] my commits **and PR title** follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) format - [x] if this is a new feature, I have added tests to consolidate the feature and prevent regressions - [ ] if this is a bug fix, I have added a test that reproduces the bug (if applicable) - [ ] I have added a reasonable amount of documentation to the code where appropriate --------- Co-authored-by: Alex Pasmantier <47638216+alexpasmantier@users.noreply.github.com>
This commit is contained in:
parent
c93ddeeadb
commit
83f29f7418
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -305,6 +305,15 @@ dependencies = [
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.40"
|
||||
@ -1989,6 +1998,7 @@ dependencies = [
|
||||
"base64",
|
||||
"better-panic",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"clipboard-win",
|
||||
"colored",
|
||||
|
@ -51,6 +51,7 @@ serde_json = "1.0.140"
|
||||
colored = "3.0.0"
|
||||
serde_with = "3.13.0"
|
||||
which = "8.0.0"
|
||||
clap_complete = "4.5.55"
|
||||
|
||||
|
||||
# target specific dependencies
|
||||
|
@ -1,10 +1,11 @@
|
||||
use crate::{
|
||||
cli::args::Shell as CliShell,
|
||||
cli::args::{Cli, Shell as CliShell},
|
||||
config::shell_integration::ShellIntegrationConfig,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use clap::CommandFactory;
|
||||
use std::fmt::Display;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Shell {
|
||||
@ -28,6 +29,31 @@ impl Default for Shell {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ShellError {
|
||||
UnsupportedShell(String),
|
||||
}
|
||||
|
||||
impl TryFrom<Shell> for clap_complete::Shell {
|
||||
type Error = ShellError;
|
||||
|
||||
fn try_from(value: Shell) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
Shell::Bash => Ok(clap_complete::Shell::Bash),
|
||||
Shell::Zsh => Ok(clap_complete::Shell::Zsh),
|
||||
Shell::Fish => Ok(clap_complete::Shell::Fish),
|
||||
Shell::PowerShell => Ok(clap_complete::Shell::PowerShell),
|
||||
Shell::Cmd => Err(ShellError::UnsupportedShell(
|
||||
"Cmd shell is not supported for completion scripts"
|
||||
.to_string(),
|
||||
)),
|
||||
Shell::Nu => Err(ShellError::UnsupportedShell(
|
||||
"Nu shell is not supported for completion scripts".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Shell {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@ -143,6 +169,7 @@ pub fn render_autocomplete_script_template(
|
||||
template: &str,
|
||||
config: &ShellIntegrationConfig,
|
||||
) -> Result<String> {
|
||||
// Custom autocomplete
|
||||
let script = template
|
||||
.replace(
|
||||
"{tv_smart_autocomplete_keybinding}",
|
||||
@ -158,7 +185,33 @@ pub fn render_autocomplete_script_template(
|
||||
config.get_command_history_keybinding_character(),
|
||||
)?,
|
||||
);
|
||||
Ok(script)
|
||||
|
||||
let clap_autocomplete =
|
||||
render_clap_autocomplete(shell).unwrap_or_default();
|
||||
|
||||
Ok(script + &clap_autocomplete)
|
||||
}
|
||||
|
||||
fn render_clap_autocomplete(shell: Shell) -> Option<String> {
|
||||
// Clap autocomplete
|
||||
let mut clap_autocomplete = vec![];
|
||||
let mut cmd = Cli::command();
|
||||
let clap_shell: clap_complete::Shell = match shell.try_into() {
|
||||
Ok(shell) => shell,
|
||||
Err(err) => {
|
||||
warn!("Failed to convert shell {:?}: {:?}", shell, err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
clap_complete::aot::generate(
|
||||
clap_shell,
|
||||
&mut cmd,
|
||||
"tv", // the command defines the name as "television"
|
||||
&mut clap_autocomplete,
|
||||
);
|
||||
|
||||
String::from_utf8(clap_autocomplete).ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -208,4 +261,30 @@ mod tests {
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), "Ctrl-s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zsh_clap_completion() {
|
||||
let shell = Shell::Zsh;
|
||||
let result = render_autocomplete_script_template(
|
||||
shell,
|
||||
"",
|
||||
&ShellIntegrationConfig::default(),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert!(result.contains("compdef _tv tv"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unsupported_clap_completion() {
|
||||
let shell = Shell::Nu;
|
||||
let result = render_autocomplete_script_template(
|
||||
shell,
|
||||
"",
|
||||
&ShellIntegrationConfig::default(),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user