refactor(passthrough)!: drop support for unused passthrough keybindings

BREAKING CHANGE: the CLI `passthrough_keybindings` are no longer
available. This feature wasn't used anywhere to my knowledge (github
search) and was adding unnecessary complexity to the code.
This commit is contained in:
Alex Pasmantier 2025-04-06 17:57:23 +02:00
parent 8b267bb1ff
commit 911cc248ac
6 changed files with 19 additions and 87 deletions

View File

@ -51,10 +51,6 @@ pub enum Action {
#[serde(alias = "select_entry")] #[serde(alias = "select_entry")]
#[serde(alias = "confirm_selection")] #[serde(alias = "confirm_selection")]
ConfirmSelection, ConfirmSelection,
/// Select the entry currently under the cursor and pass the key that was pressed
/// through to be handled the parent process.
#[serde(alias = "select_passthrough")]
SelectPassthrough(String),
/// Select the entry currently under the cursor and exit the application. /// Select the entry currently under the cursor and exit the application.
#[serde(alias = "select_and_exit")] #[serde(alias = "select_and_exit")]
SelectAndExit, SelectAndExit,

View File

@ -4,11 +4,9 @@ use anyhow::Result;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, trace}; use tracing::{debug, trace};
use crate::channels::entry::Entry; use crate::channels::entry::{Entry, PreviewType};
use crate::channels::TelevisionChannel; use crate::channels::TelevisionChannel;
use crate::config::{ use crate::config::Config;
merge_keybindings, parse_key, Binding, Config, KeyBindings,
};
use crate::keymap::Keymap; use crate::keymap::Keymap;
use crate::render::UiState; use crate::render::UiState;
use crate::television::{Mode, Television}; use crate::television::{Mode, Television};
@ -62,7 +60,6 @@ pub struct App {
pub enum ActionOutcome { pub enum ActionOutcome {
Entries(FxHashSet<Entry>), Entries(FxHashSet<Entry>),
Input(String), Input(String),
Passthrough(FxHashSet<Entry>, String),
None, None,
} }
@ -70,7 +67,6 @@ pub enum ActionOutcome {
#[derive(Debug)] #[derive(Debug)]
pub struct AppOutput { pub struct AppOutput {
pub selected_entries: Option<FxHashSet<Entry>>, pub selected_entries: Option<FxHashSet<Entry>>,
pub passthrough: Option<String>,
} }
impl From<ActionOutcome> for AppOutput { impl From<ActionOutcome> for AppOutput {
@ -78,19 +74,15 @@ impl From<ActionOutcome> for AppOutput {
match outcome { match outcome {
ActionOutcome::Entries(entries) => Self { ActionOutcome::Entries(entries) => Self {
selected_entries: Some(entries), selected_entries: Some(entries),
passthrough: None,
}, },
ActionOutcome::Input(input) => Self { ActionOutcome::Input(input) => Self {
selected_entries: None, selected_entries: Some(FxHashSet::from_iter([Entry::new(
passthrough: Some(input), input,
}, PreviewType::None,
ActionOutcome::Passthrough(entries, key) => Self { )])),
selected_entries: Some(entries),
passthrough: Some(key),
}, },
ActionOutcome::None => Self { ActionOutcome::None => Self {
selected_entries: None, selected_entries: None,
passthrough: None,
}, },
} }
} }
@ -103,7 +95,6 @@ impl App {
pub fn new( pub fn new(
channel: TelevisionChannel, channel: TelevisionChannel,
config: Config, config: Config,
passthrough_keybindings: &[String],
input: Option<String>, input: Option<String>,
) -> Self { ) -> Self {
let (action_tx, action_rx) = mpsc::unbounded_channel(); let (action_tx, action_rx) = mpsc::unbounded_channel();
@ -111,21 +102,7 @@ impl App {
let (_, event_rx) = mpsc::unbounded_channel(); let (_, event_rx) = mpsc::unbounded_channel();
let (event_abort_tx, _) = mpsc::unbounded_channel(); let (event_abort_tx, _) = mpsc::unbounded_channel();
let tick_rate = config.application.tick_rate; let tick_rate = config.application.tick_rate;
let keybindings = merge_keybindings(config.keybindings.clone(), { let keymap = Keymap::from(&config.keybindings);
&KeyBindings::from(passthrough_keybindings.iter().filter_map(
|s| match parse_key(s) {
Ok(key) => Some((
Action::SelectPassthrough(s.to_string()),
Binding::SingleKey(key),
)),
Err(e) => {
debug!("Failed to parse keybinding: {}", e);
None
}
},
))
});
let keymap = Keymap::from(&keybindings);
debug!("{:?}", keymap); debug!("{:?}", keymap);
let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel(); let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel();
@ -346,20 +323,6 @@ impl App {
self.television.current_pattern.clone(), self.television.current_pattern.clone(),
)); ));
} }
Action::SelectPassthrough(passthrough) => {
self.should_quit = true;
self.render_tx.send(RenderingTask::Quit)?;
if let Some(entries) = self
.television
.get_selected_entries(Some(Mode::Channel))
{
return Ok(ActionOutcome::Passthrough(
entries,
passthrough,
));
}
return Ok(ActionOutcome::None);
}
Action::ClearScreen => { Action::ClearScreen => {
self.render_tx.send(RenderingTask::ClearScreen)?; self.render_tx.send(RenderingTask::ClearScreen)?;
} }

View File

@ -66,14 +66,6 @@ pub struct Cli {
#[arg(short, long, value_name = "STRING", verbatim_doc_comment)] #[arg(short, long, value_name = "STRING", verbatim_doc_comment)]
pub keybindings: Option<String>, pub keybindings: Option<String>,
/// Passthrough keybindings (comma separated, e.g. "q,ctrl-w,ctrl-t")
///
/// These keybindings will trigger selection of the current entry and be
/// passed through to stdout along with the entry to be handled by the
/// parent process.
#[arg(long, value_name = "STRING", verbatim_doc_comment)]
pub passthrough_keybindings: Option<String>,
/// Input text to pass to the channel to prefill the prompt. /// Input text to pass to the channel to prefill the prompt.
/// ///
/// This can be used to provide a default value for the prompt upon /// This can be used to provide a default value for the prompt upon

View File

@ -24,7 +24,6 @@ pub struct PostProcessedCli {
pub no_preview: bool, pub no_preview: bool,
pub tick_rate: Option<f64>, pub tick_rate: Option<f64>,
pub frame_rate: Option<f64>, pub frame_rate: Option<f64>,
pub passthrough_keybindings: Vec<String>,
pub input: Option<String>, pub input: Option<String>,
pub command: Option<Command>, pub command: Option<Command>,
pub working_directory: Option<String>, pub working_directory: Option<String>,
@ -40,7 +39,6 @@ impl Default for PostProcessedCli {
no_preview: false, no_preview: false,
tick_rate: None, tick_rate: None,
frame_rate: None, frame_rate: None,
passthrough_keybindings: Vec::new(),
input: None, input: None,
command: None, command: None,
working_directory: None, working_directory: None,
@ -52,21 +50,16 @@ impl Default for PostProcessedCli {
impl From<Cli> for PostProcessedCli { impl From<Cli> for PostProcessedCli {
fn from(cli: Cli) -> Self { fn from(cli: Cli) -> Self {
// parse literal keybindings passed through the CLI
let keybindings: Option<KeyBindings> = cli.keybindings.map(|kb| { let keybindings: Option<KeyBindings> = cli.keybindings.map(|kb| {
parse_keybindings(&kb) parse_keybindings_literal(&kb, CLI_KEYBINDINGS_DELIMITER)
.map_err(|e| { .map_err(|e| {
cli_parsing_error_exit(&e.to_string()); cli_parsing_error_exit(&e.to_string());
}) })
.unwrap() .unwrap()
}); });
let passthrough_keybindings = cli // parse the preview command if provided
.passthrough_keybindings
.unwrap_or_default()
.split(',')
.map(std::string::ToString::to_string)
.collect();
let preview_kind = cli let preview_kind = cli
.preview .preview
.map(|preview| PreviewCommand { .map(|preview| PreviewCommand {
@ -110,7 +103,6 @@ impl From<Cli> for PostProcessedCli {
no_preview: cli.no_preview, no_preview: cli.no_preview,
tick_rate: cli.tick_rate, tick_rate: cli.tick_rate,
frame_rate: cli.frame_rate, frame_rate: cli.frame_rate,
passthrough_keybindings,
input: cli.input, input: cli.input,
command: cli.command, command: cli.command,
working_directory, working_directory,
@ -147,7 +139,7 @@ impl ParsedCliChannel {
const CLI_KEYBINDINGS_DELIMITER: char = ';'; const CLI_KEYBINDINGS_DELIMITER: char = ';';
/// Parse the keybindings string into a hashmap of key -> action. /// Parse a keybindings literal into a `KeyBindings` struct.
/// ///
/// The formalism used is the same as the one used in the configuration file: /// The formalism used is the same as the one used in the configuration file:
/// ```ignore /// ```ignore
@ -155,9 +147,12 @@ const CLI_KEYBINDINGS_DELIMITER: char = ';';
/// ``` /// ```
/// Parsing it globally consists of splitting by the delimiter, reconstructing toml key-value pairs /// Parsing it globally consists of splitting by the delimiter, reconstructing toml key-value pairs
/// and parsing that using logic already implemented in the configuration module. /// and parsing that using logic already implemented in the configuration module.
fn parse_keybindings(cli_keybindings: &str) -> Result<KeyBindings> { fn parse_keybindings_literal(
cli_keybindings: &str,
delimiter: char,
) -> Result<KeyBindings> {
let toml_definition = cli_keybindings let toml_definition = cli_keybindings
.split(CLI_KEYBINDINGS_DELIMITER) .split(delimiter)
.fold(String::new(), |acc, kb| acc + kb + "\n"); .fold(String::new(), |acc, kb| acc + kb + "\n");
toml::from_str(&toml_definition).map_err(|e| anyhow!(e)) toml::from_str(&toml_definition).map_err(|e| anyhow!(e))
@ -323,7 +318,6 @@ mod tests {
tick_rate: Some(50.0), tick_rate: Some(50.0),
frame_rate: Some(60.0), frame_rate: Some(60.0),
keybindings: None, keybindings: None,
passthrough_keybindings: Some("q,ctrl-w,ctrl-t".to_string()),
input: None, input: None,
command: None, command: None,
working_directory: Some("/home/user".to_string()), working_directory: Some("/home/user".to_string()),
@ -345,10 +339,6 @@ mod tests {
); );
assert_eq!(post_processed_cli.tick_rate, Some(50.0)); assert_eq!(post_processed_cli.tick_rate, Some(50.0));
assert_eq!(post_processed_cli.frame_rate, Some(60.0)); assert_eq!(post_processed_cli.frame_rate, Some(60.0));
assert_eq!(
post_processed_cli.passthrough_keybindings,
vec!["q".to_string(), "ctrl-w".to_string(), "ctrl-t".to_string()]
);
assert_eq!( assert_eq!(
post_processed_cli.working_directory, post_processed_cli.working_directory,
Some("/home/user".to_string()) Some("/home/user".to_string())
@ -366,7 +356,6 @@ mod tests {
tick_rate: Some(50.0), tick_rate: Some(50.0),
frame_rate: Some(60.0), frame_rate: Some(60.0),
keybindings: None, keybindings: None,
passthrough_keybindings: None,
input: None, input: None,
command: None, command: None,
working_directory: None, working_directory: None,
@ -396,7 +385,6 @@ mod tests {
tick_rate: Some(50.0), tick_rate: Some(50.0),
frame_rate: Some(60.0), frame_rate: Some(60.0),
keybindings: None, keybindings: None,
passthrough_keybindings: None,
input: None, input: None,
command: None, command: None,
working_directory: None, working_directory: None,
@ -421,7 +409,6 @@ mod tests {
tick_rate: Some(50.0), tick_rate: Some(50.0),
frame_rate: Some(60.0), frame_rate: Some(60.0),
keybindings: None, keybindings: None,
passthrough_keybindings: None,
input: None, input: None,
command: None, command: None,
working_directory: None, working_directory: None,
@ -449,7 +436,6 @@ mod tests {
"quit=\"esc\";select_next_entry=[\"down\",\"ctrl-j\"]" "quit=\"esc\";select_next_entry=[\"down\",\"ctrl-j\"]"
.to_string(), .to_string(),
), ),
passthrough_keybindings: None,
input: None, input: None,
command: None, command: None,
working_directory: None, working_directory: None,

View File

@ -65,16 +65,12 @@ async fn main() -> Result<()> {
CLIPBOARD.with(<_>::default); CLIPBOARD.with(<_>::default);
debug!("Creating application..."); debug!("Creating application...");
let mut app = let mut app = App::new(channel, config, args.input);
App::new(channel, config, &args.passthrough_keybindings, args.input);
stdout().flush()?; stdout().flush()?;
let output = app.run(stdout().is_terminal(), false).await?; let output = app.run(stdout().is_terminal(), false).await?;
info!("{:?}", output); info!("App output: {:?}", output);
let stdout_handle = stdout().lock(); let stdout_handle = stdout().lock();
let mut bufwriter = BufWriter::new(stdout_handle); let mut bufwriter = BufWriter::new(stdout_handle);
if let Some(passthrough) = output.passthrough {
writeln!(bufwriter, "{passthrough}")?;
}
if let Some(entries) = output.selected_entries { if let Some(entries) = output.selected_entries {
for entry in &entries { for entry in &entries {
writeln!(bufwriter, "{}", entry.stdout_repr())?; writeln!(bufwriter, "{}", entry.stdout_repr())?;

View File

@ -30,10 +30,9 @@ fn setup_app() -> (
television::channels::files::Channel::new(vec![target_dir]), television::channels::files::Channel::new(vec![target_dir]),
); );
let config = default_config_from_file().unwrap(); let config = default_config_from_file().unwrap();
let passthrough_keybindings = Vec::new();
let input = None; let input = None;
let mut app = App::new(channel, config, &passthrough_keybindings, input); let mut app = App::new(channel, config, input);
// retrieve the app's action channel handle in order to send a quit action // retrieve the app's action channel handle in order to send a quit action
let tx = app.action_tx.clone(); let tx = app.action_tx.clone();