on master help sign in
Fix append mode, and make insertion always happen at head of range.
ad814b8Nathan Vegdahl
helix-core/src/selection.rs+14
@@ -447,6 +447,20 @@ impl Selection {
447 self.transform(|r| r.min_width_1(text))
447 self.transform(|r| r.min_width_1(text))
448 }
448 }
449
449
450+ /// Transforms the selection into all of the left-side head positions,
451+ /// using 1-width semantics.
452+ pub fn cursors(self, text: RopeSlice) -> Self {
453+ self.transform(|range| {
454+ // For 1-width cursor semantics.
455+ let pos = if range.anchor < range.head {
456+ prev_grapheme_boundary(text, range.head).max(range.anchor)
457+ } else {
458+ range.head
459+ };
460+ Range::new(pos, pos)
461+ })
462+ }
463+
450 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
464 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
451 self.ranges.iter().map(move |range| range.fragment(text))
465 self.ranges.iter().map(move |range| range.fragment(text))
452 }
466 }
helix-term/src/commands.rs+23−17
@@ -1272,19 +1272,13 @@ fn append_mode(cx: &mut Context) {
1272 doc.restore_cursor = true;
1272 doc.restore_cursor = true;
1273 let text = doc.text().slice(..);
1273 let text = doc.text().slice(..);
1274
1274
1275− // TODO: preserve selections, like in `Insert` mode. Probably we'll want
1276− // an explicit separate `Append` mode or something similar, so that we
1277− // don't change the selection at all, and instead just display and edit
1278− // things differently.
1279− let selection = doc
1275+ let selection = doc.selection(view.id).clone().min_width_1(text);
1280− .selection(view.id)
1281− .clone()
1282− .min_width_1(text)
1283− .transform(|range| Range::new(range.to(), range.to()));
1284
1276
1277+ // Make sure there's room at the end of the document if the last
1278+ // selection butts up against it.
1285 let end = text.len_chars();
1279 let end = text.len_chars();
1286−
1280+ let last_range = selection.iter().last().unwrap();
1287− if selection.iter().any(|range| range.head == end) {
1281+ if !last_range.is_empty() && last_range.head == end {
1288 let transaction = Transaction::change(
1282 let transaction = Transaction::change(
1289 doc.text(),
1283 doc.text(),
1290 std::array::IntoIter::new([(end, end, Some(doc.line_ending.as_str().into()))]),
1284 std::array::IntoIter::new([(end, end, Some(doc.line_ending.as_str().into()))]),
@@ -1292,7 +1286,15 @@ fn append_mode(cx: &mut Context) {
1292 doc.apply(&transaction, view.id);
1286 doc.apply(&transaction, view.id);
1293 }
1287 }
1294
1288
1295− doc.set_selection(view.id, selection);
1289+ doc.set_selection(
1290+ view.id,
1291+ selection.clone().transform(|range| {
1292+ Range::new(
1293+ range.from(),
1294+ graphemes::next_grapheme_boundary(doc.text().slice(..), range.to()),
1295+ )
1296+ }),
1297+ );
1296}
1298}
1297
1299
1298mod cmd {
1300mod cmd {
@@ -2829,11 +2831,11 @@ pub mod insert {
2829 let (view, doc) = current!(cx.editor);
2831 let (view, doc) = current!(cx.editor);
2830
2832
2831 let text = doc.text();
2833 let text = doc.text();
2832− let selection = doc.selection(view.id);
2834+ let selection = doc.selection(view.id).clone().cursors(text.slice(..));
2833
2835
2834 // run through insert hooks, stopping on the first one that returns Some(t)
2836 // run through insert hooks, stopping on the first one that returns Some(t)
2835 for hook in HOOKS {
2837 for hook in HOOKS {
2836− if let Some(transaction) = hook(text, selection, c) {
2838+ if let Some(transaction) = hook(text, &selection, c) {
2837 doc.apply(&transaction, view.id);
2839 doc.apply(&transaction, view.id);
2838 break;
2840 break;
2839 }
2841 }
@@ -2853,7 +2855,11 @@ pub mod insert {
2853 // indent by one to reach 4 spaces).
2855 // indent by one to reach 4 spaces).
2854
2856
2855 let indent = Tendril::from(doc.indent_unit());
2857 let indent = Tendril::from(doc.indent_unit());
2856− let transaction = Transaction::insert(doc.text(), doc.selection(view.id), indent);
2858+ let transaction = Transaction::insert(
2859+ doc.text(),
2860+ &doc.selection(view.id).clone().cursors(doc.text().slice(..)),
2861+ indent,
2862+ );
2857 doc.apply(&transaction, view.id);
2863 doc.apply(&transaction, view.id);
2858 }
2864 }
2859
2865
@@ -2862,13 +2868,13 @@ pub mod insert {
2862 let text = doc.text().slice(..);
2868 let text = doc.text().slice(..);
2863
2869
2864 let contents = doc.text();
2870 let contents = doc.text();
2865− let selection = doc.selection(view.id);
2871+ let selection = doc.selection(view.id).clone().cursors(text);
2866 let mut ranges = SmallVec::with_capacity(selection.len());
2872 let mut ranges = SmallVec::with_capacity(selection.len());
2867
2873
2868 // TODO: this is annoying, but we need to do it to properly calculate pos after edits
2874 // TODO: this is annoying, but we need to do it to properly calculate pos after edits
2869 let mut offs = 0;
2875 let mut offs = 0;
2870
2876
2871− let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
2877+ let mut transaction = Transaction::change_by_selection(contents, &selection, |range| {
2872 let pos = range.head;
2878 let pos = range.head;
2873
2879
2874 let prev = if pos == 0 {
2880 let prev = if pos == 0 {