aboutsummaryrefslogtreecommitdiffstats
path: root/default-plugins/sequence/src/state/selection.rs
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-12-11 14:25:42 +0100
committerGitHub <noreply@github.com>2025-12-11 14:25:42 +0100
commit693fa082def5ef0fe030cd7fdc3b1df051b35dc6 (patch)
treeb3f651462bbc1cd2fa5b2d79207343413ef6b83c /default-plugins/sequence/src/state/selection.rs
parentfix(zellij-server): unblock building on 32bit targets (#4484) (diff)
downloadsl0ppij-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.rs214
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()
+ }
+}