diff options
| author | Aram Drevekenin <aram@poor.dev> | 2025-12-11 14:25:42 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-11 14:25:42 +0100 |
| commit | 693fa082def5ef0fe030cd7fdc3b1df051b35dc6 (patch) | |
| tree | b3f651462bbc1cd2fa5b2d79207343413ef6b83c /default-plugins/sequence/src/state/selection.rs | |
| parent | fix(zellij-server): unblock building on 32bit targets (#4484) (diff) | |
| download | sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.gz sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.xz sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.zip | |
Command Sequences, Blocking CLI Commands, New Plugin APIs (#4546)
* fix deadlocks
* initial logging to plugin implementation
* only log actions, include keys as Action::Write
* send actions to plugins as a new Event::UserAction
* add Action::Write metadata to plugin API
* run_action plugin command and permission
* block-until-exit/success/failure in the cli
* fix tests
* add NewBlockingPane to the plugin API
* run actions in a different thread so as not to block plugin execution
* add custom context to run_action flow
* fixes for floating pane z_index sorting and unknown protobuf events not crashing plugins
* always render the frame of pinned panes
* macros module
* add macros to config
* KDL macro parsing
* thread macros to the plugin thread
* get/update/delete macros from plugins
* add macros updated event
* add ability to rename macros
* fix deadlock
* fix z_indices with pinned unselectable panes bug
* allow plugins to opt-in to show_cursor
* change show_cursor(bool) to show_cursor(Option<coordinates>)
* test
* upgrade strum to allow proper enum iteration and case insensitive fromstr implementation for Actions
* better cwd tracking for exited command panes
* add initial panes to NewTab Action (no-op for now)
* add initial panes to cli newtab action
* make initial panes work
* add close_on_exit and start_suspended to the cli
* properly convert newtab action (potentially breaking for plugins)
* allow first pane in tab to block
* make distinction between opening panes with the last active client and the current pane in the cli
* improve some apis
* fix fixed size closing pane bug
* fix plugin cursor coordinates
* fix issues with launch-or-focus-plugin having to do with in-place and suppressed panes
* fix: set affected pane in replace pane
* allow setting a pane in place when it is suppressed and being focused (tests failing)
* fix tests
* some fixes for unsuppression of panes
* some fixes for completion_tx in new tab action
* allow replace/suppressed rather than replace/close only
* fixes for inserting suppressed panes without conflicts
* pane focus history and explicit unsuppress plugin api
* fix: prefer explicitly provided pane id in placement in actions that provide it
* fix floating pane coordinate change and z_index
* fix plugin cache dir to persist between sessions
* fix some stack focusing issues when adding panes without a client id
* make sure pinned panes are always above non-pinned floating panes
* allow plugins to copy to clipboard
* initial text styling additions implementations - buggy
* fix dimming issues
* remove extras, stay with just dim and unbold
* unbold and dim as indices
* allow ui components to render success/error colors
* allow plugins to send sigint/sigkill to processes, as well as receive their pid
* ui component fixes and send originating plugin context with run_action
* originating plugin for new tab too
* make plugn show pane command also expand stacked panes
* add sequence plugin
* remove macros
* remove snapshots
* add shortcut for sequence plugin
* fix plugin tests
* plugin tests for new apis
* update snapshots for sequence shortcut
* cleanups and formatting
* Event::CwdChanged
* moar cleanups
* remove some warnings
* remove mroe warnings
* remove dead code
* remove more dead code
* fix minor bugs in sequence plugin
* update sequence help and format codebase
* fix conditional blocking cli commands
* fix initial-command new-tab cli flag
* add client_ids to CwdChanged Event
* last fixes
* add pr
Diffstat (limited to 'default-plugins/sequence/src/state/selection.rs')
| -rw-r--r-- | default-plugins/sequence/src/state/selection.rs | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/default-plugins/sequence/src/state/selection.rs b/default-plugins/sequence/src/state/selection.rs new file mode 100644 index 00000000..18f83bcd --- /dev/null +++ b/default-plugins/sequence/src/state/selection.rs @@ -0,0 +1,214 @@ +use crate::state::{CommandEntry, Editing}; +use std::path::PathBuf; +use zellij_tile::prelude::*; + +pub struct Selection { + pub current_selected_command_index: Option<usize>, + pub scroll_offset: usize, +} + +impl Selection { + pub fn new() -> Self { + Self { + current_selected_command_index: Some(0), + scroll_offset: 0, + } + } + + pub fn add_empty_command_after_current_selected( + &mut self, + all_commands: &mut Vec<CommandEntry>, + editing: &mut Editing, + cwd: &Option<PathBuf>, + ) { + self.save_editing_buffer_to_current_selected(all_commands, editing); + editing.editing_input.as_mut().map(|i| i.clear()); + if !self.current_selected_command_is_empty(all_commands, editing) { + self.current_selected_command_mut(all_commands) + .map(|c| c.fill_chain_type_if_empty()); + + if let Some(current_selected_command_index) = self.current_selected_command_index.take() + { + let new_command = CommandEntry::new("", cwd.clone()); + if current_selected_command_index == all_commands.len().saturating_sub(1) { + all_commands.push(new_command); + } else { + all_commands.insert(current_selected_command_index + 1, new_command); + } + + self.current_selected_command_index = Some(current_selected_command_index + 1); + } + } + } + + pub fn current_selected_command_is_empty( + &self, + all_commands: &[CommandEntry], + editing: &Editing, + ) -> bool { + let text_input_is_empty = editing + .editing_input + .as_ref() + .map(|i| i.is_empty()) + .unwrap_or(true); + let current_selected_command_is_empty = self + .current_selected_command_index + .and_then(|i| all_commands.get(i).map(|c| c.is_empty())) + .unwrap_or(true); + text_input_is_empty && current_selected_command_is_empty + } + + pub fn remove_current_selected_command( + &mut self, + all_commands: &mut Vec<CommandEntry>, + editing: &mut Editing, + ) -> Option<PaneId> { + // returns the pane_id of the removed command, if any + let mut removed_pane_id = None; + let Some(mut current_selected_command_index) = self.current_selected_command_index else { + return removed_pane_id; + }; + if all_commands.len() > 1 && all_commands.len() > current_selected_command_index { + let command = all_commands.remove(current_selected_command_index); + removed_pane_id = command.get_pane_id(); + } else { + self.clear_current_selected_command(all_commands, editing); + } + if current_selected_command_index >= all_commands.len() + && current_selected_command_index > 0 + { + current_selected_command_index -= 1; + } + self.update_current_selected_command_index( + current_selected_command_index, + all_commands, + editing, + ); + if current_selected_command_index == all_commands.len().saturating_sub(1) { + self.current_selected_command_mut(all_commands) + .map(|c| c.clear_chain_type()); + } + removed_pane_id + } + + pub fn clear_current_selected_command( + &mut self, + all_commands: &mut [CommandEntry], + editing: &mut Editing, + ) { + let Some(current_selected_command_index) = self.current_selected_command_index else { + return; + }; + self.current_selected_command_mut(all_commands) + .map(|c| c.clear_text()); + self.clear_editing_buffer(editing); + if current_selected_command_index == all_commands.len().saturating_sub(1) { + self.current_selected_command_mut(all_commands) + .map(|c| c.clear_chain_type()); + } + } + + pub fn move_up(&mut self, all_commands: &mut [CommandEntry], editing: &mut Editing) { + self.save_editing_buffer_to_current_selected(all_commands, editing); + 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; + }, + } + self.update_editing_buffer_to_current_selected(all_commands, editing); + } + + pub fn move_down(&mut self, all_commands: &mut [CommandEntry], editing: &mut Editing) { + self.save_editing_buffer_to_current_selected(all_commands, editing); + 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; + }, + } + self.update_editing_buffer_to_current_selected(all_commands, editing); + } + + fn current_selected_command_mut<'a>( + &self, + all_commands: &'a mut [CommandEntry], + ) -> Option<&'a mut CommandEntry> { + let Some(i) = self.current_selected_command_index else { + return None; + }; + all_commands.get_mut(i) + } + + fn current_selected_command<'a>( + &self, + all_commands: &'a [CommandEntry], + ) -> Option<&'a CommandEntry> { + let Some(i) = self.current_selected_command_index else { + return None; + }; + all_commands.get(i) + } + + fn clear_editing_buffer(&self, editing: &mut Editing) { + editing.editing_input.as_mut().map(|c| c.clear()); + } + + fn update_current_selected_command_index( + &mut self, + new_index: usize, + all_commands: &[CommandEntry], + editing: &mut Editing, + ) { + self.current_selected_command_index = Some(new_index); + self.update_editing_buffer_to_current_selected(all_commands, editing); + } + + fn get_text_of_current_selected_command( + &self, + all_commands: &[CommandEntry], + ) -> Option<String> { + self.current_selected_command(all_commands) + .map(|c| c.get_text()) + } + + fn save_editing_buffer_to_current_selected( + &self, + all_commands: &mut [CommandEntry], + editing: &mut Editing, + ) { + if let Some(text_input) = editing.editing_input.as_ref().map(|i| i.get_text()) { + self.current_selected_command_mut(all_commands) + .map(|c| c.set_text(text_input.to_owned())); + } + } + + fn update_editing_buffer_to_current_selected( + &self, + all_commands: &[CommandEntry], + editing: &mut Editing, + ) { + let new_text_of_current_selected_command = self + .get_text_of_current_selected_command(all_commands) + .unwrap_or_else(|| String::new()); + if let Some(editing_input) = editing.editing_input.as_mut() { + editing_input.set_text(new_text_of_current_selected_command); + } + } +} + +impl Default for Selection { + fn default() -> Self { + Self::new() + } +} |
