refactor(shell): improve shell integration configuration syntax

This commit is contained in:
Alexandre Pasmantier 2025-02-02 12:24:30 +01:00
parent c74b47d07c
commit d3725f2cc3
5 changed files with 78 additions and 47 deletions

View File

@ -149,52 +149,30 @@ toggle_preview = "ctrl-o"
# E.g. typing `git checkout <CTRL-T>` will open television with a list of
# branches to choose from.
[shell_integration.commands]
# Add your commands here. Each key is a command that will trigger tv with the
# corresponding channel as value.
# Example: say you want the following prompts to trigger the following channels
[shell_integration.channel_triggers]
# Add your channel triggers here. Each key is a channel that will be triggered
# by the corresponding commands.
# Example: say you want the following commands to trigger the following channels
# when pressing <CTRL-T>:
# `git checkout` should trigger the `git-branches` channel
# `ls` should trigger the `dirs` channel
# `cat` should trigger the `files` channel
# `git checkout` should trigger the `git-branches` channel
# `ls` should trigger the `dirs` channel
# `cat` and `cp` should trigger the `files` channel
#
# You would add the following to your configuration file:
# ```
# [shell_integration.commands]
# "git checkout" = "git-branch"
# "ls" = "dirs"
# "cat" = "files"
# [shell_integration.channel_triggers]
# "git-branches" = ["git checkout"]
# "dirs" = ["ls"]
# "files" = ["cat", "cp"]
# ```
"env" = ["export", "unset"]
"dirs" = ["cd", "ls", "rmdir"]
"files" = ["cat", "less", "head", "tail", "vim", "bat"]
"git-diff" = ["git add"]
"git-branch" = ["git checkout", "git branch -d"]
"docker-images" = ["docker run"]
"git-repos" = ["nvim"]
# environment variables
"export" = "env"
"unset" = "env"
# dirs channel
"cd" = "dirs"
"ls" = "dirs"
"rmdir" = "dirs"
# files channel
"cat" = "files"
"less" = "files"
"head" = "files"
"tail" = "files"
"vim" = "files"
"bat" = "files"
# git-diff channel
"git add" = "git-diff"
# git-branch channel
"git checkout" = "git-branch"
"git branch -d" = "git-branch"
# docker-images channel
"docker run" = "docker-images"
# gitrepos channel
"nvim" = "git-repos"
[shell_integration.keybindings]
# controls which key binding should trigger tv
@ -203,4 +181,3 @@ toggle_preview = "ctrl-o"
# controls which keybinding should trigger tv
# for command history
"command_history" = "ctrl-r"

View File

@ -89,7 +89,7 @@ impl Config {
#[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
pub fn new() -> Result<Self> {
// Load the default_config values as base defaults
let default_config: Config = toml::from_str(DEFAULT_CONFIG)
let mut default_config: Config = toml::from_str(DEFAULT_CONFIG)
.expect("Error parsing default config");
// initialize the config builder
@ -107,22 +107,35 @@ impl Config {
let path = config_dir.join(CONFIG_FILE_NAME);
let contents = std::fs::read_to_string(&path)?;
let cfg: Config = toml::from_str(&contents).unwrap_or_else(|e| {
let mut user_cfg: Config = toml::from_str(&contents).unwrap_or_else(|e| {
warn!(
"Error parsing config file, using default configuration: {}" , e
);
default_config.clone()
});
// merge shell integration triggers with commands
default_config.shell_integration.merge_triggers();
user_cfg.shell_integration.merge_triggers();
// merge shell integration commands with default commands
let mut merged_commands =
default_config.shell_integration.commands.clone();
merged_commands
.extend(user_cfg.shell_integration.commands.clone());
user_cfg.shell_integration.commands = merged_commands;
// merge keybindings with default keybindings
let keybindings = merge_keybindings(
default_config.keybindings,
&cfg.keybindings,
&user_cfg.keybindings,
);
let cfg = Config { keybindings, ..cfg };
let final_cfg = Config {
keybindings,
..user_cfg
};
debug!("Config: {:?}", cfg);
Ok(cfg)
debug!("Config: {:?}", final_cfg);
Ok(final_cfg)
} else {
warn!("No config file found at {:?}, creating default configuration file at that location.", config_dir);
// create the default configuration file in the user's config directory

View File

@ -2,13 +2,19 @@ use std::hash::Hash;
use crate::config::parse_key;
use crate::event::Key;
use crate::utils::hashmaps;
use rustc_hash::FxHashMap;
use serde::Deserialize;
#[derive(Clone, Debug, Deserialize, Default, PartialEq)]
#[serde(default)]
pub struct ShellIntegrationConfig {
/// DEPRECATED: This is a legacy configuration option that is no longer used.
/// It is kept here for backwards compatibility.
/// {command: channel}
pub commands: FxHashMap<String, String>,
/// {channel: [commands]}
pub channel_triggers: FxHashMap<String, Vec<String>>,
pub keybindings: FxHashMap<String, String>,
}
@ -19,6 +25,21 @@ impl Hash for ShellIntegrationConfig {
}
}
impl ShellIntegrationConfig {
/// Merge the channel triggers into the commands hashmap
/// This is done to maintain backwards compatibility with the old configuration
/// format.
///
/// {command: channel} + {channel: [commands]} => {command: channel}
pub fn merge_triggers(&mut self) {
// invert the hashmap to get {command: channel}
let inverted_triggers =
hashmaps::invert_hashmap(&self.channel_triggers);
// merge the inverted hashmap with the existing commands hashmap
self.commands.extend(inverted_triggers);
}
}
const SMART_AUTOCOMPLETE_CONFIGURATION_KEY: &str = "smart_autocomplete";
const COMMAND_HISTORY_CONFIGURATION_KEY: &str = "command_history";
const DEFAULT_SHELL_AUTOCOMPLETE_KEY: char = 'T';

View File

@ -0,0 +1,19 @@
use std::collections::HashMap;
use std::hash::Hash;
pub fn invert_hashmap<K, V, I, S: ::std::hash::BuildHasher>(
hashmap: &HashMap<K, V, S>,
) -> HashMap<I, K>
where
K: Eq + Hash + Clone,
V: Eq + Hash + Clone + IntoIterator<Item = I>,
I: Eq + Hash + Clone,
{
let mut inverted = HashMap::new();
for (key, values) in hashmap {
for value in values.clone() {
inverted.insert(value, key.clone());
}
}
inverted
}

View File

@ -1,6 +1,7 @@
pub mod cache;
pub mod command;
pub mod files;
pub mod hashmaps;
pub mod indices;
pub mod input;
pub mod metadata;