diff options
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r-- | helix-term/src/commands.rs | 693 |
1 files changed, 390 insertions, 303 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 63b91942..fbeae5ff 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -322,124 +322,152 @@ 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 goto_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)); - - Range::new( - match doc.mode { - Mode::Normal | Mode::Insert => pos, - Mode::Select => range.anchor, - }, - pos, - ) - }); + 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)); - doc.set_selection(view.id, selection); + Range::new( + match doc.mode { + Mode::Normal | Mode::Insert => pos, + Mode::Select => range.anchor, + }, + pos, + ) + }), + ); } fn goto_line_end_newline(cx: &mut Context) { let (view, doc) = current!(cx.editor); - 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, + doc.selection(view.id).clone().transform(|range| { + let text = doc.text(); + let line = text.char_to_line(range.head); - doc.set_selection(view.id, selection); + let pos = line_end_char_index(&text.slice(..), line); + Range::new(pos, pos) + }), + ); } fn goto_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( - match doc.mode { - Mode::Normal => range.anchor, - Mode::Select | Mode::Insert => pos, - }, - pos, - ) - }); - - doc.set_selection(view.id, selection); -} - -fn goto_first_nonwhitespace(cx: &mut Context) { - let (view, doc) = current!(cx.editor); - - 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); + // adjust to start of the line + let pos = text.line_to_char(line); Range::new( match doc.mode { - Mode::Normal => pos, + Mode::Normal | Mode::Insert => pos, Mode::Select => range.anchor, - Mode::Insert => unreachable!(), }, pos, ) - } else { - range - } - }); + }), + ); +} - doc.set_selection(view.id, selection); +fn goto_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); + + if let Some(pos) = find_first_non_whitespace_char(text.line(line_idx)) { + let pos = pos + text.line_to_char(line_idx); + Range::new( + match doc.mode { + Mode::Normal | Mode::Insert => pos, + Mode::Select => range.anchor, + }, + pos, + ) + } else { + range + } + }), + ); } fn goto_window(cx: &mut Context, align: Align) { @@ -480,73 +508,68 @@ fn goto_window_bottom(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 goto_file_start(cx: &mut Context) { @@ -566,42 +589,40 @@ fn goto_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] @@ -644,21 +665,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 + }, + ) + }), + ); }) } @@ -752,24 +776,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); @@ -842,41 +872,69 @@ 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 select_all(cx: &mut Context) { @@ -1025,41 +1083,36 @@ fn extend_line(cx: &mut Context) { fn extend_to_line_bounds(cx: &mut Context) { let (view, doc) = current!(cx.editor); - let text = doc.text(); - let selection = doc.selection(view.id).transform(|range| { - let start = text.line_to_char(text.char_to_line(range.from())); - let end = text - .line_to_char(text.char_to_line(range.to()) + 1) - .saturating_sub(1); - - if range.anchor < range.head { - Range::new(start, end) - } else { - Range::new(end, start) - } - }); + doc.set_selection( + view.id, + doc.selection(view.id).clone().transform(|range| { + let text = doc.text(); + let start = text.line_to_char(text.char_to_line(range.from())); + let end = text + .line_to_char(text.char_to_line(range.to()) + 1) + .saturating_sub(1); - doc.set_selection(view.id, selection); + if range.anchor < range.head { + Range::new(start, end) + } else { + Range::new(end, start) + } + }), + ); } 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); } @@ -1087,20 +1140,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) { @@ -1112,10 +1169,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 @@ -1124,15 +1183,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( @@ -1508,11 +1566,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); @@ -1983,13 +2043,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 @@ -2119,14 +2181,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; } @@ -2149,6 +2212,24 @@ fn goto_last_accessed_file(cx: &mut Context) { } fn select_mode(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + + // Make sure all selections are at least 1-wide. + // (With the exception of being in an empty document, of course.) + doc.set_selection( + view.id, + doc.selection(view.id).clone().transform(|range| { + if range.is_empty() && range.head == doc.text().len_chars() { + Range::new( + graphemes::prev_grapheme_boundary(doc.text().slice(..), range.anchor), + range.head, + ) + } else { + range.min_width_1(doc.text().slice(..)) + } + }), + ); + doc_mut!(cx.editor).mode = Mode::Select; } @@ -2161,7 +2242,7 @@ fn goto_prehook(cx: &mut Context) -> bool { push_jump(cx.editor); let (view, doc) = current!(cx.editor); - let line_idx = std::cmp::min(count.get() - 1, doc.text().len_lines().saturating_sub(2)); + let line_idx = std::cmp::min(count.get() - 1, doc.text().len_lines().saturating_sub(1)); let pos = doc.text().line_to_char(line_idx); doc.set_selection(view.id, Selection::point(pos)); true @@ -2706,11 +2787,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) } } @@ -2829,17 +2912,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())) }); @@ -2879,12 +2963,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); @@ -2897,12 +2986,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); @@ -3510,20 +3600,21 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { } = event { let (view, doc) = current!(cx.editor); - let text = doc.text().slice(..); - let selection = doc.selection(view.id).transform(|range| { - match ch { - 'w' => textobject::textobject_word(text, range, objtype, count), - // TODO: cancel new ranges if inconsistent surround matches across lines - ch if !ch.is_ascii_alphanumeric() => { - textobject::textobject_surround(text, range, objtype, ch, count) + doc.set_selection( + view.id, + doc.selection(view.id).clone().transform(|range| { + let text = doc.text().slice(..); + match ch { + 'w' => textobject::textobject_word(text, range, objtype, count), + // TODO: cancel new ranges if inconsistent surround matches across lines + ch if !ch.is_ascii_alphanumeric() => { + textobject::textobject_surround(text, range, objtype, ch, count) + } + _ => range, } - _ => range, - } - }); - - doc.set_selection(view.id, selection); + }), + ); } }) } @@ -3537,17 +3628,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()); @@ -3573,9 +3660,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, @@ -3607,9 +3694,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, }; |