mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 03:25:23 +00:00
chore(terminal): custom shell keybindings (#313)
This commit is contained in:
parent
eede078715
commit
5271b507a0
@ -195,3 +195,11 @@ toggle_preview = "ctrl-o"
|
|||||||
|
|
||||||
# gitrepos channel
|
# gitrepos channel
|
||||||
"nvim" = "git-repos"
|
"nvim" = "git-repos"
|
||||||
|
|
||||||
|
[shell_integration.keybindings]
|
||||||
|
# controls which key binding should trigger tv
|
||||||
|
# for shell autocomplete
|
||||||
|
"smart_autocomplete" = "ctrl-t"
|
||||||
|
# controls which keybinding should trigger tv
|
||||||
|
# for command history
|
||||||
|
"command_history" = "ctrl-r"
|
@ -15,7 +15,7 @@ use ui::UiConfig;
|
|||||||
|
|
||||||
mod keybindings;
|
mod keybindings;
|
||||||
mod previewers;
|
mod previewers;
|
||||||
mod shell_integration;
|
pub mod shell_integration;
|
||||||
mod themes;
|
mod themes;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
|
@ -1,7 +1,40 @@
|
|||||||
|
use crate::config::parse_key;
|
||||||
|
use crate::event::Key;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default)]
|
||||||
pub struct ShellIntegrationConfig {
|
pub struct ShellIntegrationConfig {
|
||||||
pub commands: FxHashMap<String, String>,
|
pub commands: FxHashMap<String, String>,
|
||||||
|
pub keybindings: FxHashMap<String, String>,
|
||||||
|
}
|
||||||
|
const SMART_AUTOCOMPLETE_CONFIGURATION_KEY: &str = "smart_autocomplete";
|
||||||
|
const COMMAND_HISTORY_CONFIGURATION_KEY: &str = "command_history";
|
||||||
|
const DEFAULT_SHELL_AUTOCOMPLETE_KEY: char = 'T';
|
||||||
|
const DEFAULT_COMMAND_HISTORY_KEY: char = 'R';
|
||||||
|
|
||||||
|
impl ShellIntegrationConfig {
|
||||||
|
// based on the keybindings configuration provided in the configuration file
|
||||||
|
// (if any), extract the character triggers shell autocomplete
|
||||||
|
pub fn get_shell_autocomplete_keybinding_character(&self) -> char {
|
||||||
|
match self.keybindings.get(SMART_AUTOCOMPLETE_CONFIGURATION_KEY) {
|
||||||
|
Some(s) => match parse_key(s) {
|
||||||
|
Ok(Key::Ctrl(c)) => c.to_uppercase().next().unwrap(),
|
||||||
|
_ => DEFAULT_SHELL_AUTOCOMPLETE_KEY,
|
||||||
|
},
|
||||||
|
None => DEFAULT_SHELL_AUTOCOMPLETE_KEY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// based on the keybindings configuration provided in the configuration file
|
||||||
|
// (if any), extract the character triggers command history management
|
||||||
|
// through tv
|
||||||
|
pub fn get_command_history_keybinding_character(&self) -> char {
|
||||||
|
match self.keybindings.get(COMMAND_HISTORY_CONFIGURATION_KEY) {
|
||||||
|
Some(s) => match parse_key(s) {
|
||||||
|
Ok(Key::Ctrl(c)) => c.to_uppercase().next().unwrap(),
|
||||||
|
_ => DEFAULT_COMMAND_HISTORY_KEY,
|
||||||
|
},
|
||||||
|
None => DEFAULT_COMMAND_HISTORY_KEY,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ use television::cli::{
|
|||||||
guess_channel_from_prompt, list_channels, Cli, ParsedCliChannel,
|
guess_channel_from_prompt, list_channels, Cli, ParsedCliChannel,
|
||||||
PostProcessedCli,
|
PostProcessedCli,
|
||||||
};
|
};
|
||||||
|
|
||||||
use television::config::Config;
|
use television::config::Config;
|
||||||
|
use television::utils::shell::render_autocomplete_script_template;
|
||||||
use television::utils::{
|
use television::utils::{
|
||||||
shell::{completion_script, Shell},
|
shell::{completion_script, Shell},
|
||||||
stdin::is_readable_stdin,
|
stdin::is_readable_stdin,
|
||||||
@ -38,7 +40,15 @@ async fn main() -> Result<()> {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
television::cli::Command::InitShell { shell } => {
|
television::cli::Command::InitShell { shell } => {
|
||||||
let script = completion_script(Shell::from(shell))?;
|
let target_shell = Shell::from(shell);
|
||||||
|
// the completion scripts for the various shells are templated
|
||||||
|
// so that it's possible to override the keybindings triggering
|
||||||
|
// shell autocomplete and command history in tv
|
||||||
|
let script = render_autocomplete_script_template(
|
||||||
|
target_shell,
|
||||||
|
completion_script(target_shell)?,
|
||||||
|
&config.shell_integration,
|
||||||
|
)?;
|
||||||
println!("{script}");
|
println!("{script}");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::config::shell_integration::ShellIntegrationConfig;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Shell {
|
pub enum Shell {
|
||||||
Bash,
|
Bash,
|
||||||
@ -13,6 +13,16 @@ const COMPLETION_ZSH: &str = include_str!("shell/completion.zsh");
|
|||||||
const COMPLETION_BASH: &str = include_str!("shell/completion.bash");
|
const COMPLETION_BASH: &str = include_str!("shell/completion.bash");
|
||||||
const COMPLETION_FISH: &str = include_str!("shell/completion.fish");
|
const COMPLETION_FISH: &str = include_str!("shell/completion.fish");
|
||||||
|
|
||||||
|
// create the appropriate key binding for each supported shell
|
||||||
|
pub fn ctrl_keybinding(shell: Shell, character: char) -> Result<String> {
|
||||||
|
match shell {
|
||||||
|
Shell::Bash => Ok(format!(r"\C-{character}")),
|
||||||
|
Shell::Zsh => Ok(format!(r"^{character}")),
|
||||||
|
Shell::Fish => Ok(format!(r"\c{character}")),
|
||||||
|
_ => anyhow::bail!("This shell is not yet supported: {:?}", shell),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn completion_script(shell: Shell) -> Result<&'static str> {
|
pub fn completion_script(shell: Shell) -> Result<&'static str> {
|
||||||
match shell {
|
match shell {
|
||||||
Shell::Bash => Ok(COMPLETION_BASH),
|
Shell::Bash => Ok(COMPLETION_BASH),
|
||||||
@ -21,3 +31,66 @@ pub fn completion_script(shell: Shell) -> Result<&'static str> {
|
|||||||
_ => anyhow::bail!("This shell is not yet supported: {:?}", shell),
|
_ => anyhow::bail!("This shell is not yet supported: {:?}", shell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render_autocomplete_script_template(
|
||||||
|
shell: Shell,
|
||||||
|
template: &str,
|
||||||
|
config: &ShellIntegrationConfig,
|
||||||
|
) -> Result<String> {
|
||||||
|
let script = template
|
||||||
|
.replace(
|
||||||
|
"{tv_smart_autocomplete_keybinding}",
|
||||||
|
&ctrl_keybinding(
|
||||||
|
shell,
|
||||||
|
config.get_shell_autocomplete_keybinding_character(),
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"{tv_shell_history_keybinding}",
|
||||||
|
&ctrl_keybinding(
|
||||||
|
shell,
|
||||||
|
config.get_command_history_keybinding_character(),
|
||||||
|
)?,
|
||||||
|
);
|
||||||
|
Ok(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bash_ctrl_keybinding() {
|
||||||
|
let character = 's';
|
||||||
|
let shell = Shell::Bash;
|
||||||
|
let result = ctrl_keybinding(shell, character);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), "\\C-s");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zsh_ctrl_keybinding() {
|
||||||
|
let character = 's';
|
||||||
|
let shell = Shell::Zsh;
|
||||||
|
let result = ctrl_keybinding(shell, character);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), "^s");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fish_ctrl_keybinding() {
|
||||||
|
let character = 's';
|
||||||
|
let shell = Shell::Fish;
|
||||||
|
let result = ctrl_keybinding(shell, character);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), "\\cs");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_powershell_ctrl_keybinding() {
|
||||||
|
let character = 's';
|
||||||
|
let shell = Shell::PowerShell;
|
||||||
|
let result = ctrl_keybinding(shell, character);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,5 +23,5 @@ function tv_shell_history() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
bind -x '"\C-t": tv_smart_autocomplete'
|
bind -x '"{tv_smart_autocomplete_keybinding}": tv_smart_autocomplete'
|
||||||
bind -x '"\C-r": tv_shell_history'
|
bind -x '"{tv_shell_history_keybinding}": tv_shell_history'
|
||||||
|
@ -22,5 +22,5 @@ function tv_shell_history
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
bind \ct tv_smart_autocomplete
|
bind {tv_smart_autocomplete_keybinding} tv_smart_autocomplete
|
||||||
bind \cr tv_shell_history
|
bind {tv_shell_history_keybinding} tv_shell_history
|
||||||
|
@ -51,6 +51,6 @@ zle -N tv-smart-autocomplete _tv_smart_autocomplete
|
|||||||
zle -N tv-shell-history _tv_shell_history
|
zle -N tv-shell-history _tv_shell_history
|
||||||
|
|
||||||
|
|
||||||
bindkey '^T' tv-smart-autocomplete
|
bindkey '{tv_smart_autocomplete_keybinding}' tv-smart-autocomplete
|
||||||
bindkey '^R' tv-shell-history
|
bindkey '{tv_shell_history_keybinding}' tv-shell-history
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user