diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-02-24 13:10:40 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-24 13:10:40 +0100 |
| commit | 8cd6286c3ffc10d14fdafd5005fa89b0f7fdcd44 (patch) | |
| tree | c6341f5e3e251ae17db0c3427ddaf1e54d4fa91c | |
| parent | Allow closing replaced panes when opening panes in place (#4760) (diff) | |
| download | sl0ppij-8cd6286c3ffc10d14fdafd5005fa89b0f7fdcd44.tar.gz sl0ppij-8cd6286c3ffc10d14fdafd5005fa89b0f7fdcd44.tar.xz sl0ppij-8cd6286c3ffc10d14fdafd5005fa89b0f7fdcd44.zip | |
remove sequence plugin (#4761)
39 files changed, 1 insertions, 3092 deletions
@@ -3326,20 +3326,6 @@ dependencies = [ ] [[package]] -name = "sequence" -version = "0.1.0" -dependencies = [ - "fuzzy-matcher", - "regex", - "serde", - "serde_json", - "strum", - "unicode-width 0.1.10", - "uuid", - "zellij-tile", -] - -[[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -52,7 +52,6 @@ members = [ "default-plugins/about", "default-plugins/share", "default-plugins/multiple-select", - "default-plugins/sequence", "default-plugins/layout-manager", "zellij-client", "zellij-server", diff --git a/default-plugins/configuration/src/presets.rs b/default-plugins/configuration/src/presets.rs index 03d05171..4eb65042 100644 --- a/default-plugins/configuration/src/presets.rs +++ b/default-plugins/configuration/src/presets.rs @@ -155,12 +155,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Locked" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Locked" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true @@ -384,12 +378,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Normal" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true @@ -613,12 +601,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Normal" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true @@ -823,12 +805,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Normal" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true @@ -1033,12 +1009,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Normal" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true @@ -1238,12 +1208,6 @@ keybinds clear-defaults=true {{ }}; SwitchToMode "Normal" }} - bind "q" {{ - LaunchPlugin "zellij:sequence" {{ - floating true - }}; - SwitchToMode "Normal" - }} bind "l" {{ LaunchOrFocusPlugin "zellij:layout-manager" {{ floating true diff --git a/default-plugins/sequence/.cargo/config.toml b/default-plugins/sequence/.cargo/config.toml deleted file mode 100644 index 6b509f5b..00000000 --- a/default-plugins/sequence/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasip1" diff --git a/default-plugins/sequence/.gitignore b/default-plugins/sequence/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/default-plugins/sequence/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/default-plugins/sequence/Cargo.toml b/default-plugins/sequence/Cargo.toml deleted file mode 100644 index 4498d8d0..00000000 --- a/default-plugins/sequence/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "sequence" -version = "0.1.0" -authors = ["Aram Drevekenin <aram@poor.dev>"] -edition.workspace = true -license.workspace = true - -[dependencies] -zellij-tile = { path = "../../zellij-tile" } -unicode-width = "0.1" -fuzzy-matcher = "0.3" -# TODO: get this from Zellij so that we don't have to match versions -strum = "0.27" -regex = "1" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -uuid = { version = "1.0", features = ["v4"] } diff --git a/default-plugins/sequence/LICENSE.md b/default-plugins/sequence/LICENSE.md deleted file mode 120000 index f0608a63..00000000 --- a/default-plugins/sequence/LICENSE.md +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE.md
\ No newline at end of file diff --git a/default-plugins/sequence/src/lib.rs b/default-plugins/sequence/src/lib.rs deleted file mode 100644 index 1e124979..00000000 --- a/default-plugins/sequence/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod path_formatting; -pub mod state; -pub mod ui; - -pub use state::State; diff --git a/default-plugins/sequence/src/main.rs b/default-plugins/sequence/src/main.rs deleted file mode 100644 index 9c99ac5d..00000000 --- a/default-plugins/sequence/src/main.rs +++ /dev/null @@ -1,798 +0,0 @@ -mod path_formatting; -mod state; -mod ui; - -use crate::state::CommandStatus; -use crate::ui::components; -use crate::ui::layout_calculations::calculate_viewport; -use state::{parse_commands, ChainType, CommandEntry, SequenceMode, State}; -use zellij_tile::prelude::*; - -use std::collections::BTreeMap; -use std::path::PathBuf; - -register_plugin!(State); - -impl ZellijPlugin for State { - fn load(&mut self, _configuration: BTreeMap<String, String>) { - subscribe(&[ - EventType::ModeUpdate, - EventType::Key, - EventType::PermissionRequestResult, - EventType::HostFolderChanged, - EventType::CommandPaneExited, - EventType::CommandPaneOpened, - EventType::EditPaneExited, - EventType::Timer, - EventType::PastedText, - EventType::TabUpdate, - EventType::BeforeClose, - ]); - - // Store our own plugin ID and client ID - let plugin_ids = get_plugin_ids(); - self.plugin_id = Some(plugin_ids.plugin_id); - self.client_id = Some(plugin_ids.client_id); - self.cwd = Some(plugin_ids.initial_cwd); - rename_plugin_pane(plugin_ids.plugin_id, "Sequence"); - update_title(self); - } - - fn update(&mut self, event: Event) -> bool { - handle_event(self, event) - } - - #[cfg(target_family = "wasm")] - fn pipe(&mut self, pipe_message: PipeMessage) -> bool { - if !pipe_message.is_private { - return false; - } - - let Some(payload) = pipe_message.payload else { - return false; - }; - - let payload = payload.trim().to_string(); - if payload.is_empty() { - return false; - } - - // If already running, emit error - if self.execution.is_running { - if let PipeSource::Cli(pipe_id) = &pipe_message.source { - #[cfg(target_family = "wasm")] - cli_pipe_output(pipe_id, "error: sequence already running\n"); - } - return false; - } - - self.load_commands(&payload, None); - - // Handle blocking: keep CLI alive until sequence finishes - if pipe_message - .args - .get("blocking") - .map(|v| v == "true") - .unwrap_or(false) - { - if let PipeSource::Cli(pipe_id) = &pipe_message.source { - block_cli_pipe_input(pipe_id); - self.blocking_pipe_id = Some(pipe_id.clone()); - } - } - - show_self(true); - - true - } - - fn render(&mut self, rows: usize, cols: usize) { - // Store dimensions for use in calculations - self.own_rows = Some(rows); - self.own_columns = Some(cols); - - // Main screen: plugin just opened, no commands entered yet - if self.all_commands_are_pending() && !self.execution.can_run_sequence() { - for (text, x, y) in components::render_intro_hint(rows, cols) { - print_text_with_coordinates(text, x, y, None, None); - } - return; - } - - let is_staging = self.all_commands_are_pending() && self.execution.can_run_sequence(); - - // Reserve: 1 header above + 1 padding-to-controls + 1 controls + 1 below/ribbon - let max_visible_rows = rows.saturating_sub(4); - - let (offset, visible_count, hidden_above, hidden_below) = calculate_viewport( - self.execution.all_commands.len(), - max_visible_rows, - self.selection.current_selected_command_index, - self.execution.current_running_command_index, - ); - - // For the staging screen, center the content both horizontally and vertically. - // Content height: 1 header row + visible_count command rows + 1 gap + 1 help row = visible_count + 3 - let (base_x, base_y, table_max_width) = if is_staging { - let longest_cwd = self.execution.longest_cwd_display(&self.cwd); - let longest_cmd = self - .execution - .all_commands - .iter() - .map(|c| c.get_text().chars().count()) - .max() - .unwrap_or(1); - let (max_chain_w, max_status_w) = components::calculate_max_widths( - &self.execution.all_commands, - self.layout.spinner_frame, - ); - let content_width = components::calculate_longest_line( - &longest_cwd, - longest_cmd, - max_chain_w, - max_status_w, - ) - .min(cols); - // header + commands + gap + help + ribbon/below = visible_count + 4 - let content_height = visible_count + 4; - let bx = cols.saturating_sub(content_width) / 2; - let by = rows.saturating_sub(content_height) / 2; - (bx, by, Some(content_width)) - } else { - (1, 0, self.own_columns.map(|o| o.saturating_sub(1))) - }; - - let mut table = components::build_table_header(hidden_above > 0 || hidden_below > 0); - - for index in offset..offset + visible_count { - table = components::add_command_row( - table, - self, - index, - offset, - visible_count, - hidden_above, - hidden_below, - ); - } - - print_table_with_coordinates(table, base_x, base_y, table_max_width, None); - - let help_y = base_y + visible_count + 2; - let (first_help, first_help_len, second_help) = - components::render_help_lines(self, Some(cols)); - let help_x = if is_staging { - cols.saturating_sub(first_help_len) / 2 - } else { - base_x - }; - print_text_with_coordinates(first_help, help_x, help_y, None, None); - - if let Some((second_help_text, second_help_len)) = second_help { - let second_help_x = if is_staging { - cols.saturating_sub(second_help_len) / 2 - } else { - base_x - }; - print_text_with_coordinates(second_help_text, second_help_x, help_y + 1, None, None); - } - - // Mode ribbon: "<Tab> [Single Pane] [Spread]" — only while running - if self.execution.is_running { - let ribbon_y = help_y + 1; - print_text_with_coordinates( - Text::new("<Tab>").color_all(3), - base_x, - ribbon_y, - None, - None, - ); - let ribbon_x = base_x + 6; // "<Tab>" (5) + space (1) - let (single_ribbon, spread_ribbon) = if self.mode == SequenceMode::SinglePane { - (Text::new("Single Pane").selected(), Text::new("Spread")) - } else { - (Text::new("Single Pane"), Text::new("Spread").selected()) - }; - print_ribbon_with_coordinates(single_ribbon, ribbon_x, ribbon_y, Some(15), None); - print_ribbon_with_coordinates(spread_ribbon, ribbon_x + 15, ribbon_y, Some(10), None); - } - } -} - -pub fn handle_event(state: &mut State, event: Event) -> bool { - use std::path::PathBuf; - - match event { - Event::PermissionRequestResult(_) => { - change_host_folder(PathBuf::from("/")); - update_title(state); - true - }, - Event::ModeUpdate(mode_info) => { - state.shell = mode_info.shell.clone(); - false - }, - Event::Key(key) => { - let should_render = handle_key_event(state, key); - let repositioned = state.reposition_plugin(); - if repositioned { - // we only want to render once we have repositioned, we will do this in TabUpdate - return false; - } - show_cursor(None); - should_render - }, - Event::Timer(_elapsed) => { - let should_render = update_spinner(state); - update_title(state); - should_render - }, - Event::PastedText(pasted_text) => { - if state.execution.is_running { - return false; - } - let payload = pasted_text.trim().to_string(); - if payload.is_empty() { - return false; - } - state.load_commands(&payload, None); - let repositioned = state.reposition_plugin(); - !repositioned - }, - Event::TabUpdate(tab_infos) => { - if let Some(tab_info) = tab_infos.iter().find(|t| t.active) { - state.total_viewport_columns = Some(tab_info.viewport_columns); - state.total_viewport_rows = Some(tab_info.viewport_rows); - } - state.reposition_plugin() - }, - Event::CommandPaneExited(terminal_pane_id, exit_code, _context) => { - let should_render = handle_command_pane_exited(state, terminal_pane_id, exit_code); - let repositioned = state.reposition_plugin(); - update_title(state); - should_render || repositioned - }, - Event::CommandPaneOpened(_terminal_pane_id, _context) => { - // Schedule spinner on first pane open - if !state.layout.spinner_timer_scheduled { - set_timeout(0.1); - state.layout.spinner_timer_scheduled = true; - } - update_title(state); - true - }, - Event::EditPaneExited(terminal_pane_id, _exit_code, _context) => { - let should_render = handle_editor_pane_exited(state, terminal_pane_id); - let repositioned = state.reposition_plugin(); - should_render || repositioned - }, - Event::BeforeClose => { - if let Some(pipe_id) = state.blocking_pipe_id.take() { - unblock_cli_pipe_input(&pipe_id); - } - false - }, - _ => false, - } -} - -fn handle_command_pane_exited( - state: &mut State, - terminal_pane_id: u32, - exit_code: Option<i32>, -) -> bool { - let pane_id = PaneId::Terminal(terminal_pane_id); - - // Find which command this pane belongs to — stale events produce no match - let Some(cmd_index) = - state.execution.all_commands.iter().position( - |c| matches!(c.get_status(), CommandStatus::Running(Some(id)) if id == pane_id), - ) - else { - return false; - }; - - state.execution.all_commands[cmd_index] - .set_status(CommandStatus::Exited(exit_code, Some(pane_id))); - - if !state.execution.is_running { - return true; - } - - let chain_type = state.execution.all_commands[cmd_index].get_chain_type(); - let should_continue = match chain_type { - ChainType::And => exit_code.unwrap_or(0) == 0, - ChainType::Or => exit_code.unwrap_or(0) != 0, - ChainType::Then => true, - ChainType::None => false, - }; - - let next_index = cmd_index + 1; - if should_continue && next_index < state.execution.all_commands.len() { - launch_command_at_index(state, next_index, Some(pane_id), false); - } else { - state.execution.is_running = false; - state.selection.current_selected_command_index = None; - // Unblock blocking CLI pipe if one is waiting - if let Some(pipe_id) = state.blocking_pipe_id.take() { - unblock_cli_pipe_input(&pipe_id); - } - } - true -} - -fn handle_editor_pane_exited(state: &mut State, terminal_pane_id: u32) -> bool { - let pane_id = PaneId::Terminal(terminal_pane_id); - - // Ignore events from unrelated panes - if state.editor_pane_id != Some(pane_id) { - return false; - } - - // Read the temp file contents - let contents = if let Some(ref path) = state.editor_temp_file { - std::fs::read_to_string(path).unwrap_or_default() - } else { - String::new() - }; - - // Delete the temp file - if let Some(ref path) = state.editor_temp_file { - let _ = std::fs::remove_file(path); - } - - // Clear editor state - state.editor_pane_id = None; - state.editor_temp_file = None; - - // Parse file into commands - let commands = parse_commands(&contents, state.cwd.clone()); - - if !commands.is_empty() { - state.execution.all_commands = commands; - state.execution.current_running_command_index = 0; - state.selection.current_selected_command_index = None; - state.execution.is_running = false; - } else { - // Reset to empty pending state - state.execution.all_commands = vec![CommandEntry::new("", state.cwd.clone())]; - state.execution.current_running_command_index = 0; - state.selection.current_selected_command_index = None; - state.execution.is_running = false; - } - - show_self(true); - - true -} - -fn launch_command_at_index( - state: &mut State, - index: usize, - replace_pane_id: Option<PaneId>, - force_visible: bool, -) { - let cmd = &state.execution.all_commands[index]; - let cmd_text = cmd.get_text().trim().to_string(); - let command = CommandToRun { - path: state - .shell - .clone() - .unwrap_or_else(|| PathBuf::from("/bin/bash")), - args: vec!["-ic".to_string(), cmd_text.clone()], - cwd: cmd.get_cwd(), - }; - let user_is_watching = - replace_pane_id.is_some() && replace_pane_id == state.execution.displayed_pane_id; - let new_pane_id = if state.mode == SequenceMode::Spread { - open_command_pane_near_plugin(command, BTreeMap::new()) - } else if user_is_watching { - let prev_pane_id = replace_pane_id.unwrap(); - let pane_id = - open_command_pane_in_place_of_pane_id(prev_pane_id, command, false, BTreeMap::new()); - if let Some(pane_id) = pane_id { - state.execution.displayed_pane_id = Some(pane_id); - } - pane_id - } else if force_visible { - let (tab_id, pane_id) = open_command_pane_in_new_tab(command, BTreeMap::new()); - if let Some(pane_id) = pane_id { - state.execution.displayed_pane_id = Some(pane_id); - } - if let (Some(tab_id), Some(plugin_id)) = (tab_id, state.plugin_id) { - state.sequence_tab_id = Some(tab_id); - break_panes_to_tab_with_id(&[PaneId::Plugin(plugin_id)], tab_id, true); - focus_pane_with_id(PaneId::Plugin(plugin_id), false, false); - } - pane_id - } else { - // User navigated away — open in background, invisible until they navigate to it - open_command_pane_background(command, BTreeMap::new()) - }; - if let Some(pane_id) = new_pane_id { - rename_pane_with_id(pane_id, &cmd_text); - state.execution.all_commands[index].set_status(CommandStatus::Running(Some(pane_id))); - state.execution.current_running_command_index = index; - state.selection.current_selected_command_index = Some(index); - state.show_selected_pane(); - } -} - -fn handle_key_event(state: &mut State, key: KeyWithModifier) -> bool { - // Ctrl+Space — copy to clipboard - if key.has_modifiers(&[KeyModifier::Ctrl]) && matches!(key.bare_key, BareKey::Char(' ')) { - state.copy_to_clipboard(); - return false; - } - - // Ctrl+W — close tab if sequence has run; close plugin if staging; no-op if running - if key.has_modifiers(&[KeyModifier::Ctrl]) && matches!(key.bare_key, BareKey::Char('w')) { - if state.all_commands_are_pending() { - if let Some(plugin_id) = state.plugin_id { - close_pane_with_id(PaneId::Plugin(plugin_id)); - } - } else if !state.execution.is_running { - close_panes_and_return_to_shell(state); - } - return true; - } - - // Ctrl+C — interrupt if running; clear commands if staging; close plugin if empty - if key.has_modifiers(&[KeyModifier::Ctrl]) && matches!(key.bare_key, BareKey::Char('c')) { - if state.execution.is_running { - interrupt_sequence(state); - return true; - } else if state.all_commands_are_pending() { - if state.execution.all_commands.iter().any(|c| !c.is_empty()) { - state.clear_all_commands(); - } else if let Some(plugin_id) = state.plugin_id { - close_pane_with_id(PaneId::Plugin(plugin_id)); - } - return true; - } - } - - let is_staging = state.all_commands_are_pending() && state.execution.can_run_sequence(); - - if matches!(key.bare_key, BareKey::Up) - && !key.has_modifiers(&[KeyModifier::Ctrl]) - && !key.has_modifiers(&[KeyModifier::Alt]) - { - if !is_staging { - state.move_selection_up(); - return true; - } - return false; - } - - if matches!(key.bare_key, BareKey::Down) - && !key.has_modifiers(&[KeyModifier::Ctrl]) - && !key.has_modifiers(&[KeyModifier::Alt]) - { - if !is_staging { - state.move_selection_down(); - return true; - } - return false; - } - - // e — open external editor (when not running) - if !state.execution.is_running - && key.has_no_modifiers() - && matches!(key.bare_key, BareKey::Char('e')) - { - state.open_editor(); - return true; - } - - // Enter — rerun sequence - if key.has_no_modifiers() && matches!(key.bare_key, BareKey::Enter) { - rerun_sequence(state); - return true; - } - - // Tab — toggle spread mode (when sequence has run) - if key.has_no_modifiers() - && matches!(key.bare_key, BareKey::Tab) - && !state.all_commands_are_pending() - { - toggle_spread_mode(state); - return true; - } - - false -} - -fn close_panes_and_return_to_shell(state: &mut State) -> bool { - if let Some(tab_id) = state.sequence_tab_id { - close_tab_with_id(tab_id as u64); - } - true -} - -fn update_spinner(state: &mut State) -> bool { - // Advance spinner frame for RUNNING animation - state.layout.spinner_frame = (state.layout.spinner_frame + 1) % 8; - - // Reset timer scheduled flag since this timer just fired - state.layout.spinner_timer_scheduled = false; - - // If we have a running command, schedule next timer event - let has_running = state - .execution - .all_commands - .iter() - .any(|command| matches!(command.get_status(), CommandStatus::Running(_))); - if has_running { - set_timeout(0.1); - state.layout.spinner_timer_scheduled = true; - } - has_running -} - -fn rerun_sequence(state: &mut State) { - if !state.can_run_sequence() { - return; - } - - state.execution.all_commands.retain(|c| !c.is_empty()); - - if state.execution.all_commands.is_empty() { - return; - } - - let selected_index = state - .selection - .current_selected_command_index - .unwrap_or(0) - .min(state.execution.all_commands.len().saturating_sub(1)); - - state.execution.is_running = true; - state.execution.current_running_command_index = selected_index; - ensure_spinner_running(state); - match state.execution.all_commands[selected_index].get_pane_id() { - Some(PaneId::Terminal(id)) => { - let pane_id = PaneId::Terminal(id); - - // Replace the displayed pane with the starting pane first, while - // displayed_pane_id is still valid. The replaced pane is suppressed - // and can then be closed safely below. - if state.mode == SequenceMode::SinglePane { - if let Some(displayed) = state.execution.displayed_pane_id { - if displayed != pane_id { - replace_pane_with_existing_pane(displayed, pane_id, true); - } - } - state.execution.displayed_pane_id = Some(pane_id); - } - - // Close and reset commands after selected (now suppressed). - for i in (selected_index + 1)..state.execution.all_commands.len() { - if let Some(p) = state.execution.all_commands[i].get_pane_id() { - close_pane_with_id(p); - } - state.execution.all_commands[i].set_status(CommandStatus::Pending); - } - - state.execution.all_commands[selected_index] - .set_status(CommandStatus::Running(Some(pane_id))); - state.selection.current_selected_command_index = Some(selected_index); - rerun_command_pane(id); - }, - _ => { - // Close and reset commands after selected. - for i in (selected_index + 1)..state.execution.all_commands.len() { - if let Some(p) = state.execution.all_commands[i].get_pane_id() { - close_pane_with_id(p); - } - state.execution.all_commands[i].set_status(CommandStatus::Pending); - } - let replace_pane_id = state.execution.displayed_pane_id; - launch_command_at_index(state, selected_index, replace_pane_id, true); - }, - } -} - -fn toggle_spread_mode(state: &mut State) { - match state.mode { - SequenceMode::SinglePane => enter_spread_mode(state), - SequenceMode::Spread => exit_spread_mode(state), - } -} - -fn enter_spread_mode(state: &mut State) { - state.mode = SequenceMode::Spread; - - let first_pane_id = state - .execution - .all_commands - .first() - .and_then(|c| c.get_pane_id()); - let Some(first_pane_id) = first_pane_id else { - return; - }; - - if let Some(displayed) = state.execution.displayed_pane_id { - if displayed != first_pane_id { - replace_pane_with_existing_pane(displayed, first_pane_id, true); - } - } - state.execution.displayed_pane_id = Some(first_pane_id); - - for cmd in state.execution.all_commands.iter().skip(1) { - if let Some(pane_id) = cmd.get_pane_id() { - show_pane_with_id(pane_id, false, false); - } - } - state.show_selected_pane(); -} - -fn exit_spread_mode(state: &mut State) { - // Remove all highlights before leaving spread mode - let all_pane_ids: Vec<PaneId> = state - .execution - .all_commands - .iter() - .filter_map(|c| c.get_pane_id()) - .collect(); - if !all_pane_ids.is_empty() { - highlight_and_unhighlight_panes(vec![], all_pane_ids); - } - state.mode = SequenceMode::SinglePane; - - let first_pane_id = state - .execution - .all_commands - .first() - .and_then(|c| c.get_pane_id()); - let selected_pane_id = state - .selection - .current_selected_command_index - .and_then(|i| state.execution.all_commands.get(i)) - .and_then(|c| c.get_pane_id()) - .or(first_pane_id); - - let Some(selected_pane_id) = selected_pane_id else { - return; - }; - - if let Some(first_pane_id) = first_pane_id { - if first_pane_id != selected_pane_id { - replace_pane_with_existing_pane(first_pane_id, selected_pane_id, true); - } - } - - for cmd in &state.execution.all_commands { - if let Some(pane_id) = cmd.get_pane_id() { - if pane_id != selected_pane_id { - hide_pane_with_id(pane_id); - } - } - } - - state.execution.displayed_pane_id = Some(selected_pane_id); -} - -fn interrupt_sequence(state: &mut State) { - if state.execution.is_running { - for command in state.execution.all_commands.iter_mut() { - if let CommandStatus::Running(pane_id) = command.get_status() { - if let Some(pane_id) = pane_id { - send_sigkill_to_pane_id(pane_id); - } - command.set_status(CommandStatus::Interrupted(pane_id)); - } - } - state.execution.is_running = false; - state.selection.current_selected_command_index = None; - // Unblock blocking CLI pipe if interrupted - if let Some(pipe_id) = state.blocking_pipe_id.take() { - unblock_cli_pipe_input(&pipe_id); - } - } else { - eprintln!("Cannot interrupt a sequence that is not running."); - } -} - -pub fn update_title(state: &mut State) { - let Some(tab_id) = state.sequence_tab_id else { - return; - }; - let tab_id_u64 = tab_id as u64; - - // All pending and not yet running - if state.all_commands_are_pending() && !state.execution.is_running { - rename_tab_with_id(tab_id_u64, "Run one or more commands in sequence"); - return; - } - - // Currently running: "<cmd> <n>/<total> <spinner>" - let has_running = state - .execution - .all_commands - .iter() - .any(|c| matches!(c.get_status(), CommandStatus::Running(..))); - if has_running { - let idx = state.execution.current_running_command_index; - let total = state.execution.all_commands.len(); - let cmd_text = state - .execution - .all_commands - .get(idx) - .map(|c| c.get_text()) - .unwrap_or_default(); - let spinner = components::get_spinner_frame(state.layout.spinner_frame); - rename_tab_with_id( - tab_id_u64, - &format!("{} {}/{} {}", cmd_text, idx + 1, total, spinner), - ); - return; - } - - // Interrupted: "<cmd> [INTERRUPTED]" - if let Some(cmd) = state - .execution - .all_commands - .iter() - .find(|c| matches!(c.get_status(), CommandStatus::Interrupted(_))) - { - let status_str = - components::format_status_text(&cmd.get_status(), state.layout.spinner_frame); - rename_tab_with_id(tab_id_u64, &format!("{} {}", cmd.get_text(), status_str)); - return; - } - - // All exited - let all_exited = state - .execution - .all_commands - .iter() - .all(|c| matches!(c.get_status(), CommandStatus::Exited(..))); - if all_exited { - let all_success = state - .execution - .all_commands - .iter() - .all(|c| matches!(c.get_status(), CommandStatus::Exited(Some(0), _))); - if all_success { - rename_tab_with_id(tab_id_u64, "Sequence Complete"); - } else { - // Show first command with non-zero or unknown exit - if let Some(cmd) = state - .execution - .all_commands - .iter() - .find(|c| !matches!(c.get_status(), CommandStatus::Exited(Some(0), _))) - { - let status_str = - components::format_status_text(&cmd.get_status(), state.layout.spinner_frame); - rename_tab_with_id(tab_id_u64, &format!("{} {}", cmd.get_text(), status_str)); - } else { - rename_tab_with_id(tab_id_u64, "Sequence Complete"); - } - } - return; - } - - // Partial (chain condition failed): show last exited command with its status - if let Some(cmd) = state - .execution - .all_commands - .iter() - .rev() - .find(|c| matches!(c.get_status(), CommandStatus::Exited(..))) - { - let status_str = - components::format_status_text(&cmd.get_status(), state.layout.spinner_frame); - rename_tab_with_id(tab_id_u64, &format!("{} {}", cmd.get_text(), status_str)); - } else { - rename_tab_with_id(tab_id_u64, "Stopped"); - } -} - -fn ensure_spinner_running(state: &mut State) { - if !state.layout.spinner_timer_scheduled { - set_timeout(0.1); - state.layout.spinner_timer_scheduled = true; - } -} diff --git a/default-plugins/sequence/src/path_formatting.rs b/default-plugins/sequence/src/path_formatting.rs deleted file mode 100644 index 01bf50e6..00000000 --- a/default-plugins/sequence/src/path_formatting.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::PathBuf; - -/// Expand a path string to an absolute PathBuf -/// Handles ~, ./, .., relative and absolute paths -pub fn expand_path(path_str: &str, current_cwd: Option<&PathBuf>) -> Option<PathBuf> { - let expanded = if path_str.starts_with("~/") || path_str == "~" { - let home_dir = std::env::var("HOME").ok()?; - if path_str == "~" { - PathBuf::from(home_dir) - } else { - PathBuf::from(home_dir).join(&path_str[2..]) - } - } else if path_str.starts_with('/') { - PathBuf::from(path_str) - } else { - let base = current_cwd?; - base.join(path_str) - }; - - let mut normalized = PathBuf::new(); - for component in expanded.components() { - match component { - std::path::Component::ParentDir => { - normalized.pop(); - }, - std::path::Component::CurDir => {}, - _ => { - normalized.push(component); - }, - } - } - - Some(normalized) -} - -/// Format a path for display in the prompt -/// Example: /home/user/some/long/path -> ~/s/l/path -pub fn format_cwd(cwd: &PathBuf) -> String { - let path_str = cwd.to_string_lossy().to_string(); - - let home_dir = std::env::var("HOME").unwrap_or_default(); - let path_str = if !home_dir.is_empty() && path_str.starts_with(&home_dir) { - path_str.replacen(&home_dir, "~", 1) - } else { - path_str - }; - - let parts: Vec<&str> = path_str.split('/').collect(); - - if parts.len() <= 1 { - return path_str; - } - - let mut formatted_parts = Vec::new(); - for (i, part) in parts.iter().enumerate() { - if i == parts.len() - 1 { - formatted_parts.push(part.to_string()); - } else if part.is_empty() { - formatted_parts.push(String::new()); - } else { - formatted_parts.push(part.chars().next().unwrap_or_default().to_string()); - } - } - - formatted_parts.join("/") -} - -/// Alias for expand_path for backwards compatibility -pub fn resolve_path(current_cwd: Option<&PathBuf>, path_str: &str) -> Option<PathBuf> { - expand_path(path_str, current_cwd) -} diff --git a/default-plugins/sequence/src/state/chain_type.rs b/default-plugins/sequence/src/state/chain_type.rs deleted file mode 100644 index 3da3316c..00000000 --- a/default-plugins/sequence/src/state/chain_type.rs +++ /dev/null @@ -1,45 +0,0 @@ -use serde::{Deserialize, Serialize}; -use zellij_tile::prelude::*; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum ChainType { - And, - Or, - Then, - None, -} - -impl Default for ChainType { - fn default() -> Self { - ChainType::None - } -} - -impl ChainType { - pub fn to_unblock_condition(&self) -> Option<UnblockCondition> { - match self { - ChainType::And => Some(UnblockCondition::OnExitSuccess), - ChainType::Or => Some(UnblockCondition::OnExitFailure), - ChainType::Then => Some(UnblockCondition::OnAnyExit), - ChainType::None => None, - } - } - - pub fn as_str(&self) -> &str { - match self { - ChainType::And => "&&", - ChainType::Or => "||", - ChainType::Then => ";", - ChainType::None => "", - } - } - - pub fn cycle_next(&mut self) { - *self = match self { - ChainType::And => ChainType::Or, - ChainType::Or => ChainType::Then, - ChainType::Then => ChainType::And, - ChainType::None => ChainType::And, - }; - } -} diff --git a/default-plugins/sequence/src/state/command_entry.rs b/default-plugins/sequence/src/state/command_entry.rs deleted file mode 100644 index 4ca9350a..00000000 --- a/default-plugins/sequence/src/state/command_entry.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::{ChainType, CommandStatus}; -use std::path::PathBuf; -use zellij_tile::prelude::PaneId; - -#[derive(Debug, Clone)] -pub struct CommandEntry { - text: String, - cwd: Option<PathBuf>, - pub(super) chain_type: ChainType, - pub(super) status: CommandStatus, -} - -impl Default for CommandEntry { - fn default() -> Self { - CommandEntry { - text: String::default(), - cwd: None, - chain_type: ChainType::default(), - status: CommandStatus::default(), - } - } -} - -impl CommandEntry { - pub fn new(text: &str, cwd: Option<PathBuf>) -> Self { - CommandEntry { - text: text.to_owned(), - cwd, - ..Default::default() - } - } - pub fn get_text(&self) -> String { - self.text.clone() - } - pub fn set_text(&mut self, text: String) { - self.text = text; - } - pub fn clear_text(&mut self) { - self.text.clear(); - } - pub fn get_chain_type(&self) -> ChainType { - self.chain_type - } - pub fn set_chain_type(&mut self, chain_type: ChainType) { - self.chain_type = chain_type; - } - pub fn get_status(&self) -> CommandStatus { - self.status.clone() - } - pub fn set_status(&mut self, status: CommandStatus) { - self.status = status; - } - pub fn get_pane_id(&self) -> Option<PaneId> { - self.status.get_pane_id() - } - pub fn is_empty(&self) -> bool { - self.text.trim().is_empty() - } - pub fn fill_chain_type_if_empty(&mut self) { - if let ChainType::None = self.chain_type { - self.chain_type = ChainType::And; - } - } - pub fn clear_chain_type(&mut self) { - self.chain_type = ChainType::None; - } - pub fn clear_status(&mut self) { - self.status = CommandStatus::Pending; - } - pub fn get_cwd(&self) -> Option<PathBuf> { - self.cwd.clone() - } - pub fn set_cwd(&mut self, cwd: Option<PathBuf>) { - self.cwd = cwd; - } -} diff --git a/default-plugins/sequence/src/state/command_parser.rs b/default-plugins/sequence/src/state/command_parser.rs deleted file mode 100644 index 35935cd3..00000000 --- a/default-plugins/sequence/src/state/command_parser.rs +++ /dev/null @@ -1,370 +0,0 @@ -use super::{ChainType, CommandEntry}; - -#[derive(Debug, Clone, Copy, PartialEq)] -enum ParseState { - Normal, - InSingleQuote, - InDoubleQuote, - Escaped(EscapeContext), -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum EscapeContext { - Normal, - DoubleQuote, -} - -pub fn split_by_chain_operators(text: &str) -> Vec<(String, Option<ChainType>)> { - let mut segments = Vec::new(); - let mut current_segment = String::new(); - let mut state = ParseState::Normal; - let chars: Vec<char> = text.chars().collect(); - let mut i = 0; - - while i < chars.len() { - let ch = chars[i]; - - match state { - ParseState::Normal => { - if ch == '\\' { - state = ParseState::Escaped(EscapeContext::Normal); - current_segment.push(ch); - i += 1; - } else if ch == '\'' { - state = ParseState::InSingleQuote; - current_segment.push(ch); - i += 1; - } else if ch == '"' { - state = ParseState::InDoubleQuote; - current_segment.push(ch); - i += 1; - } else if ch == '&' && i + 1 < chars.len() && chars[i + 1] == '&' { - let segment_text = current_segment.trim().to_string(); - if !segment_text.is_empty() { - segments.push((segment_text, Some(ChainType::And))); - } - current_segment.clear(); - i += 2; - } else if ch == '|' && i + 1 < chars.len() && chars[i + 1] == '|' { - let segment_text = current_segment.trim().to_string(); - if !segment_text.is_empty() { - segments.push((segment_text, Some(ChainType::Or))); - } - current_segment.clear(); - i += 2; - } else if ch == ';' { - let segment_text = current_segment.trim().to_string(); - if !segment_text.is_empty() { - segments.push((segment_text, Some(ChainType::Then))); - } - current_segment.clear(); - i += 1; - } else { - current_segment.push(ch); - i += 1; - } - }, - ParseState::InSingleQuote => { - current_segment.push(ch); - if ch == '\'' { - state = ParseState::Normal; - } - i += 1; - }, - ParseState::InDoubleQuote => { - if ch == '\\' { - state = ParseState::Escaped(EscapeContext::DoubleQuote); - current_segment.push(ch); - i += 1; - } else if ch == '"' { - current_segment.push(ch); - state = ParseState::Normal; - i += 1; - } else { - current_segment.push(ch); - i += 1; - } - }, - ParseState::Escaped(context) => { - current_segment.push(ch); - state = match context { - EscapeContext::Normal => ParseState::Normal, - EscapeContext::DoubleQuote => ParseState::InDoubleQuote, - }; - i += 1; - }, - } - } - - let final_segment = current_segment.trim().to_string(); - if !final_segment.is_empty() { - segments.push((final_segment, None)); - } - - segments -} - -pub fn detect_cd_command(text: &str) -> Option<String> { - let trimmed = text.trim(); - - if trimmed == "cd" { - return Some("~".to_string()); - } - - if trimmed.starts_with("cd ") { - let path = trimmed[3..].trim(); - return Some(path.to_string()); - } - - None -} - -/// Serialize a sequence of commands to editor format. -/// CWD changes are emitted as `cd <path>` lines; chain type as trailing ` &&`, ` ||`, ` ;`. -pub fn serialize_sequence_to_editor(commands: &[CommandEntry]) -> String { - let mut result = String::new(); - - result.push_str("# One cmd per line. Use && || ; to chain. cd <path> sets cwd.\n"); - result.push_str("\n"); - - let mut prev_cwd: Option<std::path::PathBuf> = None; - - for cmd in commands { - if cmd.get_text().trim().is_empty() { - continue; - } - - let cmd_cwd = cmd.get_cwd(); - if cmd_cwd != prev_cwd && cmd_cwd.is_some() { - let path = cmd_cwd.as_ref().unwrap(); - result.push_str(&format!("cd {}\n", path.display())); - } - prev_cwd = cmd_cwd; - - let op_suffix = match cmd.get_chain_type() { - ChainType::And => " &&", - ChainType::Or => " ||", - ChainType::Then => " ;", - ChainType::None => "", - }; - - result.push_str(&format!("{}{}\n", cmd.get_text(), op_suffix)); - } - - result -} - -/// Parse command text into a Vec<CommandEntry>. -/// `cd <path>` lines set the cwd for subsequent commands. -/// Blank lines and `#` comment lines are ignored. -pub fn parse_commands( - contents: &str, - initial_cwd: Option<std::path::PathBuf>, -) -> Vec<CommandEntry> { - use crate::path_formatting; - let mut commands = Vec::new(); - let mut current_cwd = initial_cwd; - - for line in contents.lines() { - let trimmed = line.trim(); - if trimmed.is_empty() || trimmed.starts_with('#') { - continue; - } - - // Split by chain operators first so that "cd /path &&" doesn't absorb the operator - // into the path. Each resulting segment is then checked for cd independently. - let segments = split_by_chain_operators(trimmed); - for (text, chain_type_opt) in segments { - let text = text.trim().to_string(); - if text.is_empty() { - continue; - } - if let Some(path) = detect_cd_command(&text) { - if let Some(new_cwd) = path_formatting::resolve_path(current_cwd.as_ref(), &path) { - current_cwd = Some(new_cwd); - } - // cd segments are not added as command entries; their chain type is discarded - } else { - let mut entry = CommandEntry::new(&text, current_cwd.clone()); - if let Some(chain_type) = chain_type_opt { - entry.set_chain_type(chain_type); - } - commands.push(entry); - } - } - } - - // Commands with no explicit operator default to && - if !commands.is_empty() { - let last_idx = commands.len() - 1; - for cmd in &mut commands[..last_idx] { - cmd.fill_chain_type_if_empty(); - } - } - - commands -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_single_command_no_operator() { - let result = split_by_chain_operators("ls"); - assert_eq!(result, vec![("ls".to_string(), None)]); - } - - #[test] - fn test_two_commands_with_and() { - let result = split_by_chain_operators("ls && pwd"); - assert_eq!( - result, - vec![ - ("ls".to_string(), Some(ChainType::And)), - ("pwd".to_string(), None) - ] - ); - } - - #[test] - fn test_two_commands_with_or() { - let result = split_by_chain_operators("cmd1 || cmd2"); - assert_eq!( - result, - vec![ - ("cmd1".to_string(), Some(ChainType::Or)), - ("cmd2".to_string(), None) - ] - ); - } - - #[test] - fn test_two_commands_with_semicolon() { - let result = split_by_chain_operators("echo hi ; ls"); - assert_eq!( - result, - vec![ - ("echo hi".to_string(), Some(ChainType::Then)), - ("ls".to_string(), None) - ] - ); - } - - #[test] - fn test_three_commands_mixed() { - let result = split_by_chain_operators("a && b || c"); - assert_eq!( - result, - vec![ - ("a".to_string(), Some(ChainType::And)), - ("b".to_string(), Some(ChainType::Or)), - ("c".to_string(), None) - ] - ); - } - - #[test] - fn test_operator_in_single_quotes() { - let result = split_by_chain_operators("echo '&&' && ls"); - assert_eq!( - result, - vec![ - ("echo '&&'".to_string(), Some(ChainType::And)), - ("ls".to_string(), None) - ] - ); - } - - #[test] - fn test_operator_in_double_quotes() { - let result = split_by_chain_operators("echo \"||\" || pwd"); - assert_eq!( - result, - vec![ - ("echo \"||\"".to_string(), Some(ChainType::Or)), - ("pwd".to_string(), None) - ] - ); - } - - #[test] - fn test_escaped_operator() { - let result = split_by_chain_operators("echo \\&& test"); - assert_eq!(result, vec![("echo \\&& test".to_string(), None)]); - } - - #[test] - fn test_complex_quoting() { - let result = split_by_chain_operators("cmd1 && echo \"a || b\" && cmd2"); - assert_eq!( - result, - vec![ - ("cmd1".to_string(), Some(ChainType::And)), - ("echo \"a || b\"".to_string(), Some(ChainType::And)), - ("cmd2".to_string(), None) - ] - ); - } - - #[test] - fn test_empty_segments_filtered() { - let result = split_by_chain_operators("cmd1 && && cmd2"); - assert_eq!( - result, - vec![ - ("cmd1".to_string(), Some(ChainType::And)), - ("cmd2".to_string(), None) - ] - ); - } - - #[test] - fn test_whitespace_trimming() { - let result = split_by_chain_operators(" cmd1 && cmd2 "); - assert_eq!( - result, - vec![ - ("cmd1".to_string(), Some(ChainType::And)), - ("cmd2".to_string(), None) - ] - ); - } - - #[test] - fn test_trailing_operator_with_space() { - let result = split_by_chain_operators("ls && "); - assert_eq!(result, vec![("ls".to_string(), Some(ChainType::And))]); - } - - #[test] - fn test_editor_file_no_operator_defaults_to_and() { - let contents = "cmd1\ncmd2\ncmd3"; - let result = parse_commands(contents, None); - assert_eq!(result.len(), 3); - assert_eq!(result[0].get_chain_type(), ChainType::And); - assert_eq!(result[1].get_chain_type(), ChainType::And); - assert_eq!(result[2].get_chain_type(), ChainType::None); - } - - #[test] - fn test_editor_file_explicit_operator_preserved() { - let contents = "cmd1 ||\ncmd2"; - let result = parse_commands(contents, None); - assert_eq!(result.len(), 2); - assert_eq!(result[0].get_chain_type(), ChainType::Or); - assert_eq!(result[1].get_chain_type(), ChainType::None); - } - - #[test] - fn test_cd_with_chain_operator_does_not_absorb_operator_into_path() { - use super::super::CommandEntry; - use std::path::PathBuf; - - let contents = "cd /some/path && cargo build"; - let result = parse_commands(contents, None); - assert_eq!(result.len(), 1); - assert_eq!(result[0].get_text(), "cargo build"); - assert_eq!(result[0].get_cwd(), Some(PathBuf::from("/some/path"))); - } -} diff --git a/default-plugins/sequence/src/state/command_status.rs b/default-plugins/sequence/src/state/command_status.rs deleted file mode 100644 index 39717e8b..00000000 --- a/default-plugins/sequence/src/state/command_status.rs +++ /dev/null @@ -1,26 +0,0 @@ -use zellij_tile::prelude::*; - -#[derive(Debug, Clone)] -pub enum CommandStatus { - Exited(Option<i32>, Option<PaneId>), - Running(Option<PaneId>), - Pending, - Interrupted(Option<PaneId>), -} - -impl CommandStatus { - pub fn get_pane_id(&self) -> Option<PaneId> { - match self { - CommandStatus::Exited(_, pane_id) => *pane_id, - CommandStatus::Running(pane_id) => *pane_id, - CommandStatus::Pending => None, - CommandStatus::Interrupted(pane_id) => *pane_id, - } - } -} - -impl Default for CommandStatus { - fn default() -> Self { - CommandStatus::Pending - } -} diff --git a/default-plugins/sequence/src/state/execution.rs b/default-plugins/sequence/src/state/execution.rs deleted file mode 100644 index 42d4112a..00000000 --- a/default-plugins/sequence/src/state/execution.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::path_formatting::format_cwd; -use crate::state::{CommandEntry, CommandStatus}; -use std::path::PathBuf; -use zellij_tile::prelude::*; - -pub struct Execution { - pub all_commands: Vec<CommandEntry>, - pub current_running_command_index: usize, - pub is_running: bool, - pub displayed_pane_id: Option<PaneId>, -} - -impl Execution { - pub fn new() -> Self { - Self { - all_commands: vec![CommandEntry::default()], - current_running_command_index: 0, - is_running: false, - displayed_pane_id: None, - } - } - - pub fn longest_cwd_display(&self, global_cwd: &Option<PathBuf>) -> String { - self.all_commands - .iter() - .map(|cmd| { - let cwd = cmd.get_cwd().or_else(|| global_cwd.clone()); - if let Some(cwd) = &cwd { - format_cwd(cwd) - } else { - "~".to_string() - } - }) - .max_by_key(|s| s.len()) - .unwrap_or_else(|| "~".to_string()) - } - - pub fn remove_empty_commands(&mut self) { - if self.all_commands.iter().len() > 1 { - self.all_commands.retain(|c| !c.get_text().is_empty()); - } - } - - pub fn get_first_command(&self) -> Option<CommandEntry> { - self.all_commands.iter().next().cloned() - } - - pub fn set_command_status(&mut self, command_index: usize, status: CommandStatus) { - self.all_commands - .get_mut(command_index) - .map(|c| c.set_status(status)); - } - - pub fn set_current_running_command_status(&mut self, status: CommandStatus) { - self.all_commands - .get_mut(self.current_running_command_index) - .map(|c| c.set_status(status)); - } - - pub fn get_current_running_command_status(&self) -> Option<CommandStatus> { - self.all_commands - .get(self.current_running_command_index) - .map(|c| c.get_status()) - } - - pub fn can_run_sequence(&self) -> bool { - self.all_commands.iter().any(|command| !command.is_empty()) - } - - pub fn copy_to_clipboard(&self) { - let text_to_copy = self - .all_commands - .iter() - .map(|c| format!("{}", c.get_text())) - .collect::<Vec<_>>() - .join("\n"); - if !text_to_copy.is_empty() { - copy_to_clipboard(text_to_copy); - } - } -} - -impl Default for Execution { - fn default() -> Self { - Self::new() - } -} diff --git a/default-plugins/sequence/src/state/layout.rs b/default-plugins/sequence/src/state/layout.rs deleted file mode 100644 index 6b070778..00000000 --- a/default-plugins/sequence/src/state/layout.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub struct Layout { - pub spinner_frame: usize, - pub spinner_timer_scheduled: bool, -} - -impl Layout { - pub fn new() -> Self { - Self { - spinner_frame: 0, - spinner_timer_scheduled: false, - } - } -} - -impl Default for Layout { - fn default() -> Self { - Self::new() - } -} diff --git a/default-plugins/sequence/src/state/mod.rs b/default-plugins/sequence/src/state/mod.rs deleted file mode 100644 index 148e6702..00000000 --- a/default-plugins/sequence/src/state/mod.rs +++ /dev/null @@ -1,231 +0,0 @@ -mod chain_type; -mod command_entry; -mod command_parser; -mod command_status; -mod execution; -mod layout; -mod selection; -mod sequence_mode; - -pub use chain_type::ChainType; -pub use command_entry::CommandEntry; -pub use command_parser::{parse_commands, serialize_sequence_to_editor}; -pub use command_status::CommandStatus; -pub use execution::Execution; -pub use layout::Layout; -pub use selection::Selection; -pub use sequence_mode::SequenceMode; - -use std::collections::BTreeMap; -use std::path::PathBuf; -use zellij_tile::prelude::*; - -#[derive(Default)] -pub struct State { - pub shell: Option<PathBuf>, - pub plugin_id: Option<u32>, - pub client_id: Option<u16>, - pub own_rows: Option<usize>, - pub own_columns: Option<usize>, - pub cwd: Option<PathBuf>, - pub total_viewport_columns: Option<usize>, - pub total_viewport_rows: Option<usize>, - pub selection: Selection, - pub execution: Execution, - pub layout: Layout, - pub current_position: Option<FloatingPaneCoordinates>, - pub mode: SequenceMode, - pub editor_pane_id: Option<PaneId>, - pub editor_temp_file: Option<PathBuf>, - pub sequence_tab_id: Option<usize>, - pub blocking_pipe_id: Option<String>, -} - -impl State { - pub fn set_plugin_id(&mut self, plugin_id: u32) { - self.plugin_id = Some(plugin_id); - } - - pub fn move_selection_up(&mut self) { - self.selection.move_up(&self.execution.all_commands); - self.show_selected_pane(); - } - - pub fn move_selection_down(&mut self) { - self.selection.move_down(&self.execution.all_commands); - self.show_selected_pane(); - } - - pub fn show_selected_pane(&mut self) { - if self.mode == SequenceMode::Spread { - let all_pane_ids: Vec<PaneId> = self - .execution - .all_commands - .iter() - .filter_map(|c| c.get_pane_id()) - .collect(); - let selected_pane_id = self - .current_selected_command() - .and_then(|c| c.get_pane_id()); - if let Some(selected) = selected_pane_id { - let to_unhighlight: Vec<PaneId> = all_pane_ids - .into_iter() - .filter(|&p| p != selected) - .collect(); - highlight_and_unhighlight_panes(vec![selected], to_unhighlight); - } - return; - } - let target_pane_id = self - .current_selected_command() - .and_then(|c| c.get_pane_id()); - if let (Some(displayed), Some(target)) = (self.execution.displayed_pane_id, target_pane_id) - { - if displayed != target { - replace_pane_with_existing_pane(displayed, target, true); - self.execution.displayed_pane_id = Some(target); - } - } - } - - pub fn remove_empty_commands(&mut self) { - self.execution.remove_empty_commands(); - } - - pub fn clear_all_commands(&mut self) { - self.execution.all_commands = vec![CommandEntry::new("", self.cwd.clone())]; - self.selection.current_selected_command_index = None; - self.execution.current_running_command_index = 0; - } - - pub fn load_commands(&mut self, command_string: &str, cwd_override: Option<PathBuf>) { - let effective_cwd = cwd_override.or_else(|| self.cwd.clone()); - let commands = parse_commands(command_string, effective_cwd); - if commands.is_empty() { - return; - } - self.execution.all_commands = commands; - self.execution.current_running_command_index = 0; - self.selection.current_selected_command_index = None; - } - - pub fn can_run_sequence(&self) -> bool { - self.execution.can_run_sequence() - } - - pub fn copy_to_clipboard(&mut self) { - self.execution.copy_to_clipboard(); - } - - pub fn current_selected_command_mut(&mut self) -> Option<&mut CommandEntry> { - let Some(i) = self.selection.current_selected_command_index else { - return None; - }; - self.execution.all_commands.get_mut(i) - } - - /// Serialize current sequence to a temp file and open it in the user's $EDITOR. - /// Stores the editor pane id and temp file path for later retrieval. - pub fn open_editor(&mut self) { - if self.execution.is_running { - return; - } - let Some(plugin_id) = self.plugin_id else { - return; - }; - - let serialized = serialize_sequence_to_editor(&self.execution.all_commands); - let temp_path = PathBuf::from(format!("/tmp/zellij-sequence-{}.sh", plugin_id)); - - if let Err(e) = std::fs::write(&temp_path, &serialized) { - eprintln!("Failed to write sequence to temp file: {}", e); - return; - } - - let file_to_open = FileToOpen::new(temp_path.clone()); - let editor_pane_id = open_file_in_place_of_plugin(file_to_open, false, BTreeMap::new()); - self.editor_pane_id = editor_pane_id; - self.editor_temp_file = Some(temp_path); - } - - pub fn reposition_plugin(&mut self) -> bool { - let mut repositioned = false; - let Some(plugin_id) = self.plugin_id else { - return repositioned; - }; - let Some(total_viewport_rows) = self.total_viewport_rows else { - return repositioned; - }; - let Some(total_viewport_columns) = self.total_viewport_columns else { - return repositioned; - }; - if self.all_commands_are_pending() { - return false; - } - - let total_commands = std::cmp::max(self.execution.all_commands.iter().len(), 1); - let height_padding = 7; - - // Calculate longest table row width - let longest_cwd_display = self.execution.longest_cwd_display(&self.cwd); - let longest_command = self - .execution - .all_commands - .iter() - .map(|cmd| cmd.get_text().chars().count()) - .max() - .unwrap_or(1); - - let (max_chain_width, max_status_width) = crate::ui::components::calculate_max_widths( - &self.execution.all_commands, - self.layout.spinner_frame, - ); - - let longest_line = crate::ui::components::calculate_longest_line( - &longest_cwd_display, - longest_command, - max_chain_width, - max_status_width, - ); - - let ui_width = std::cmp::max(longest_line, 50) + 4; // 2 for ui-padding, 2 for pane frame - let width = std::cmp::min(ui_width, total_viewport_columns.saturating_sub(2)); - - let height = (height_padding + total_commands).min((total_viewport_rows * 50) / 100); - - // Position: top-right with 1-space margins - let x = total_viewport_columns.saturating_sub(width); - let y = 1; - - let coordinates = FloatingPaneCoordinates::new( - Some(x.to_string()), - Some(y.to_string()), - Some(width.to_string()), - Some(height.to_string()), - Some(true), // should be pinned for sequence - Some(false), - ); - coordinates.map(|coordinates| { - if Some(&coordinates) != self.current_position.as_ref() { - self.current_position = Some(coordinates.clone()); - repositioned = true; - change_floating_panes_coordinates(vec![(PaneId::Plugin(plugin_id), coordinates)]); - } - }); - repositioned - } - - pub fn all_commands_are_pending(&self) -> bool { - self.execution - .all_commands - .iter() - .all(|command| matches!(command.get_status(), CommandStatus::Pending)) - } - - fn current_selected_command(&self) -> Option<&CommandEntry> { - let Some(i) = self.selection.current_selected_command_index else { - return None; - }; - self.execution.all_commands.get(i) - } -} diff --git a/default-plugins/sequence/src/state/selection.rs b/default-plugins/sequence/src/state/selection.rs deleted file mode 100644 index 2137f47d..00000000 --- a/default-plugins/sequence/src/state/selection.rs +++ /dev/null @@ -1,47 +0,0 @@ -use super::CommandEntry; - -pub struct Selection { - pub current_selected_command_index: Option<usize>, -} - -impl Selection { - pub fn new() -> Self { - Self { - current_selected_command_index: None, - } - } - - pub fn move_up(&mut self, all_commands: &[CommandEntry]) { - match self.current_selected_command_index.as_mut() { - Some(i) if *i > 0 => { - *i -= 1; - }, - None => { - self.current_selected_command_index = Some(all_commands.len().saturating_sub(1)); - }, - _ => { - self.current_selected_command_index = None; - }, - } - } - - pub fn move_down(&mut self, all_commands: &[CommandEntry]) { - match self.current_selected_command_index.as_mut() { - Some(i) if *i < all_commands.len().saturating_sub(1) => { - *i += 1; - }, - None => { - self.current_selected_command_index = Some(0); - }, - _ => { - self.current_selected_command_index = None; - }, - } - } -} - -impl Default for Selection { - fn default() -> Self { - Self::new() - } -} diff --git a/default-plugins/sequence/src/state/sequence_mode.rs b/default-plugins/sequence/src/state/sequence_mode.rs deleted file mode 100644 index 3d079d70..00000000 --- a/default-plugins/sequence/src/state/sequence_mode.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(Default, Debug, Clone, PartialEq)] -pub enum SequenceMode { - #[default] - SinglePane, - Spread, -} diff --git a/default-plugins/sequence/src/ui/components.rs b/default-plugins/sequence/src/ui/components.rs deleted file mode 100644 index bc319567..00000000 --- a/default-plugins/sequence/src/ui/components.rs +++ /dev/null @@ -1,567 +0,0 @@ -use crate::path_formatting::format_cwd; -use crate::state::{CommandStatus, State}; -use crate::ui::truncation::{calculate_available_cmd_width, truncate_middle}; -use std::path::PathBuf; -use zellij_tile::prelude::*; - -const SPINNER_FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"]; - -const INTRO_PART1: &str = "Paste a series of commands to run in sequence"; -const INTRO_PART2: &str = "or <e> to edit in your editor"; -const INTRO_FULL: &str = - "Paste a series of commands to run in sequence or <e> to edit in your editor"; - -const HELP_RUNNING_WITH_SELECTION: &str = "<Ctrl c> - interrupt, <Enter> - run from selected"; -const HELP_RUNNING_NO_SELECTION: &str = "<Ctrl c> - interrupt, <↓↑> - navigate"; -const HELP_STOPPED_WITH_SELECTION: &str = - "<Ctrl w> - close all, <Enter> - run from selected, <e> - edit"; -const HELP_STOPPED_NO_SELECTION: &str = - "<Ctrl w> - close all, <↓↑> - navigate, <Enter> - run from first"; - -const HELP_PENDING: &str = "<e> - edit, <Enter> - run"; - -fn select_help_text(sequence: &State) -> &'static str { - let is_running = sequence - .execution - .all_commands - .iter() - .any(|command| matches!(command.get_status(), CommandStatus::Running(_))); - let all_pending = sequence - .execution - .all_commands - .iter() - .all(|command| matches!(command.get_status(), CommandStatus::Pending)); - let has_selection = sequence.selection.current_selected_command_index.is_some(); - - if all_pending && !sequence.execution.is_running { - HELP_PENDING - } else if is_running { - if has_selection { - HELP_RUNNING_WITH_SELECTION - } else { - HELP_RUNNING_NO_SELECTION - } - } else { - if has_selection { - HELP_STOPPED_WITH_SELECTION - } else { - HELP_STOPPED_NO_SELECTION - } - } -} - -fn style_help_text(text: &str) -> Text { - Text::new(text) - .color_substring(3, "<Ctrl c>") - .color_substring(3, "<Ctrl w>") - .color_substring(3, "<↓↑>") - .color_substring(3, "<Esc>") - .color_substring(3, "<Enter>") - .color_substring(3, "<e>") -} - -pub fn render_help_lines( - sequence: &State, - max_width: Option<usize>, -) -> (Text, usize, Option<(Text, usize)>) { - let help_text = select_help_text(sequence); - - if let Some(width) = max_width { - if let Some((first_line, second_line)) = split_help_line(help_text, width) { - let first_len = first_line.chars().count(); - let first_styled = style_help_text(&first_line).unbold_all(); - - let second_line_width = second_line.chars().count(); - let (second_styled, second_len) = if second_line_width > width { - let (truncated, len) = truncate_help_line(&second_line, width); - (style_help_text(&truncated).unbold_all(), len) - } else { - ( - style_help_text(&second_line).unbold_all(), - second_line_width, - ) - }; - - return (first_styled, first_len, Some((second_styled, second_len))); - } - - let (truncated_text, help_len) = truncate_help_line(help_text, width); - ( - style_help_text(&truncated_text).unbold_all(), - help_len, - None, - ) - } else { - let help_len = help_text.chars().count(); - (style_help_text(help_text).unbold_all(), help_len, None) - } -} - -/// Returns `(text, x, y)` tuples for the main screen intro hint. -/// The text is centered horizontally; if it does not fit on one line, two lines -/// are returned separated by one blank line (1-line padding). -pub fn render_intro_hint(rows: usize, cols: usize) -> Vec<(Text, usize, usize)> { - let full_len = INTRO_FULL.chars().count(); - - if full_len <= cols { - let x = cols.saturating_sub(full_len) / 2; - let y = rows / 2; - vec![(Text::new(INTRO_FULL).color_substring(3, "<e>"), x, y)] - } else { - let p1_len = INTRO_PART1.chars().count(); - let p2_len = INTRO_PART2.chars().count(); - let x1 = cols.saturating_sub(p1_len) / 2; - let x2 = cols.saturating_sub(p2_len) / 2; - // 3 display rows: line1, padding, line2 — center the group vertically - let start_y = rows.saturating_sub(3) / 2; - vec![ - ( - Text::new(INTRO_PART1).color_substring(3, "<e>"), - x1, - start_y, - ), - ( - Text::new(INTRO_PART2).color_substring(3, "<e>"), - x2, - start_y + 2, - ), - ] - } -} - -fn split_help_line(help_text: &str, max_width: usize) -> Option<(String, String)> { - let text_width = help_text.chars().count(); - - if text_width <= max_width { - return None; - } - - let keybindings = parse_help_keybindings(help_text); - - if keybindings.is_empty() { - return None; - } - - for split_point in 1..keybindings.len() { - let first_part: Vec<String> = keybindings - .iter() - .take(split_point) - .map(|(key, _, action)| format!("{} - {}", key, action)) - .collect(); - let first_line = first_part.join(", "); - - let second_part: Vec<String> = keybindings - .iter() - .skip(split_point) - .map(|(key, _, action)| format!("{} - {}", key, action)) - .collect(); - let second_line = second_part.join(", "); - - if first_line.chars().count() <= max_width && second_line.chars().count() <= max_width { - return Some((first_line, second_line)); - } - } - - None -} - -fn folder_cell(cwd: &Option<PathBuf>) -> (Text, String) { - let cwd_display = if let Some(cwd) = cwd { - format_cwd(cwd) - } else { - "~".to_string() - }; - let folder_display = format!("{} >", cwd_display); - let text = Text::new(&folder_display).color_range(0, 0..cwd_display.len()); - (text, folder_display) -} - -fn command_cell(text: &str) -> Text { - let text = if text.is_empty() { " " } else { text }; - Text::new(text) -} - -fn chain_cell(chain_type: &crate::state::ChainType) -> Text { - let text = chain_type_to_str(chain_type); - if !text.is_empty() { - Text::new(text).color_range(1, 0..text.len()) - } else { - Text::new(text) - } -} - -pub fn get_spinner_frame(frame: usize) -> &'static str { - SPINNER_FRAMES[frame % SPINNER_FRAMES.len()] -} - -pub fn format_status_text(status: &CommandStatus, spinner_frame: usize) -> String { - match status { - CommandStatus::Running(_) => { - let spinner = get_spinner_frame(spinner_frame); - format!("[RUNNING] {}", spinner) - }, - CommandStatus::Interrupted(_) => "[INTERRUPTED]".to_string(), - CommandStatus::Exited(Some(code), _) => format!("[EXIT CODE: {}]", code), - CommandStatus::Exited(None, _) => "[EXITED]".to_string(), - CommandStatus::Pending => " ".to_string(), - } -} - -fn apply_status_color(text: Text, status: &CommandStatus, status_str: &str) -> Text { - match status { - CommandStatus::Running(_) => text.color_range(3, 0..status_str.len()), - CommandStatus::Interrupted(_) => text.error_color_range(0..status_str.len()), - CommandStatus::Exited(Some(0), _) => { - let number_start = 12; - let number_end = status_str.len() - 1; - text.success_color_range(number_start..number_end) - }, - CommandStatus::Exited(Some(_), _) => { - let number_start = 12; - let number_end = status_str.len() - 1; - text.error_color_range(number_start..number_end) - }, - _ => text, - } -} - -fn status_cell(status: &CommandStatus, spinner_frame: usize) -> Text { - let status_str = format_status_text(status, spinner_frame); - let text = Text::new(&status_str); - apply_status_color(text, status, &status_str) -} - -fn apply_row_styles(cells: Vec<Text>, is_selected: bool) -> Vec<Text> { - let mut styled_cells = cells; - - if is_selected { - styled_cells = styled_cells.into_iter().map(|c| c.selected()).collect(); - } - - styled_cells -} - -fn overflow_cell(indicator: &str, is_selected: bool) -> Text { - let mut text = Text::new(indicator).dim_all(); - if is_selected { - text = text.selected(); - } - text -} - -fn command_sequence_row( - state: &State, - cwd: &Option<PathBuf>, - truncated_cmd_text: &str, - chain_type: &crate::state::ChainType, - status: &crate::state::CommandStatus, - is_selected: bool, - overflow_indicator: Option<String>, -) -> (Vec<Text>, usize) { - let (folder_text, folder_display) = folder_cell(cwd); - - let mut cells = vec![ - folder_text, - command_cell(truncated_cmd_text), - chain_cell(chain_type), - status_cell(status, state.layout.spinner_frame), - ]; - - cells = apply_row_styles(cells, is_selected); - - let truncated_cmd_text = if truncated_cmd_text.is_empty() { - " " - } else { - truncated_cmd_text - }; - let mut row_length = folder_display.chars().count() - + truncated_cmd_text.chars().count() - + chain_type_to_str(chain_type).chars().count() - + format_status_text(status, state.layout.spinner_frame) - .chars() - .count() - + 3; - - if let Some(indicator) = overflow_indicator { - let indicator_len = indicator.chars().count(); - cells.push(overflow_cell(&indicator, is_selected)); - row_length += indicator_len + 1; - } - - (cells, row_length) -} - -pub fn build_table_header(has_overflow: bool) -> Table { - let header_cols = if has_overflow { - vec![" ", " ", " ", " ", " "] - } else { - vec![" ", " ", " ", " "] - }; - Table::new().add_row(header_cols) -} - -fn calculate_overflow_indicator( - visible_idx: usize, - visible_count: usize, - hidden_above: usize, - hidden_below: usize, -) -> Option<String> { - if visible_idx == 0 && hidden_above > 0 { - Some(format!("[+{}]", hidden_above)) - } else if visible_idx == visible_count.saturating_sub(1) && hidden_below > 0 { - Some(format!("[+{}]", hidden_below)) - } else if hidden_above > 0 || hidden_below > 0 { - Some(format!(" ")) - } else { - None - } -} - -pub fn calculate_row_layout_info( - index: usize, - offset: usize, - visible_count: usize, - hidden_above: usize, - hidden_below: usize, - cols: usize, - folder_width: usize, - max_chain_width: usize, - max_status_width: usize, -) -> Option<(Option<String>, usize)> { - if index < offset || index >= offset + visible_count { - return None; - } - - let visible_idx = index.saturating_sub(offset); - let overflow_indicator = - calculate_overflow_indicator(visible_idx, visible_count, hidden_above, hidden_below); - - let available_cmd_width = calculate_available_cmd_width( - cols, - folder_width, - overflow_indicator.as_ref(), - max_chain_width, - max_status_width, - ); - - Some((overflow_indicator, available_cmd_width)) -} - -pub fn add_command_row( - table: Table, - state: &State, - index: usize, - offset: usize, - visible_count: usize, - hidden_above: usize, - hidden_below: usize, -) -> Table { - let command = match state.execution.all_commands.get(index) { - Some(cmd) => cmd, - None => return table, - }; - - let cmd_text = command.get_text(); - let cmd_text = cmd_text.trim(); - let chain_type = &command.get_chain_type(); - let status = &command.get_status(); - let command_cwd = command.get_cwd().or_else(|| state.cwd.clone()); - - let cols = state.own_columns.unwrap_or(80); - let longest_cwd_display = state.execution.longest_cwd_display(&state.cwd); - let folder_display = format!("{} >", longest_cwd_display); - let folder_width = folder_display.chars().count(); - - let (max_chain_width, max_status_width) = - calculate_max_widths(&state.execution.all_commands, state.layout.spinner_frame); - - let Some((overflow_indicator, available_cmd_width)) = calculate_row_layout_info( - index, - offset, - visible_count, - hidden_above, - hidden_below, - cols, - folder_width, - max_chain_width, - max_status_width, - ) else { - return table; - }; - - let truncated_cmd_text = truncate_middle(cmd_text, available_cmd_width, None).0; - - let (row, _) = command_sequence_row( - state, - &command_cwd, - &truncated_cmd_text, - chain_type, - status, - state.selection.current_selected_command_index == Some(index), - overflow_indicator, - ); - - table.add_styled_row(row) -} - -pub fn calculate_max_widths( - commands: &[crate::state::CommandEntry], - spinner_frame: usize, -) -> (usize, usize) { - let max_chain_width = commands - .iter() - .map(|cmd| chain_type_to_str(&cmd.get_chain_type()).chars().count()) - .max() - .unwrap_or(0); - - let max_status_width = commands - .iter() - .map(|cmd| { - format_status_text(&cmd.get_status(), spinner_frame) - .chars() - .count() - }) - .max() - .unwrap_or(0); - - (max_chain_width, max_status_width) -} - -pub fn calculate_longest_line( - longest_cwd_display: &str, - longest_command: usize, - max_chain_width: usize, - max_status_width: usize, -) -> usize { - let folder_display = format!("{} >", longest_cwd_display); - let cell_padding = 3; - folder_display.chars().count() - + longest_command - + max_chain_width - + max_status_width - + cell_padding -} - -pub fn chain_type_to_str(chain_type: &crate::state::ChainType) -> &'static str { - match chain_type { - crate::state::ChainType::And => "AND", - crate::state::ChainType::Or => "OR", - crate::state::ChainType::Then => "THEN", - crate::state::ChainType::None => " ", - } -} - -pub fn truncate_help_line(help_text: &str, max_width: usize) -> (String, usize) { - let text_width = help_text.chars().count(); - - if text_width <= max_width { - return (help_text.to_string(), text_width); - } - - let keybindings = parse_help_keybindings(help_text); - - if let Some(result) = try_shortened_help(&keybindings, max_width) { - return result; - } - - if let Some(result) = try_keys_spaced(&keybindings, max_width) { - return result; - } - - if let Some(result) = try_keys_tight(&keybindings, max_width) { - return result; - } - - truncate_with_ellipsis( - &keybindings - .iter() - .map(|(k, _, _)| *k) - .collect::<Vec<_>>() - .join("/"), - max_width, - ) -} - -fn parse_help_keybindings(help_text: &str) -> Vec<(&str, &str, &str)> { - help_text - .split(", ") - .filter_map(|part| { - part.find(" - ").map(|dash_pos| { - let key = &part[..dash_pos]; - let action = &part[dash_pos + 3..]; - let first_word = action.split_whitespace().next().unwrap_or(action); - (key, first_word, action) - }) - }) - .collect() -} - -fn try_shortened_help( - keybindings: &[(&str, &str, &str)], - max_width: usize, -) -> Option<(String, usize)> { - let shortened: Vec<String> = keybindings - .iter() - .map(|(key, first_word, _)| format!("{} - {}", key, first_word)) - .collect(); - let shortened_text = shortened.join(", "); - let shortened_width = shortened_text.chars().count(); - - if shortened_width <= max_width { - Some((shortened_text, shortened_width)) - } else { - None - } -} - -fn try_keys_spaced( - keybindings: &[(&str, &str, &str)], - max_width: usize, -) -> Option<(String, usize)> { - let keys_spaced_text = keybindings - .iter() - .map(|(key, _, _)| *key) - .collect::<Vec<_>>() - .join(" / "); - let keys_spaced_width = keys_spaced_text.chars().count(); - - if keys_spaced_width <= max_width { - Some((keys_spaced_text, keys_spaced_width)) - } else { - None - } -} - -fn try_keys_tight(keybindings: &[(&str, &str, &str)], max_width: usize) -> Option<(String, usize)> { - let keys_tight_text = keybindings - .iter() - .map(|(key, _, _)| *key) - .collect::<Vec<_>>() - .join("/"); - let keys_tight_width = keys_tight_text.chars().count(); - - if keys_tight_width <= max_width { - Some((keys_tight_text, keys_tight_width)) - } else { - None - } -} - -fn truncate_with_ellipsis(text: &str, max_width: usize) -> (String, usize) { - let available = max_width.saturating_sub(3); - let mut truncated = String::new(); - let mut current_width = 0; - - for ch in text.chars() { - if current_width + 1 <= available { - truncated.push(ch); - current_width += 1; - } else { - break; - } - } - - truncated.push_str("..."); - let truncated_width = truncated.chars().count(); - (truncated, truncated_width) -} diff --git a/default-plugins/sequence/src/ui/layout_calculations.rs b/default-plugins/sequence/src/ui/layout_calculations.rs deleted file mode 100644 index 911dbc62..00000000 --- a/default-plugins/sequence/src/ui/layout_calculations.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub fn calculate_viewport( - total_commands: usize, - max_visible: usize, - selected_index: Option<usize>, - running_index: usize, -) -> (usize, usize, usize, usize) { - if total_commands <= max_visible { - return (0, total_commands, 0, 0); - } - - let focus_index = selected_index.unwrap_or(running_index); - let half_visible = max_visible / 2; - - let offset = if focus_index < half_visible { - 0 - } else if focus_index >= total_commands.saturating_sub(half_visible) { - total_commands.saturating_sub(max_visible) - } else { - focus_index.saturating_sub(half_visible) - }; - - let offset = offset.min(total_commands.saturating_sub(max_visible)); - let visible_count = max_visible.min(total_commands.saturating_sub(offset)); - let hidden_above = offset; - let hidden_below = total_commands.saturating_sub(offset + visible_count); - - (offset, visible_count, hidden_above, hidden_below) -} diff --git a/default-plugins/sequence/src/ui/mod.rs b/default-plugins/sequence/src/ui/mod.rs deleted file mode 100644 index 7255fa35..00000000 --- a/default-plugins/sequence/src/ui/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod components; -pub mod layout_calculations; -pub mod truncation; diff --git a/default-plugins/sequence/src/ui/truncation.rs b/default-plugins/sequence/src/ui/truncation.rs deleted file mode 100644 index 0c4073a3..00000000 --- a/default-plugins/sequence/src/ui/truncation.rs +++ /dev/null @@ -1,373 +0,0 @@ -use unicode_width::UnicodeWidthStr; - -const BASE_SEPARATOR_WIDTH: usize = 2; - -pub fn calculate_available_cmd_width( - cols: usize, - folder_width: usize, - overflow_indicator: Option<&String>, - chain_width: usize, - status_width: usize, -) -> usize { - let mut separator_width = BASE_SEPARATOR_WIDTH; - if overflow_indicator.is_some() { - separator_width += 1; - } - if status_width > 0 { - separator_width += 1; - } - - let overflow_width = overflow_indicator.map(|s| s.chars().count()).unwrap_or(0); - - cols.saturating_sub(folder_width) - .saturating_sub(chain_width) - .saturating_sub(status_width) - .saturating_sub(separator_width) - .saturating_sub(overflow_width) - .saturating_sub(2) - .max(1) -} - -pub fn truncate_middle( - text: &str, - max_width: usize, - cursor_position: Option<usize>, -) -> (String, Option<usize>) { - let text_width = text.width(); - - if text_width <= max_width { - return (text.to_string(), cursor_position); - } - - if max_width < 5 { - return truncate_minimal(text, max_width, cursor_position); - } - - if let Some(cursor_char_idx) = cursor_position { - return truncate_with_cursor(text, max_width, cursor_char_idx); - } - - truncate_no_cursor(text, max_width) -} - -fn truncate_minimal( - text: &str, - max_width: usize, - cursor_position: Option<usize>, -) -> (String, Option<usize>) { - let mut result = String::new(); - let mut current_width = 0; - let mut new_cursor_pos = None; - let mut char_pos = 0; - - for ch in text.chars() { - let ch_width = ch.to_string().width(); - if current_width + ch_width <= max_width { - result.push(ch); - if let Some(cursor_char) = cursor_position { - if char_pos == cursor_char { - new_cursor_pos = Some(char_pos); - } - } - char_pos += 1; - current_width += ch_width; - } else { - break; - } - } - (result, new_cursor_pos) -} - -fn truncate_no_cursor(text: &str, max_width: usize) -> (String, Option<usize>) { - let available_for_text = max_width.saturating_sub(3); - let left_width = available_for_text / 2; - let right_width = available_for_text - left_width; - - let mut left_part = String::new(); - let mut current_width = 0; - for ch in text.chars() { - let ch_width = ch.to_string().width(); - if current_width + ch_width <= left_width { - left_part.push(ch); - current_width += ch_width; - } else { - break; - } - } - - let chars: Vec<char> = text.chars().collect(); - let mut right_part = String::new(); - let mut current_width = 0; - for ch in chars.iter().rev() { - let ch_width = ch.to_string().width(); - if current_width + ch_width <= right_width { - right_part.insert(0, *ch); - current_width += ch_width; - } else { - break; - } - } - - (format!("{}...{}", left_part, right_part), None) -} - -fn truncate_with_cursor( - text: &str, - max_width: usize, - cursor_char_idx: usize, -) -> (String, Option<usize>) { - let chars: Vec<char> = text.chars().collect(); - let char_widths: Vec<usize> = chars.iter().map(|ch| ch.to_string().width()).collect(); - - let width_before_cursor: usize = char_widths[..cursor_char_idx].iter().sum(); - let width_after_cursor: usize = char_widths[cursor_char_idx..].iter().sum(); - let available_one_ellipsis = max_width.saturating_sub(3); - - let (start_idx, end_idx) = if width_before_cursor <= available_one_ellipsis { - calculate_end_truncation(&chars, &char_widths, available_one_ellipsis) - } else if width_after_cursor <= available_one_ellipsis { - calculate_start_truncation(&chars, &char_widths, available_one_ellipsis) - } else { - calculate_middle_truncation(&char_widths, max_width, cursor_char_idx) - }; - - let (start_idx, end_idx) = - adjust_small_truncations(start_idx, end_idx, &char_widths, cursor_char_idx, &chars); - let (start_idx, end_idx) = - trim_excess(&char_widths, start_idx, end_idx, max_width, cursor_char_idx); - - build_truncated_result(&chars, start_idx, end_idx, cursor_char_idx) -} - -fn calculate_end_truncation( - chars: &[char], - char_widths: &[usize], - available: usize, -) -> (usize, usize) { - let mut end_idx = 0; - let mut width = 0; - while end_idx < chars.len() && width + char_widths[end_idx] <= available { - width += char_widths[end_idx]; - end_idx += 1; - } - (0, end_idx) -} - -fn calculate_start_truncation( - chars: &[char], - char_widths: &[usize], - available: usize, -) -> (usize, usize) { - let mut start_idx = chars.len(); - let mut width = 0; - while start_idx > 0 && width + char_widths[start_idx - 1] <= available { - start_idx -= 1; - width += char_widths[start_idx]; - } - (start_idx, chars.len()) -} - -fn calculate_middle_truncation( - char_widths: &[usize], - max_width: usize, - cursor_char_idx: usize, -) -> (usize, usize) { - let available_both_ellipsis = max_width.saturating_sub(6); - let target_before = available_both_ellipsis / 2; - let target_after = available_both_ellipsis - target_before; - - let mut start_idx = cursor_char_idx; - let mut width_before = 0; - while start_idx > 0 && width_before + char_widths[start_idx - 1] <= target_before { - start_idx -= 1; - width_before += char_widths[start_idx]; - } - - let mut end_idx = cursor_char_idx; - let mut width_after = 0; - while end_idx < char_widths.len() && width_after + char_widths[end_idx] <= target_after { - width_after += char_widths[end_idx]; - end_idx += 1; - } - - let leftover_before = target_before.saturating_sub(width_before); - let leftover_after = target_after.saturating_sub(width_after); - - if leftover_before > 0 { - let mut extra = leftover_before; - while end_idx < char_widths.len() && char_widths[end_idx] <= extra { - extra -= char_widths[end_idx]; - end_idx += 1; - } - } - if leftover_after > 0 { - let mut extra = leftover_after; - while start_idx > 0 && char_widths[start_idx - 1] <= extra { - start_idx -= 1; - extra -= char_widths[start_idx]; - } - } - - (start_idx, end_idx) -} - -fn adjust_small_truncations( - start_idx: usize, - end_idx: usize, - char_widths: &[usize], - cursor_char_idx: usize, - chars: &[char], -) -> (usize, usize) { - let width_truncated_start: usize = char_widths[..start_idx].iter().sum(); - let width_truncated_end: usize = char_widths[end_idx..].iter().sum(); - - let mut start_idx = start_idx; - let mut end_idx = end_idx; - - if width_truncated_start > 0 && width_truncated_start < 3 && end_idx < chars.len() { - let gained = width_truncated_start; - start_idx = 0; - let mut removed = 0; - while end_idx > cursor_char_idx + 1 && removed < gained { - end_idx -= 1; - removed += char_widths[end_idx]; - } - } else if width_truncated_end > 0 && width_truncated_end < 3 && start_idx > 0 { - let gained = width_truncated_end; - end_idx = chars.len(); - let mut removed = 0; - while start_idx < cursor_char_idx && removed < gained { - removed += char_widths[start_idx]; - start_idx += 1; - } - } - - (start_idx, end_idx) -} - -fn trim_excess( - char_widths: &[usize], - start_idx: usize, - end_idx: usize, - max_width: usize, - cursor_char_idx: usize, -) -> (usize, usize) { - let truncate_start = start_idx > 0; - let truncate_end = end_idx < char_widths.len(); - let ellipsis_width = match (truncate_start, truncate_end) { - (true, true) => 6, - (true, false) | (false, true) => 3, - (false, false) => 0, - }; - - let visible_width: usize = char_widths[start_idx..end_idx].iter().sum(); - let mut start_idx = start_idx; - let mut end_idx = end_idx; - - if visible_width + ellipsis_width > max_width { - let mut excess = visible_width + ellipsis_width - max_width; - if truncate_end { - while excess > 0 && end_idx > cursor_char_idx + 1 { - end_idx -= 1; - excess = excess.saturating_sub(char_widths[end_idx]); - } - } - if excess > 0 && truncate_start { - while excess > 0 && start_idx < cursor_char_idx { - excess = excess.saturating_sub(char_widths[start_idx]); - start_idx += 1; - } - } - } - - (start_idx, end_idx) -} - -fn build_truncated_result( - chars: &[char], - start_idx: usize, - end_idx: usize, - cursor_char_idx: usize, -) -> (String, Option<usize>) { - let mut result = String::new(); - let mut new_cursor_char_pos = 0; - let mut current_char_pos = 0; - - if start_idx > 0 { - result.push_str("..."); - current_char_pos = 3; - } - - for i in start_idx..end_idx { - if i == cursor_char_idx { - new_cursor_char_pos = current_char_pos; - } - result.push(chars[i]); - current_char_pos += 1; - } - - if cursor_char_idx >= end_idx { - new_cursor_char_pos = current_char_pos; - } - - if end_idx < chars.len() { - result.push_str("..."); - } - - (result, Some(new_cursor_char_pos)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_truncate_middle_no_truncation_needed() { - let text = "hello world"; - let (result, cursor_pos) = truncate_middle(text, 20, Some(6)); - assert_eq!(result, "hello world"); - assert_eq!(cursor_pos, Some(6)); - } - - #[test] - fn test_truncate_middle_no_cursor() { - let text = "this is a very long string that needs truncation"; - let (result, cursor_pos) = truncate_middle(text, 20, None); - assert!(result.contains("...")); - assert_eq!(cursor_pos, None); - } - - #[test] - fn test_truncate_middle_cursor_at_start() { - let text = "this is a very long string that needs truncation"; - let (result, cursor_pos) = truncate_middle(text, 20, Some(0)); - assert!(result.starts_with("this")); - assert!(result.ends_with("...")); - assert_eq!(cursor_pos, Some(0)); - } - - #[test] - fn test_truncate_middle_cursor_at_end() { - let text = "this is a very long string that needs truncation"; - let cursor_char = text.chars().count(); - let (result, _cursor_pos) = truncate_middle(text, 20, Some(cursor_char)); - assert!(result.starts_with("...")); - assert!(result.ends_with("truncation")); - } - - #[test] - fn test_truncate_middle_cursor_in_middle() { - let text = "this is a very long string that needs truncation"; - let cursor_char = "this is a very ".chars().count(); - let (result, cursor_pos) = truncate_middle(text, 20, Some(cursor_char)); - assert!(result.contains("very")); - assert!(cursor_pos.is_some()); - } - - #[test] - fn test_truncate_middle_wide_chars() { - let text = "こんにちは世界"; - let (result, _) = truncate_middle(text, 10, None); - assert!(result.width() <= 10); - } -} diff --git a/example/default.kdl b/example/default.kdl index 59e70eb7..ebe7f29c 100644 --- a/example/default.kdl +++ b/example/default.kdl @@ -242,7 +242,6 @@ plugins { configuration location="zellij:configuration" plugin-manager location="zellij:plugin-manager" about location="zellij:about" - sequence location="zellij:sequence" } // Plugins to load in the background when a new session starts diff --git a/src/main.rs b/src/main.rs index 48fcf67e..f7fe74e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -216,33 +216,6 @@ fn main() { commands::send_action_to_session(command_cli_action, opts.session, config); std::process::exit(0); } - if let Some(Command::Sessions(Sessions::Sequence { payload, blocking })) = opts.command { - let args = if blocking { - let mut args: std::collections::BTreeMap<String, String> = - std::collections::BTreeMap::new(); - args.insert("blocking".to_string(), "true".to_string()); - Some(zellij_utils::input::layout::PluginUserConfiguration::new( - args, - )) - } else { - None - }; - let command_cli_action = CliAction::Pipe { - name: None, - payload, - args, - plugin: Some("zellij:sequence".to_string()), - plugin_configuration: None, - force_launch_plugin: true, - skip_plugin_cache: false, - floating_plugin: None, - in_place_plugin: None, - plugin_cwd: None, - plugin_title: None, - }; - commands::send_action_to_session(command_cli_action, opts.session, config); - std::process::exit(0); - } } if let Some(Command::Sessions(Sessions::ListSessions { diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 96fb9c12..4def0981 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -1,6 +1,6 @@ #![allow(unused)] -use ::insta::assert_snapshot; +use insta::assert_snapshot; use zellij_utils::{ pane_size::Size, position::{Column, Line, Position}, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 96a50e3f..d78d9433 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -79,10 +79,6 @@ fn workspace_members() -> &'static Vec<WorkspaceMember> { build: true, }, WorkspaceMember { - crate_name: "default-plugins/sequence", - build: true, - }, - WorkspaceMember { crate_name: "default-plugins/layout-manager", build: true, }, diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 50a68e07..8c6cb627 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -150,12 +150,6 @@ keybinds { }; SwitchToMode "Normal" } - bind "q" { - LaunchPlugin "zellij:sequence" { - floating true - }; - SwitchToMode "Normal" - } bind "l" { LaunchOrFocusPlugin "zellij:layout-manager" { floating true @@ -248,7 +242,6 @@ plugins { configuration location="zellij:configuration" plugin-manager location="zellij:plugin-manager" about location="zellij:about" - sequence location="zellij:sequence" } // Plugins to load in the background when a new session starts diff --git a/zellij-utils/assets/plugins/sequence.wasm b/zellij-utils/assets/plugins/sequence.wasm Binary files differdeleted file mode 100755 index b2e470a1..00000000 --- a/zellij-utils/assets/plugins/sequence.wasm +++ /dev/null diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index afe198b8..7b64e147 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -630,34 +630,6 @@ tail -f /tmp/my-live-logfile | zellij pipe --name logs --plugin https://example. #[clap(short('c'), long, value_parser, display_order(4))] plugin_configuration: Option<PluginUserConfiguration>, }, - /// Send commands to the sequence plugin (sugar for: zellij pipe --plugin zellij:sequence) - #[clap( - visible_alias = "seq", - override_usage( - r#" -zellij sequence [OPTIONS] [--] <COMMANDS> - -* Run a sequence of commands: - -zellij sequence -- 'echo hello && echo world' - -* Pipe commands from STDIN: - -echo 'echo hello && echo world' | zellij sequence - -* Wait for the sequence to complete before returning: - -zellij sequence --blocking -- 'echo hello && echo world' -"# - ) - )] - Sequence { - /// The commands to run (if blank, will listen to STDIN) - payload: Option<String>, - /// Block until the sequence finishes before exiting - #[clap(short, long, value_parser, takes_value(false), default_value("false"))] - blocking: bool, - }, } #[derive(Debug, Subcommand, Clone, Serialize, Deserialize)] diff --git a/zellij-utils/src/consts.rs b/zellij-utils/src/consts.rs index ef28b9a9..12cc5c2a 100644 --- a/zellij-utils/src/consts.rs +++ b/zellij-utils/src/consts.rs @@ -138,7 +138,6 @@ mod not_wasm { add_plugin!(assets, "about.wasm"); add_plugin!(assets, "share.wasm"); add_plugin!(assets, "multiple-select.wasm"); - add_plugin!(assets, "sequence.wasm"); add_plugin!(assets, "layout-manager.wasm"); assets }; diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index f2e2c1e6..69af52f9 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -67,7 +67,6 @@ impl PluginConfig { || tag == "about" || tag == "share" || tag == "multiple-select" - || tag == "sequence" || tag == "layout-manager" { Some(PluginConfig { diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap index 9fa94e0b..bc5feeff 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap @@ -129,12 +129,6 @@ keybinds clear-defaults=true { } SwitchToMode "normal" } - bind "q" { - LaunchPlugin "zellij:sequence" { - floating true - } - SwitchToMode "normal" - } bind "s" { LaunchOrFocusPlugin "zellij:share" { floating true @@ -268,7 +262,6 @@ plugins { cwd "/" } plugin-manager location="zellij:plugin-manager" - sequence location="zellij:sequence" session-manager location="zellij:session-manager" status-bar location="zellij:status-bar" strider location="zellij:strider" diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap index 3d9cd208..26a1be67 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap @@ -129,12 +129,6 @@ keybinds clear-defaults=true { } SwitchToMode "normal" } - bind "q" { - LaunchPlugin "zellij:sequence" { - floating true - } - SwitchToMode "normal" - } bind "s" { LaunchOrFocusPlugin "zellij:share" { floating true @@ -271,7 +265,6 @@ plugins { cwd "/" } plugin-manager location="zellij:plugin-manager" - sequence location="zellij:sequence" session-manager location="zellij:session-manager" status-bar location="zellij:status-bar" strider location="zellij:strider" diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 4bfd7dc2..d794c05f 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -4483,37 +4483,6 @@ Config { bare_key: Char( 'q', ), - key_modifiers: {}, - }: [ - LaunchPlugin { - plugin: RunPlugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, - ), - should_float: true, - should_open_in_place: false, - close_replaced_pane: false, - skip_cache: false, - cwd: None, - }, - SwitchToMode { - input_mode: Normal, - }, - ], - KeyWithModifier { - bare_key: Char( - 'q', - ), key_modifiers: { Ctrl, }, @@ -6111,18 +6080,6 @@ Config { ), initial_cwd: None, }, - "sequence": RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, "session-manager": RunPlugin { _allow_exec_host_cmd: false, location: Zellij( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 9b47705b..4151c52a 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -4483,37 +4483,6 @@ Config { bare_key: Char( 'q', ), - key_modifiers: {}, - }: [ - LaunchPlugin { - plugin: RunPlugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, - ), - should_float: true, - should_open_in_place: false, - close_replaced_pane: false, - skip_cache: false, - cwd: None, - }, - SwitchToMode { - input_mode: Normal, - }, - ], - KeyWithModifier { - bare_key: Char( - 'q', - ), key_modifiers: { Ctrl, }, @@ -6111,18 +6080,6 @@ Config { ), initial_cwd: None, }, - "sequence": RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, "session-manager": RunPlugin { _allow_exec_host_cmd: false, location: Zellij( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap index b9be4e99..eb5db32b 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -198,18 +198,6 @@ Config { ), initial_cwd: None, }, - "sequence": RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, "session-manager": RunPlugin { _allow_exec_host_cmd: false, location: Zellij( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index a9e97f93..c7e71adf 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -4483,37 +4483,6 @@ Config { bare_key: Char( 'q', ), - key_modifiers: {}, - }: [ - LaunchPlugin { - plugin: RunPlugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, - ), - should_float: true, - should_open_in_place: false, - close_replaced_pane: false, - skip_cache: false, - cwd: None, - }, - SwitchToMode { - input_mode: Normal, - }, - ], - KeyWithModifier { - bare_key: Char( - 'q', - ), key_modifiers: { Ctrl, }, @@ -7882,18 +7851,6 @@ Config { ), initial_cwd: None, }, - "sequence": RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, "session-manager": RunPlugin { _allow_exec_host_cmd: false, location: Zellij( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index f124cb6a..b5211847 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -4483,37 +4483,6 @@ Config { bare_key: Char( 'q', ), - key_modifiers: {}, - }: [ - LaunchPlugin { - plugin: RunPlugin( - RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, - ), - should_float: true, - should_open_in_place: false, - close_replaced_pane: false, - skip_cache: false, - cwd: None, - }, - SwitchToMode { - input_mode: Normal, - }, - ], - KeyWithModifier { - bare_key: Char( - 'q', - ), key_modifiers: { Ctrl, }, @@ -6111,18 +6080,6 @@ Config { ), initial_cwd: None, }, - "sequence": RunPlugin { - _allow_exec_host_cmd: false, - location: Zellij( - PluginTag( - "sequence", - ), - ), - configuration: PluginUserConfiguration( - {}, - ), - initial_cwd: None, - }, "session-manager": RunPlugin { _allow_exec_host_cmd: false, location: Zellij( |
