on master help sign in
Ensure a minimum selection width on commands that need it.
e725957Nathan Vegdahl
helix-core/src/selection.rs+7−2
@@ -308,7 +308,7 @@ impl Selection {
308 }
308 }
309
309
310 /// Normalizes a `Selection`.
310 /// Normalizes a `Selection`.
311− pub fn normalize(mut self) -> Self {
311+ fn normalize(mut self) -> Self {
312 let primary = self.ranges[self.primary_index];
312 let primary = self.ranges[self.primary_index];
313 self.ranges.sort_unstable_by_key(Range::from);
313 self.ranges.sort_unstable_by_key(Range::from);
314 self.primary_index = self
314 self.primary_index = self
@@ -363,7 +363,12 @@ impl Selection {
363 *range = f(*range)
363 *range = f(*range)
364 }
364 }
365
365
366− self
366+ self.normalize()
367+ }
368+
369+ /// A convenience short-cut for `transform(|r| r.min_width_1(text))`.
370+ pub fn min_width_1(mut self, text: RopeSlice) -> Self {
371+ self.transform(|r| r.min_width_1(text))
367 }
372 }
368
373
369 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
374 pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
helix-term/src/commands.rs+66−62
@@ -718,24 +718,30 @@ fn replace(cx: &mut Context) {
718 _ => None,
718 _ => None,
719 };
719 };
720
720
721− if let Some(ch) = ch {
721+ let text = doc.text().slice(..);
722− let transaction =
722+ let selection = doc.selection(view.id).clone().min_width_1(text);
723− Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
724− let max_to = rope_end_without_line_ending(&doc.text().slice(..));
725− let to = std::cmp::min(max_to, range.to() + 1);
726− let text: String = RopeGraphemes::new(doc.text().slice(range.from()..to))
727− .map(|g| {
728− let cow: Cow<str> = g.into();
729− if str_is_line_ending(&cow) {
730− cow
731− } else {
732− ch.into()
733− }
734− })
735− .collect();
736
723
724+ if let Some(ch) = ch {
725+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
726+ if !range.is_empty() {
727+ let text: String =
728+ RopeGraphemes::new(doc.text().slice(range.from()..range.to()))
729+ .map(|g| {
730+ let cow: Cow<str> = g.into();
731+ if str_is_line_ending(&cow) {
732+ cow
733+ } else {
734+ ch.into()
735+ }
736+ })
737+ .collect();
738+
737− (range.from(), to, Some(text.into()))
739+ (range.from(), range.to(), Some(text.into()))
740+ } else {
741+ // No change.
742+ (range.from(), range.to(), None)
743+ }
738− });
744+ });
739
745
740 doc.apply(&transaction, view.id);
746 doc.apply(&transaction, view.id);
741 doc.append_changes_to_history(view.id);
747 doc.append_changes_to_history(view.id);
@@ -1050,24 +1056,18 @@ fn extend_line(cx: &mut Context) {
1050}
1056}
1051
1057
1052fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId) {
1058fn delete_selection_impl(reg: &mut Register, doc: &mut Document, view_id: ViewId) {
1053− // first yank the selection
1054− let values: Vec<String> = doc
1055− .selection(view_id)
1056− .fragments(doc.text().slice(..))
1059+ let text = doc.text().slice(..);
1057− .map(Cow::into_owned)
1060+ let selection = doc.selection(view_id).clone().min_width_1(text);
1058− .collect();
1059
1061
1062+ // first yank the selection
1063+ let values: Vec<String> = selection.fragments(text).map(Cow::into_owned).collect();
1060 reg.write(values);
1064 reg.write(values);
1061
1065
1062 // then delete
1066 // then delete
1063− let transaction =
1064− Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
1067+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
1065− let alltext = doc.text().slice(..);
1066− let line = alltext.char_to_line(range.head);
1068+ let line = text.char_to_line(range.head);
1067− let max_to = rope_end_without_line_ending(&alltext);
1068− let to = std::cmp::min(max_to, range.to() + 1);
1069− (range.from(), to, None)
1069+ (range.from(), range.to(), None)
1070− });
1070+ });
1071 doc.apply(&transaction, view_id);
1071 doc.apply(&transaction, view_id);
1072}
1072}
1073
1073
@@ -1513,11 +1513,13 @@ mod cmd {
1513
1513
1514 match cx.editor.clipboard_provider.get_contents() {
1514 match cx.editor.clipboard_provider.get_contents() {
1515 Ok(contents) => {
1515 Ok(contents) => {
1516+ let selection = doc
1517+ .selection(view.id)
1518+ .clone()
1519+ .min_width_1(doc.text().slice(..));
1516 let transaction =
1520 let transaction =
1517− Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
1521+ Transaction::change_by_selection(doc.text(), &selection, |range| {
1518− let max_to = rope_end_without_line_ending(&doc.text().slice(..));
1519− let to = std::cmp::min(max_to, range.to() + 1);
1520− (range.from(), to, Some(contents.as_str().into()))
1522+ (range.from(), range.to(), Some(contents.as_str().into()))
1521 });
1523 });
1522
1524
1523 doc.apply(&transaction, view.id);
1525 doc.apply(&transaction, view.id);
@@ -2864,17 +2866,18 @@ fn paste_impl(
2864 let mut values = values.iter().cloned().map(Tendril::from).chain(repeat);
2866 let mut values = values.iter().cloned().map(Tendril::from).chain(repeat);
2865
2867
2866 let text = doc.text();
2868 let text = doc.text();
2869+ let selection = doc.selection(view.id).clone().min_width_1(text.slice(..));
2867
2870
2868− let transaction = Transaction::change_by_selection(text, doc.selection(view.id), |range| {
2871+ let transaction = Transaction::change_by_selection(text, &selection, |range| {
2869 let pos = match (action, linewise) {
2872 let pos = match (action, linewise) {
2870 // paste linewise before
2873 // paste linewise before
2871 (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
2874 (Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
2872 // paste linewise after
2875 // paste linewise after
2873− (Paste::After, true) => text.line_to_char(text.char_to_line(range.to()) + 1),
2876+ (Paste::After, true) => text.line_to_char(text.char_to_line(range.to())),
2874 // paste insert
2877 // paste insert
2875 (Paste::Before, false) => range.from(),
2878 (Paste::Before, false) => range.from(),
2876 // paste append
2879 // paste append
2877− (Paste::After, false) => range.to() + 1,
2880+ (Paste::After, false) => range.to(),
2878 };
2881 };
2879 (pos, pos, Some(values.next().unwrap()))
2882 (pos, pos, Some(values.next().unwrap()))
2880 });
2883 });
@@ -2914,12 +2917,17 @@ fn replace_with_yanked(cx: &mut Context) {
2914
2917
2915 if let Some(values) = registers.read(reg_name) {
2918 if let Some(values) = registers.read(reg_name) {
2916 if let Some(yank) = values.first() {
2919 if let Some(yank) = values.first() {
2917− let transaction =
2920+ let selection = doc
2921+ .selection(view.id)
2922+ .clone()
2923+ .min_width_1(doc.text().slice(..));
2918− Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
2924+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
2919− let max_to = rope_end_without_line_ending(&doc.text().slice(..));
2925+ if !range.is_empty() {
2920− let to = std::cmp::min(max_to, range.to() + 1);
2921− (range.from(), to, Some(yank.as_str().into()))
2926+ (range.from(), range.to(), Some(yank.as_str().into()))
2927+ } else {
2928+ (range.from(), range.to(), None)
2929+ }
2922− });
2930+ });
2923
2931
2924 doc.apply(&transaction, view.id);
2932 doc.apply(&transaction, view.id);
2925 doc.append_changes_to_history(view.id);
2933 doc.append_changes_to_history(view.id);
@@ -2932,12 +2940,13 @@ fn replace_selections_with_clipboard_impl(editor: &mut Editor) {
2932
2940
2933 match editor.clipboard_provider.get_contents() {
2941 match editor.clipboard_provider.get_contents() {
2934 Ok(contents) => {
2942 Ok(contents) => {
2935− let transaction =
2943+ let selection = doc
2944+ .selection(view.id)
2945+ .clone()
2946+ .min_width_1(doc.text().slice(..));
2936− Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
2947+ let transaction = Transaction::change_by_selection(doc.text(), &selection, |range| {
2937− let max_to = rope_end_without_line_ending(&doc.text().slice(..));
2938− let to = std::cmp::min(max_to, range.to() + 1);
2939− (range.from(), to, Some(contents.as_str().into()))
2948+ (range.from(), range.to(), Some(contents.as_str().into()))
2940− });
2949+ });
2941
2950
2942 doc.apply(&transaction, view.id);
2951 doc.apply(&transaction, view.id);
2943 doc.append_changes_to_history(view.id);
2952 doc.append_changes_to_history(view.id);
@@ -3575,18 +3584,13 @@ fn surround_add(cx: &mut Context) {
3575 {
3584 {
3576 let (view, doc) = current!(cx.editor);
3585 let (view, doc) = current!(cx.editor);
3577 let text = doc.text().slice(..);
3586 let text = doc.text().slice(..);
3578− let selection = doc.selection(view.id);
3587+ let selection = doc.selection(view.id).clone().min_width_1(text);
3579 let (open, close) = surround::get_pair(ch);
3588 let (open, close) = surround::get_pair(ch);
3580
3589
3581 let mut changes = Vec::new();
3590 let mut changes = Vec::new();
3582 for (i, range) in selection.iter().enumerate() {
3591 for (i, range) in selection.iter().enumerate() {
3583− let from = range.from();
3584− let line = text.char_to_line(range.to());
3585− let max_to = rope_end_without_line_ending(&text);
3586− let to = std::cmp::min(range.to() + 1, max_to);
3587−
3588− changes.push((from, from, Some(Tendril::from_char(open))));
3592+ changes.push((range.from(), range.from(), Some(Tendril::from_char(open))));
3589− changes.push((to, to, Some(Tendril::from_char(close))));
3593+ changes.push((range.to(), range.to(), Some(Tendril::from_char(close))));
3590 }
3594 }
3591
3595
3592 let transaction = Transaction::change(doc.text(), changes.into_iter());
3596 let transaction = Transaction::change(doc.text(), changes.into_iter());
@@ -3612,9 +3616,9 @@ fn surround_replace(cx: &mut Context) {
3612 {
3616 {
3613 let (view, doc) = current!(cx.editor);
3617 let (view, doc) = current!(cx.editor);
3614 let text = doc.text().slice(..);
3618 let text = doc.text().slice(..);
3615− let selection = doc.selection(view.id);
3619+ let selection = doc.selection(view.id).clone().min_width_1(text);
3616
3620
3617− let change_pos = match surround::get_surround_pos(text, selection, from, count)
3621+ let change_pos = match surround::get_surround_pos(text, &selection, from, count)
3618 {
3622 {
3619 Some(c) => c,
3623 Some(c) => c,
3620 None => return,
3624 None => return,
@@ -3646,9 +3650,9 @@ fn surround_delete(cx: &mut Context) {
3646 {
3650 {
3647 let (view, doc) = current!(cx.editor);
3651 let (view, doc) = current!(cx.editor);
3648 let text = doc.text().slice(..);
3652 let text = doc.text().slice(..);
3649− let selection = doc.selection(view.id);
3653+ let selection = doc.selection(view.id).clone().min_width_1(text);
3650
3654
3651− let change_pos = match surround::get_surround_pos(text, selection, ch, count) {
3655+ let change_pos = match surround::get_surround_pos(text, &selection, ch, count) {
3652 Some(c) => c,
3656 Some(c) => c,
3653 None => return,
3657 None => return,
3654 };
3658 };