From ae011dc0b544ddec8bc64020628cd84ee41db0f5 Mon Sep 17 00:00:00 2001 From: leokayson <1140933692@qq.com> Date: Fri, 15 Aug 2025 01:01:39 +0800 Subject: [PATCH] Allow to use key shortcut to operate multi-cursors --- config/.oxrc | 13 +++++ kaolinite/src/document/cursor.rs | 96 +++++++++++++++++++++++++++++++- kaolinite/tests/test.rs | 24 ++++++++ plugins/quickcomment.lua | 4 +- src/config/editor.rs | 30 ++++++++++ 5 files changed, 164 insertions(+), 3 deletions(-) diff --git a/config/.oxrc b/config/.oxrc index 55211dc4..cce6429a 100644 --- a/config/.oxrc +++ b/config/.oxrc @@ -25,6 +25,18 @@ event_mapping = { ["ctrl_right"] = function() editor:move_next_word() end, + ["ctrl_shift_up"] = function() + editor:add_cursor_above() + end, + ["ctrl_shift_down"] = function() + editor:add_cursor_below() + end, + ["alt_shift_up"] = function() + editor:delete_cursor_below() + end, + ["alt_shift_down"] = function() + editor:delete_cursor_above() + end, ["home"] = function() editor:move_home() end, @@ -59,6 +71,7 @@ event_mapping = { end, ["esc"] = function() editor:cancel_selection() + editor:cancel_multi_cursors() end, ["shift_home"] = function() local n_moves = editor.cursor.x diff --git a/kaolinite/src/document/cursor.rs b/kaolinite/src/document/cursor.rs index bf9e8d87..2c11fe68 100644 --- a/kaolinite/src/document/cursor.rs +++ b/kaolinite/src/document/cursor.rs @@ -1,6 +1,7 @@ use crate::event::Status; use crate::utils::{tab_boundaries_backward, tab_boundaries_forward, width}; use crate::{Document, Loc}; +use std::cmp::{max, min}; use std::ops::Range; /// Defines a cursor's position and any selection it may be covering @@ -21,6 +22,7 @@ impl Document { /// Select with the cursor up pub fn select_up(&mut self) -> Status { // Return if already at start of document + self.secondary_cursors.retain(|loc| loc.y != 0); if self.loc().y == 0 { return Status::StartOfFile; } @@ -45,8 +47,11 @@ impl Document { /// Select with the cursor down pub fn select_down(&mut self) -> Status { + let len_lines = self.len_lines(); // Return if already on end of document - if self.len_lines() < self.loc().y + 1 { + self.secondary_cursors + .retain(|loc| loc.y != (len_lines - 1)); + if len_lines < self.loc().y + 1 { return Status::EndOfFile; } self.cursor.loc.y += 1; @@ -171,6 +176,90 @@ impl Document { self.move_to(&Loc::at(0, last)); } + /// Add a cursor above + pub fn add_cursor_above(&mut self) { + let loc_y_now = self.cursor.loc.y; + let min_y = min( + loc_y_now, + self.secondary_cursors + .iter() + .map(|loc| loc.y) + .min() + .unwrap_or(loc_y_now), + ); + + if min_y > 0 { + self.secondary_cursors + .push(Loc::at(self.cursor.loc.x, min_y - 1)); + } + } + + /// Add a cursor below + pub fn add_cursor_below(&mut self) { + let loc_y_now = self.cursor.loc.y; + let max_y = max( + loc_y_now, + self.secondary_cursors + .iter() + .map(|loc| loc.y) + .max() + .unwrap_or(loc_y_now), + ); + + if max_y < self.len_lines() { + self.secondary_cursors + .push(Loc::at(self.cursor.loc.x, max_y + 1)); + } + } + + /// Delete a cursor above + pub fn delete_cursor_above(&mut self) { + if self.secondary_cursors.is_empty() { + return; + } + + let loc_y_now = self.cursor.loc.y; + let mut min_y = min( + loc_y_now, + self.secondary_cursors + .iter() + .map(|loc| loc.y) + .min() + .unwrap_or(loc_y_now), + ); + + if min_y == loc_y_now { + self.move_down(); + min_y += 1; + } + + self.secondary_cursors.retain(|loc| loc.y != min_y); + } + + /// Delete a cursor below + pub fn delete_cursor_below(&mut self) { + if self.secondary_cursors.is_empty() { + return; + } + + let loc_y_now = self.cursor.loc.y; + let mut max_y = max( + loc_y_now, + self.secondary_cursors + .iter() + .map(|loc| loc.y) + .max() + .unwrap_or(loc_y_now), + ); + + if max_y == loc_y_now { + self.move_up(); + max_y -= 1; + } + + self.secondary_cursors.retain(|loc| loc.y != max_y); + } + /// Select to the top of the document pub fn select_top(&mut self) { self.select_to(&Loc::at(0, 0)); @@ -423,6 +512,11 @@ impl Document { self.cursor.selection_end = self.cursor.loc; } + /// Cancels the current multi cursors in the buffer + pub fn cancel_multi_cursors(&mut self) { + self.secondary_cursors.clear(); + } + /// Create a new alternative cursor pub fn new_cursor(&mut self, loc: Loc) { if let Some(idx) = self.has_cursor(loc) { diff --git a/kaolinite/tests/test.rs b/kaolinite/tests/test.rs index b099f075..baa71c8b 100644 --- a/kaolinite/tests/test.rs +++ b/kaolinite/tests/test.rs @@ -1045,6 +1045,30 @@ fn fuzz() { } 23 => doc.undo(), 24 => doc.redo(), + 25 => { + doc.add_cursor_above(); + Ok(()) + } + 26 => { + doc.add_cursor_below(); + Ok(()) + } + 27 => { + doc.delete_cursor_above(); + Ok(()) + } + 28 => { + doc.delete_cursor_below(); + Ok(()) + } + 29 => { + doc.delete_cursor_below(); + Ok(()) + } + 30 => { + doc.delete_cursor_below(); + Ok(()) + } _ => Ok(()), }; println!("{} | {}", doc.loc().x, doc.char_ptr); diff --git a/plugins/quickcomment.lua b/plugins/quickcomment.lua index 6c1f7ed5..dddecf2e 100644 --- a/plugins/quickcomment.lua +++ b/plugins/quickcomment.lua @@ -102,8 +102,8 @@ function quickcomment:comment_start() comment_start = "#" elseif editor.document_type == "Clojure" then comment_start = ";" - elseif editor.document_type == "Zsh" then - comment_start = "#" + elseif editor.document_type == "Batch" then + comment_start = "@REM" else comment_start = "//" end diff --git a/src/config/editor.rs b/src/config/editor.rs index ad33940d..d8793351 100644 --- a/src/config/editor.rs +++ b/src/config/editor.rs @@ -274,6 +274,30 @@ impl LuaUserData for Editor { } Ok(()) }); + methods.add_method_mut("add_cursor_above", |_, editor, ()| { + if let Some(doc) = editor.try_doc_mut() { + doc.add_cursor_above(); + } + Ok(()) + }); + methods.add_method_mut("add_cursor_below", |_, editor, ()| { + if let Some(doc) = editor.try_doc_mut() { + doc.add_cursor_below(); + } + Ok(()) + }); + methods.add_method_mut("delete_cursor_above", |_, editor, ()| { + if let Some(doc) = editor.try_doc_mut() { + doc.delete_cursor_above(); + } + Ok(()) + }); + methods.add_method_mut("delete_cursor_below", |_, editor, ()| { + if let Some(doc) = editor.try_doc_mut() { + doc.delete_cursor_below(); + } + Ok(()) + }); methods.add_method_mut("move_previous_word", |_, editor, ()| { editor.prev_word(); editor.update_highlighter(); @@ -350,6 +374,12 @@ impl LuaUserData for Editor { } Ok(()) }); + methods.add_method_mut("cancel_multi_cursors", |_, editor, ()| { + if let Some(doc) = editor.try_doc_mut() { + doc.cancel_multi_cursors(); + } + Ok(()) + }); methods.add_method_mut("cursor_to_viewport", |_, editor, ()| { if let Some(doc) = editor.try_doc_mut() { doc.bring_cursor_in_viewport();