From 1275b7376f8cafe10b27a2f5c55cfe05631456b3 Mon Sep 17 00:00:00 2001 From: Gokul Soumya Date: Wed, 16 Jun 2021 21:41:54 +0530 Subject: [PATCH 1/2] Fix cursor position bugs related to o and O - `O` at the beginning of file didn't move cursor - `o` and `O` messed up cursor position with multiple cursors Fixes #127 --- helix-term/src/commands.rs | 72 ++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c80716d453f3..59ae0e793cae 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1495,53 +1495,55 @@ fn open(cx: &mut Context, open: Open) { enter_insert_mode(doc); let text = doc.text().slice(..); + let contents = doc.text(); let selection = doc.selection(view.id); let mut ranges = SmallVec::with_capacity(selection.len()); + let mut offs = 0; - let changes: Vec = selection - .iter() - .map(|range| { - let line = text.char_to_line(range.head); - - let line = match open { - // adjust position to the end of the line (next line - 1) - Open::Below => line + 1, - // adjust position to the end of the previous line (current line - 1) - Open::Above => line, - }; + let mut transaction = Transaction::change_by_selection(contents, selection, |range| { + let line = text.char_to_line(range.head); - let index = doc.text().line_to_char(line).saturating_sub(1); + let line = match open { + // adjust position to the end of the line (next line - 1) + Open::Below => line + 1, + // adjust position to the end of the previous line (current line - 1) + Open::Above => line, + }; - // TODO: share logic with insert_newline for indentation - let indent_level = indent::suggested_indent_for_pos( - doc.language_config(), - doc.syntax(), - text, - index, - true, - ); - let indent = doc.indent_unit().repeat(indent_level); - let mut text = String::with_capacity(1 + indent.len()); - text.push('\n'); - text.push_str(&indent); - let text = text.repeat(count); + let index = doc.text().line_to_char(line).saturating_sub(1); - // calculate new selection range - let pos = index + text.chars().count(); - ranges.push(Range::new(pos, pos)); + // TODO: share logic with insert_newline for indentation + let indent_level = indent::suggested_indent_for_pos( + doc.language_config(), + doc.syntax(), + text, + index, + true, + ); + let indent = doc.indent_unit().repeat(indent_level); + let mut text = String::with_capacity(1 + indent.len()); + text.push('\n'); + text.push_str(&indent); + let text = text.repeat(count); + + // calculate new selection range + let pos = if line == 0 { + 0 // Required since text will have a min len of 1 (\n) + } else { + index + offs + text.chars().count() + }; + ranges.push(Range::new(pos, pos)); - (index, index, Some(text.into())) - }) - .collect(); + offs += text.chars().count(); + + (index, index, Some(text.into())) + }); // TODO: count actually inserts "n" new lines and starts editing on all of them. // TODO: append "count" newlines and modify cursors to those lines - let selection = Selection::new(ranges, 0); - - let transaction = - Transaction::change(doc.text(), changes.into_iter()).with_selection(selection); + transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); doc.apply(&transaction, view.id); } From 016e3883303732295212f87b8d3ca83701d6d6ea Mon Sep 17 00:00:00 2001 From: Gokul Soumya Date: Thu, 17 Jun 2021 11:49:02 +0530 Subject: [PATCH 2/2] Let o, O take counts for multiple cursors --- helix-term/src/commands.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 59ae0e793cae..e6ecda06ede4 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -107,6 +107,7 @@ impl<'a> Context<'a> { self.callbacks.push(callback); } + /// Returns 1 if no explicit count was provided #[inline] pub fn count(&self) -> usize { self.count.map_or(1, |v| v.get()) @@ -1511,14 +1512,15 @@ fn open(cx: &mut Context, open: Open) { Open::Above => line, }; - let index = doc.text().line_to_char(line).saturating_sub(1); + // insert newlines after this index for both Above and Below variants + let linend_index = doc.text().line_to_char(line).saturating_sub(1); // TODO: share logic with insert_newline for indentation let indent_level = indent::suggested_indent_for_pos( doc.language_config(), doc.syntax(), text, - index, + linend_index, true, ); let indent = doc.indent_unit().repeat(indent_level); @@ -1527,22 +1529,21 @@ fn open(cx: &mut Context, open: Open) { text.push_str(&indent); let text = text.repeat(count); - // calculate new selection range + // calculate new selection ranges let pos = if line == 0 { 0 // Required since text will have a min len of 1 (\n) } else { - index + offs + text.chars().count() + offs + linend_index + 1 }; - ranges.push(Range::new(pos, pos)); + for i in 0..count { + ranges.push(Range::new(pos + i, pos + i)); + } offs += text.chars().count(); - (index, index, Some(text.into())) + (linend_index, linend_index, Some(text.into())) }); - // TODO: count actually inserts "n" new lines and starts editing on all of them. - // TODO: append "count" newlines and modify cursors to those lines - transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); doc.apply(&transaction, view.id);