Skip to content

Commit

Permalink
prevent selection collapse when inserting a newline (#2414)
Browse files Browse the repository at this point in the history
Inserting a newline currently collapses any connected selections when inserting
or appending. It's happening because we're reducing the selections down to
their cursors (`let selection = ..` line) and then computing the new selection
based on the cursor. We're discarding the original head and anchor information
which are necessary to emulate Kakoune's behavior.

In Kakoune, inserting a newline retains the existing selection and _slides_
it (moves head and anchor by the same amount) forward by the newline and
indentation amount. Appending a newline extends the selection to include the
newline and any new indentation.

With the implementation of insert_newline here, we slide by adding the global
and local offsets to both head and anchor. We extend by adding the global
offset to both head and anchor but the local offset only to the head.
  • Loading branch information
the-mikedavis authored May 11, 2022
1 parent 40647f0 commit e0b5cdf
Showing 1 changed file with 23 additions and 9 deletions.
32 changes: 23 additions & 9 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2777,14 +2777,14 @@ pub mod insert {
let text = doc.text().slice(..);

let contents = doc.text();
let selection = doc.selection(view.id).clone().cursors(text);
let selection = doc.selection(view.id).clone();
let mut ranges = SmallVec::with_capacity(selection.len());

// TODO: this is annoying, but we need to do it to properly calculate pos after edits
let mut offs = 0;
let mut global_offs = 0;

let mut transaction = Transaction::change_by_selection(contents, &selection, |range| {
let pos = range.head;
let pos = range.cursor(text);

let prev = if pos == 0 {
' '
Expand Down Expand Up @@ -2814,27 +2814,41 @@ pub mod insert {
.and_then(|pair| if pair.close == curr { Some(pair) } else { None })
.is_some();

let new_head_pos = if on_auto_pair {
let local_offs = if on_auto_pair {
let inner_indent = indent.clone() + doc.indent_style.as_str();
text.reserve_exact(2 + indent.len() + inner_indent.len());
text.push_str(doc.line_ending.as_str());
text.push_str(&inner_indent);
let new_head_pos = pos + offs + text.chars().count();
let local_offs = text.chars().count();
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
new_head_pos
local_offs
} else {
text.reserve_exact(1 + indent.len());
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
pos + offs + text.chars().count()
text.chars().count()
};

let new_range = if doc.restore_cursor {
// when appending, extend the range by local_offs
Range::new(
range.anchor + global_offs,
range.head + local_offs + global_offs,
)
} else {
// when inserting, slide the range by local_offs
Range::new(
range.anchor + local_offs + global_offs,
range.head + local_offs + global_offs,
)
};

// TODO: range replace or extend
// range.replace(|range| range.is_empty(), head); -> fn extend if cond true, new head pos
// can be used with cx.mode to do replace or extend on most changes
ranges.push(Range::new(new_head_pos, new_head_pos));
offs += text.chars().count();
ranges.push(new_range);
global_offs += text.chars().count();

(pos, pos, Some(text.into()))
});
Expand Down

0 comments on commit e0b5cdf

Please sign in to comment.