aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/commands.rs637
-rw-r--r--helix-term/src/ui/editor.rs6
2 files changed, 355 insertions, 288 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index a3799e7e..e76962f0 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -311,91 +311,119 @@ impl PartialEq for Command {
fn move_char_left(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_horizontally(text, range, Direction::Backward, count, Movement::Move)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_horizontally(
+ doc.text().slice(..),
+ range,
+ Direction::Backward,
+ count,
+ Movement::Move,
+ )
+ }),
+ );
}
fn move_char_right(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_horizontally(text, range, Direction::Forward, count, Movement::Move)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_horizontally(
+ doc.text().slice(..),
+ range,
+ Direction::Forward,
+ count,
+ Movement::Move,
+ )
+ }),
+ );
}
fn move_line_up(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_vertically(text, range, Direction::Backward, count, Movement::Move)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_vertically(
+ doc.text().slice(..),
+ range,
+ Direction::Backward,
+ count,
+ Movement::Move,
+ )
+ }),
+ );
}
fn move_line_down(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_vertically(text, range, Direction::Forward, count, Movement::Move)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_vertically(
+ doc.text().slice(..),
+ range,
+ Direction::Forward,
+ count,
+ Movement::Move,
+ )
+ }),
+ );
}
fn move_line_end(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text();
+ let line = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line = text.char_to_line(range.head);
-
- let pos = line_end_char_index(&text.slice(..), line);
- let pos = graphemes::nth_prev_grapheme_boundary(text.slice(..), pos, 1);
- let pos = range.head.max(pos).max(text.line_to_char(line));
+ let pos = line_end_char_index(&text.slice(..), line);
+ let pos = graphemes::nth_prev_grapheme_boundary(text.slice(..), pos, 1);
+ let pos = range.head.max(pos).max(text.line_to_char(line));
- Range::new(pos, pos)
- });
-
- doc.set_selection(view.id, selection);
+ Range::new(pos, pos)
+ }),
+ );
}
fn move_line_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text();
+ let line = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line = text.char_to_line(range.head);
-
- // adjust to start of the line
- let pos = text.line_to_char(line);
- Range::new(pos, pos)
- });
-
- doc.set_selection(view.id, selection);
+ // adjust to start of the line
+ let pos = text.line_to_char(line);
+ Range::new(pos, pos)
+ }),
+ );
}
fn move_first_nonwhitespace(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text();
+ let line_idx = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line_idx = text.char_to_line(range.head);
-
- if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
- let pos = pos + text.line_to_char(line_idx);
- Range::new(pos, pos)
- } else {
- range
- }
- });
-
- doc.set_selection(view.id, selection);
+ if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
+ let pos = pos + text.line_to_char(line_idx);
+ Range::new(pos, pos)
+ } else {
+ range
+ }
+ }),
+ );
}
// TODO: move vs extend could take an extra type Extend/Move that would
@@ -405,73 +433,68 @@ fn move_first_nonwhitespace(cx: &mut Context) {
fn move_next_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_next_word_start(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| movement::move_next_word_start(doc.text().slice(..), range, count)),
+ );
}
fn move_prev_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_prev_word_start(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| movement::move_prev_word_start(doc.text().slice(..), range, count)),
+ );
}
fn move_next_word_end(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_next_word_end(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| movement::move_next_word_end(doc.text().slice(..), range, count)),
+ );
}
fn move_next_long_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_next_long_word_start(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_next_long_word_start(doc.text().slice(..), range, count)
+ }),
+ );
}
fn move_prev_long_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_prev_long_word_start(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_prev_long_word_start(doc.text().slice(..), range, count)
+ }),
+ );
}
fn move_next_long_word_end(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_next_long_word_end(text, range, count));
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_next_long_word_end(doc.text().slice(..), range, count)
+ }),
+ );
}
fn move_file_start(cx: &mut Context) {
@@ -491,42 +514,40 @@ fn move_file_end(cx: &mut Context) {
fn extend_next_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc.selection(view.id).transform(|range| {
- let word = movement::move_next_word_start(text, range, count);
- let pos = word.head;
- Range::new(range.anchor, pos)
- });
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let word = movement::move_next_word_start(doc.text().slice(..), range, count);
+ let pos = word.head;
+ Range::new(range.anchor, pos)
+ }),
+ );
}
fn extend_prev_word_start(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc.selection(view.id).transform(|range| {
- let word = movement::move_prev_word_start(text, range, count);
- let pos = word.head;
- Range::new(range.anchor, pos)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let word = movement::move_prev_word_start(doc.text().slice(..), range, count);
+ let pos = word.head;
+ Range::new(range.anchor, pos)
+ }),
+ );
}
fn extend_next_word_end(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
-
- let selection = doc.selection(view.id).transform(|range| {
- let word = movement::move_next_word_end(text, range, count);
- let pos = word.head;
- Range::new(range.anchor, pos)
- });
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let word = movement::move_next_word_end(doc.text().slice(..), range, count);
+ let pos = word.head;
+ Range::new(range.anchor, pos)
+ }),
+ );
}
#[inline]
@@ -569,21 +590,24 @@ where
};
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- search_fn(text, ch, range.head, count, inclusive).map_or(range, |pos| {
- if extend {
- Range::new(range.anchor, pos)
- } else {
- // select
- Range::new(range.head, pos)
- }
- // or (pos, pos) to move to found val
- })
- });
-
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ search_fn(doc.text().slice(..), ch, range.head, count, inclusive).map_or(
+ range,
+ |pos| {
+ if extend {
+ Range::new(range.anchor, pos)
+ } else {
+ // select
+ Range::new(range.head, pos)
+ }
+ // or (pos, pos) to move to found val
+ },
+ )
+ }),
+ );
})
}
@@ -661,20 +685,20 @@ fn extend_prev_char(cx: &mut Context) {
fn extend_first_nonwhitespace(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text();
+ let line_idx = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line_idx = text.char_to_line(range.head);
-
- if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
- let pos = pos + text.line_to_char(line_idx);
- Range::new(range.anchor, pos)
- } else {
- range
- }
- });
-
- doc.set_selection(view.id, selection);
+ if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) {
+ let pos = pos + text.line_to_char(line_idx);
+ Range::new(range.anchor, pos)
+ } else {
+ range
+ }
+ }),
+ );
}
fn replace(cx: &mut Context) {
@@ -695,24 +719,30 @@ fn replace(cx: &mut Context) {
_ => None,
};
- if let Some(ch) = ch {
- let transaction =
- Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
- let max_to = rope_end_without_line_ending(&doc.text().slice(..));
- let to = std::cmp::min(max_to, range.to() + 1);
- let text: String = RopeGraphemes::new(doc.text().slice(range.from()..to))
- .map(|g| {
- let cow: Cow<str> = g.into();
- if str_is_line_ending(&cow) {
- cow
- } else {
- ch.into()
- }
- })
- .collect();
+ let text = doc.text().slice(..);
+ let selection = doc.selection(view.id).clone().min_width_1(text);
- (range.from(), to, Some(text.into()))
- });
+ if let Some(ch) = ch {
+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
+ if !range.is_empty() {
+ let text: String =
+ RopeGraphemes::new(doc.text().slice(range.from()..range.to()))
+ .map(|g| {
+ let cow: Cow<str> = g.into();
+ if str_is_line_ending(&cow) {
+ cow
+ } else {
+ ch.into()
+ }
+ })
+ .collect();
+
+ (range.from(), range.to(), Some(text.into()))
+ } else {
+ // No change.
+ (range.from(), range.to(), None)
+ }
+ });
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
@@ -785,73 +815,101 @@ fn half_page_down(cx: &mut Context) {
fn extend_char_left(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_horizontally(text, range, Direction::Backward, count, Movement::Extend)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_horizontally(
+ doc.text().slice(..),
+ range,
+ Direction::Backward,
+ count,
+ Movement::Extend,
+ )
+ }),
+ );
}
fn extend_char_right(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_horizontally(text, range, Direction::Forward, count, Movement::Extend)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_horizontally(
+ doc.text().slice(..),
+ range,
+ Direction::Forward,
+ count,
+ Movement::Extend,
+ )
+ }),
+ );
}
fn extend_line_up(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_vertically(text, range, Direction::Backward, count, Movement::Extend)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_vertically(
+ doc.text().slice(..),
+ range,
+ Direction::Backward,
+ count,
+ Movement::Extend,
+ )
+ }),
+ );
}
fn extend_line_down(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- movement::move_vertically(text, range, Direction::Forward, count, Movement::Extend)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_vertically(
+ doc.text().slice(..),
+ range,
+ Direction::Forward,
+ count,
+ Movement::Extend,
+ )
+ }),
+ );
}
fn extend_line_end(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text().slice(..);
+ let line = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line = text.char_to_line(range.head);
-
- let pos = line_end_char_index(&text.slice(..), line);
- let pos = graphemes::nth_prev_grapheme_boundary(text.slice(..), pos, 1);
- let pos = range.head.max(pos).max(text.line_to_char(line));
-
- Range::new(range.anchor, pos)
- });
+ let pos = line_end_char_index(&text, line);
+ let pos = graphemes::nth_prev_grapheme_boundary(text, pos, 1);
+ let pos = range.head.max(pos).max(text.line_to_char(line));
- doc.set_selection(view.id, selection);
+ Range::new(range.anchor, pos)
+ }),
+ );
}
fn extend_line_start(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = doc.text();
+ let line = text.char_to_line(range.head);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line = text.char_to_line(range.head);
-
- // adjust to start of the line
- let pos = text.line_to_char(line);
- Range::new(range.anchor, pos)
- });
-
- doc.set_selection(view.id, selection);
+ // adjust to start of the line
+ let pos = text.line_to_char(line);
+ Range::new(range.anchor, pos)
+ }),
+ );
}
fn select_all(cx: &mut Context) {
@@ -998,23 +1056,17 @@ fn extend_line(cx: &mut Context) {
}
fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId) {
- // first yank the selection
- let values: Vec<String> = doc
- .selection(view_id)
- .fragments(doc.text().slice(..))
- .map(Cow::into_owned)
- .collect();
+ let text = doc.text().slice(..);
+ let selection = doc.selection(view_id).clone().min_width_1(text);
+ // first yank the selection
+ let values: Vec<String> = selection.fragments(text).map(Cow::into_owned).collect();
reg.write(values);
// then delete
- let transaction =
- Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
- let alltext = doc.text().slice(..);
- let max_to = rope_end_without_line_ending(&alltext);
- let to = std::cmp::min(max_to, range.to() + 1);
- (range.from(), to, None)
- });
+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
+ (range.from(), range.to(), None)
+ });
doc.apply(&transaction, view_id);
}
@@ -1042,20 +1094,24 @@ fn change_selection(cx: &mut Context) {
fn collapse_selection(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
- let selection = doc
- .selection(view.id)
- .transform(|range| Range::new(range.head, range.head));
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| Range::new(range.head, range.head)),
+ );
}
fn flip_selections(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
- let selection = doc
- .selection(view.id)
- .transform(|range| Range::new(range.head, range.anchor));
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| Range::new(range.head, range.anchor)),
+ );
}
fn enter_insert_mode(doc: &mut Document) {
@@ -1067,10 +1123,12 @@ fn insert_mode(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
- let selection = doc
- .selection(view.id)
- .transform(|range| Range::new(range.to(), range.from()));
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id)
+ .clone()
+ .transform(|range| Range::new(range.to(), range.from())),
+ );
}
// inserts at the end of each selection
@@ -1079,15 +1137,14 @@ fn append_mode(cx: &mut Context) {
enter_insert_mode(doc);
doc.restore_cursor = true;
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
+ let selection = doc.selection(view.id).clone().transform(|range| {
Range::new(
range.from(),
- graphemes::next_grapheme_boundary(text, range.to()), // to() + next char
+ graphemes::next_grapheme_boundary(doc.text().slice(..), range.to()), // to() + next char
)
});
- let end = text.len_chars();
+ let end = doc.text().len_chars();
if selection.iter().any(|range| range.head == end) {
let transaction = Transaction::change(
@@ -1463,11 +1520,13 @@ mod cmd {
match cx.editor.clipboard_provider.get_contents() {
Ok(contents) => {
+ let selection = doc
+ .selection(view.id)
+ .clone()
+ .min_width_1(doc.text().slice(..));
let transaction =
- Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
- let max_to = rope_end_without_line_ending(&doc.text().slice(..));
- let to = std::cmp::min(max_to, range.to() + 1);
- (range.from(), to, Some(contents.as_str().into()))
+ Transaction::change_by_selection(doc.text(), &selection, |range| {
+ (range.from(), range.to(), Some(contents.as_str().into()))
});
doc.apply(&transaction, view.id);
@@ -1906,13 +1965,15 @@ fn append_to_line(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
- let selection = doc.selection(view.id).transform(|range| {
- let text = doc.text();
- let line = text.char_to_line(range.head);
- let pos = line_end_char_index(&text.slice(..), line);
- Range::new(pos, pos)
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ let text = &doc.text().slice(..);
+ let line = text.char_to_line(range.head);
+ let pos = line_end_char_index(text, line);
+ Range::new(pos, pos)
+ }),
+ );
}
/// Sometimes when applying formatting changes we want to mark the buffer as unmodified, for
@@ -2042,14 +2103,15 @@ fn normal_mode(cx: &mut Context) {
// if leaving append mode, move cursor back by 1
if doc.restore_cursor {
- let text = doc.text().slice(..);
- let selection = doc.selection(view.id).transform(|range| {
- Range::new(
- range.from(),
- graphemes::prev_grapheme_boundary(text, range.to()),
- )
- });
- doc.set_selection(view.id, selection);
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ Range::new(
+ range.from(),
+ graphemes::prev_grapheme_boundary(doc.text().slice(..), range.to()),
+ )
+ }),
+ );
doc.restore_cursor = false;
}
@@ -2674,11 +2736,13 @@ pub mod insert {
pub fn delete_word_backward(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let selection = doc
- .selection(view.id)
- .transform(|range| movement::move_prev_word_start(text, range, count));
- doc.set_selection(view.id, selection);
+
+ doc.set_selection(
+ view.id,
+ doc.selection(view.id).clone().transform(|range| {
+ movement::move_prev_word_start(doc.text().slice(..), range, count)
+ }),
+ );
delete_selection(cx)
}
}
@@ -2797,17 +2861,18 @@ fn paste_impl(
let mut values = values.iter().cloned().map(Tendril::from).chain(repeat);
let text = doc.text();
+ let selection = doc.selection(view.id).clone().min_width_1(text.slice(..));
- let transaction = Transaction::change_by_selection(text, doc.selection(view.id), |range| {
+ let transaction = Transaction::change_by_selection(text, &selection, |range| {
let pos = match (action, linewise) {
// paste linewise before
(Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
// paste linewise after
- (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1),
+ (Paste::After, true) => text.line_to_char(text.char_to_line(range.to())),
// paste insert
(Paste::Before, false) => range.from(),
// paste append
- (Paste::After, false) => range.to() + 1,
+ (Paste::After, false) => range.to(),
};
(pos, pos, Some(values.next().unwrap()))
});
@@ -2847,12 +2912,17 @@ fn replace_with_yanked(cx: &mut Context) {
if let Some(values) = registers.read(reg_name) {
if let Some(yank) = values.first() {
- let transaction =
- Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
- let max_to = rope_end_without_line_ending(&doc.text().slice(..));
- let to = std::cmp::min(max_to, range.to() + 1);
- (range.from(), to, Some(yank.as_str().into()))
- });
+ let selection = doc
+ .selection(view.id)
+ .clone()
+ .min_width_1(doc.text().slice(..));
+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
+ if !range.is_empty() {
+ (range.from(), range.to(), Some(yank.as_str().into()))
+ } else {
+ (range.from(), range.to(), None)
+ }
+ });
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
@@ -2865,12 +2935,13 @@ fn replace_selections_with_clipboard_impl(editor: &mut Editor) {
match editor.clipboard_provider.get_contents() {
Ok(contents) => {
- let transaction =
- Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
- let max_to = rope_end_without_line_ending(&doc.text().slice(..));
- let to = std::cmp::min(max_to, range.to() + 1);
- (range.from(), to, Some(contents.as_str().into()))
- });
+ let selection = doc
+ .selection(view.id)
+ .clone()
+ .min_width_1(doc.text().slice(..));
+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
+ (range.from(), range.to(), Some(contents.as_str().into()))
+ });
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
@@ -3502,17 +3573,13 @@ fn surround_add(cx: &mut Context) {
{
let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..);
- let selection = doc.selection(view.id);
+ let selection = doc.selection(view.id).clone().min_width_1(text);
let (open, close) = surround::get_pair(ch);
let mut changes = Vec::new();
for range in selection.iter() {
- let from = range.from();
- let max_to = rope_end_without_line_ending(&text);
- let to = std::cmp::min(range.to() + 1, max_to);
-
- changes.push((from, from, Some(Tendril::from_char(open))));
- changes.push((to, to, Some(Tendril::from_char(close))));
+ changes.push((range.from(), range.from(), Some(Tendril::from_char(open))));
+ changes.push((range.to(), range.to(), Some(Tendril::from_char(close))));
}
let transaction = Transaction::change(doc.text(), changes.into_iter());
@@ -3538,9 +3605,9 @@ fn surround_replace(cx: &mut Context) {
{
let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..);
- let selection = doc.selection(view.id);
+ let selection = doc.selection(view.id).clone().min_width_1(text);
- let change_pos = match surround::get_surround_pos(text, selection, from, count)
+ let change_pos = match surround::get_surround_pos(text, &selection, from, count)
{
Some(c) => c,
None => return,
@@ -3572,9 +3639,9 @@ fn surround_delete(cx: &mut Context) {
{
let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..);
- let selection = doc.selection(view.id);
+ let selection = doc.selection(view.id).clone().min_width_1(text);
- let change_pos = match surround::get_surround_pos(text, selection, ch, count) {
+ let change_pos = match surround::get_surround_pos(text, &selection, ch, count) {
Some(c) => c,
None => return,
};
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 14c34493..138456ca 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -8,7 +8,7 @@ use crate::{
use helix_core::{
coords_at_pos,
- graphemes::ensure_grapheme_boundary,
+ graphemes::ensure_grapheme_boundary_next,
syntax::{self, HighlightEvent},
LineEnding, Position, Range,
};
@@ -140,8 +140,8 @@ impl EditorView {
let highlights = highlights.into_iter().map(|event| match event.unwrap() {
// convert byte offsets to char offset
HighlightEvent::Source { start, end } => {
- let start = ensure_grapheme_boundary(text, text.byte_to_char(start));
- let end = ensure_grapheme_boundary(text, text.byte_to_char(end));
+ let start = ensure_grapheme_boundary_next(text, text.byte_to_char(start));
+ let end = ensure_grapheme_boundary_next(text, text.byte_to_char(end));
HighlightEvent::Source { start, end }
}
event => event,