Skip to content

Commit 50da07b

Browse files
committed
account for overlapping deletes
1 parent 6eb034d commit 50da07b

File tree

3 files changed

+161
-40
lines changed

3 files changed

+161
-40
lines changed

β€Žhelix-core/src/auto_pairs.rsβ€Ž

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub fn hook_insert(
136136

137137
#[must_use]
138138
pub fn hook_delete(doc: &Rope, range: &Range, pairs: &AutoPairs) -> Option<(Deletion, Range)> {
139+
log::trace!("autopairs delete hook range: {:#?}", range);
140+
139141
let text = doc.slice(..);
140142
let cursor = range.cursor(text);
141143

@@ -175,12 +177,27 @@ pub fn handle_delete(doc: &Rope, range: &Range) -> Option<(Deletion, Range)> {
175177
let size_delete = end_next - end_prev;
176178
let next_head = graphemes::next_grapheme_boundary(text, range.head) - size_delete;
177179

178-
let next_range = match range.direction() {
179-
Direction::Forward => Range::new(range.anchor, next_head),
180-
Direction::Backward => Range::new(range.anchor - size_delete, next_head),
180+
// if the range is a single grapheme cursor, we do not want to shrink the
181+
// range, just move it, so we only subtract the size of the closing pair char
182+
let next_anchor = match (range.direction(), range.is_single_grapheme(text)) {
183+
// single grapheme forward needs to move, but only the width of the
184+
// character under the cursor, which is the closer
185+
(Direction::Forward, true) => range.anchor - (end_next - cursor),
186+
(Direction::Backward, true) => range.anchor - (cursor - end_prev),
187+
188+
(Direction::Forward, false) => range.anchor,
189+
(Direction::Backward, false) => range.anchor - size_delete,
181190
};
182191

183-
log::trace!("auto pair delete: {:?}, range: {:?}", delete, range,);
192+
let next_range = Range::new(next_anchor, next_head);
193+
194+
log::trace!(
195+
"auto pair delete: {:?}, range: {:?}, next_range: {:?}, text len: {}",
196+
delete,
197+
range,
198+
next_range,
199+
text.len_chars()
200+
);
184201

185202
Some((delete, next_range))
186203
}

β€Žhelix-core/src/transaction.rsβ€Ž

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,10 +708,13 @@ impl Transaction {
708708
{
709709
let mut end_ranges = SmallVec::with_capacity(selection.len());
710710
let mut offset = 0;
711+
let mut last = 0;
711712

712713
let transaction = Transaction::delete_by_selection(doc, selection, |start_range| {
713714
let ((from, to), end_range) = f(start_range);
714-
let change_size = to - from;
715+
716+
// must account for possibly overlapping deletes
717+
let change_size = if last > from { to - last } else { to - from };
715718

716719
let new_range = if let Some(end_range) = end_range {
717720
end_range
@@ -725,10 +728,18 @@ impl Transaction {
725728
new_range.head.saturating_sub(offset),
726729
);
727730

731+
log::trace!(
732+
"delete from: {}, to: {}, offset: {}, new_range: {:?}, offset_range: {:?}",
733+
from,
734+
to,
735+
offset,
736+
new_range,
737+
offset_range
738+
);
739+
728740
end_ranges.push(offset_range);
729741
offset += change_size;
730-
731-
log::trace!("delete from: {}, to: {}, offset: {}", from, to, offset);
742+
last = to;
732743

733744
(from, to)
734745
});

β€Žhelix-term/tests/test/auto_pairs.rsβ€Ž

Lines changed: 126 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ async fn delete_basic() -> anyhow::Result<()> {
656656
test((
657657
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
658658
"i<backspace>",
659-
format!("#[{}|]#", LINE_END),
659+
format!("#[|{}]#", LINE_END),
660660
))
661661
.await?;
662662
}
@@ -668,9 +668,21 @@ async fn delete_basic() -> anyhow::Result<()> {
668668
async fn delete_multi() -> anyhow::Result<()> {
669669
for pair in DEFAULT_PAIRS {
670670
test((
671-
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
671+
helpers::platform_line(&format!(
672+
indoc! {"\
673+
{open}#[|{close}]#
674+
{open}#(|{close})#
675+
{open}#(|{close})#
676+
"},
677+
open = pair.0,
678+
close = pair.1,
679+
)),
672680
"i<backspace>",
673-
format!("#[{}|]#", LINE_END),
681+
helpers::platform_line(indoc! {"\
682+
#[|\n]#
683+
#(|\n)#
684+
#(|\n)#
685+
"}),
674686
))
675687
.await?;
676688
}
@@ -684,7 +696,21 @@ async fn delete_whitespace() -> anyhow::Result<()> {
684696
test((
685697
helpers::platform_line(&format!("{} #[| ]#{}", pair.0, pair.1)),
686698
"i<backspace>",
687-
helpers::platform_line(&format!("{}#[{}|]#", pair.0, pair.1)),
699+
helpers::platform_line(&format!("{}#[|{}]#", pair.0, pair.1)),
700+
))
701+
.await?;
702+
}
703+
704+
Ok(())
705+
}
706+
707+
#[tokio::test(flavor = "multi_thread")]
708+
async fn delete_whitespace_after_word() -> anyhow::Result<()> {
709+
for pair in DEFAULT_PAIRS {
710+
test((
711+
helpers::platform_line(&format!("foo{} #[| ]#{}", pair.0, pair.1)),
712+
"i<backspace>",
713+
helpers::platform_line(&format!("foo{}#[|{}]#", pair.0, pair.1)),
688714
))
689715
.await?;
690716
}
@@ -709,7 +735,7 @@ async fn delete_whitespace_multi() -> anyhow::Result<()> {
709735
"i<backspace>",
710736
helpers::platform_line(&format!(
711737
indoc! {"\
712-
{open}#[{close}|]#
738+
{open}#[|{close}]#
713739
{open}#(|{open})#{close}{close}
714740
{open}{open}#(|{close}{close})#
715741
foo#(|\n)#
@@ -803,7 +829,7 @@ async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> {
803829
(
804830
format!("{}#[|{}]#{}", open, close, LINE_END),
805831
"i<backspace>",
806-
format!("#[{}|]#", LINE_END),
832+
format!("#[|{}]#", LINE_END),
807833
),
808834
)
809835
.await?;
@@ -816,9 +842,95 @@ async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> {
816842
async fn delete_after_word() -> anyhow::Result<()> {
817843
for pair in DEFAULT_PAIRS {
818844
test((
819-
format!("foo{}#[|{}]#{}", pair.0, pair.1, LINE_END),
845+
helpers::platform_line(&format!("foo{}#[|{}]#", pair.0, pair.1)),
820846
"i<backspace>",
821-
format!("foo#[{}|]#", LINE_END),
847+
helpers::platform_line("foo#[|\n]#"),
848+
))
849+
.await?;
850+
}
851+
852+
Ok(())
853+
}
854+
855+
#[tokio::test(flavor = "multi_thread")]
856+
async fn insert_then_delete() -> anyhow::Result<()> {
857+
for pair in differing_pairs() {
858+
test((
859+
helpers::platform_line("#[\n|]#\n"),
860+
format!("ofoo{}<backspace>", pair.0),
861+
helpers::platform_line("\nfoo#[\n|]#\n"),
862+
))
863+
.await?;
864+
}
865+
866+
Ok(())
867+
}
868+
869+
#[tokio::test(flavor = "multi_thread")]
870+
async fn insert_then_delete_whitespace() -> anyhow::Result<()> {
871+
for pair in differing_pairs() {
872+
test((
873+
helpers::platform_line("foo#[\n|]#"),
874+
format!("i{}<space><backspace><backspace>", pair.0),
875+
helpers::platform_line("foo#[|\n]#"),
876+
))
877+
.await?;
878+
}
879+
880+
Ok(())
881+
}
882+
883+
#[tokio::test(flavor = "multi_thread")]
884+
async fn insert_then_delete_multi() -> anyhow::Result<()> {
885+
for pair in differing_pairs() {
886+
test((
887+
helpers::platform_line(indoc! {"\
888+
through a day#[\n|]#
889+
in and out of weeks#(\n|)#
890+
over a year#(\n|)#
891+
"}),
892+
format!("i{}<space><backspace><backspace>", pair.0),
893+
helpers::platform_line(indoc! {"\
894+
through a day#[|\n]#
895+
in and out of weeks#(|\n)#
896+
over a year#(|\n)#
897+
"}),
898+
))
899+
.await?;
900+
}
901+
902+
Ok(())
903+
}
904+
905+
#[tokio::test(flavor = "multi_thread")]
906+
async fn append_then_delete() -> anyhow::Result<()> {
907+
for pair in differing_pairs() {
908+
test((
909+
helpers::platform_line("fo#[o|]#"),
910+
format!("a{}<space><backspace><backspace>", pair.0),
911+
helpers::platform_line("fo#[o\n|]#"),
912+
))
913+
.await?;
914+
}
915+
916+
Ok(())
917+
}
918+
919+
#[tokio::test(flavor = "multi_thread")]
920+
async fn append_then_delete_multi() -> anyhow::Result<()> {
921+
for pair in differing_pairs() {
922+
test((
923+
helpers::platform_line(indoc! {"\
924+
#[through a day|]#
925+
#(in and out of weeks|)#
926+
#(over a year|)#
927+
"}),
928+
format!("a{}<space><backspace><backspace>", pair.0),
929+
helpers::platform_line(indoc! {"\
930+
#[through a day\n|]#
931+
#(in and out of weeks\n|)#
932+
#(over a year\n|)#
933+
"}),
822934
))
823935
.await?;
824936
}
@@ -849,7 +961,7 @@ async fn delete_before_word() -> anyhow::Result<()> {
849961
test((
850962
format!("{}#[|{}]#foo{}", pair.0, pair.1, LINE_END),
851963
"i<backspace>",
852-
format!("#[f|]#oo{}", LINE_END),
964+
format!("#[|f]#oo{}", LINE_END),
853965
))
854966
.await?;
855967
}
@@ -913,7 +1025,7 @@ async fn delete_before_eol() -> anyhow::Result<()> {
9131025
close = pair.1
9141026
),
9151027
"i<backspace>",
916-
format!("{0}#[{0}|]#", LINE_END),
1028+
format!("{0}#[|{0}]#", LINE_END),
9171029
))
9181030
.await?;
9191031
}
@@ -944,25 +1056,6 @@ async fn delete_auto_pairs_disabled() -> anyhow::Result<()> {
9441056
Ok(())
9451057
}
9461058

947-
#[tokio::test(flavor = "multi_thread")]
948-
async fn delete_multi_range() -> anyhow::Result<()> {
949-
for pair in DEFAULT_PAIRS {
950-
test((
951-
format!(
952-
"{open}#[|{close}]#{eol}{open}#(|{close})#{eol}{open}#(|{close})#{eol}",
953-
open = pair.0,
954-
close = pair.1,
955-
eol = LINE_END
956-
),
957-
"i<backspace>",
958-
format!("#[{eol}|]##({eol}|)##({eol}|)#", eol = LINE_END),
959-
))
960-
.await?;
961-
}
962-
963-
Ok(())
964-
}
965-
9661059
#[tokio::test(flavor = "multi_thread")]
9671060
async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> {
9681061
for pair in DEFAULT_PAIRS {
@@ -989,7 +1082,7 @@ async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> {
9891082
pair.0, pair.1, LINE_END
9901083
),
9911084
"i<backspace>",
992-
format!("hello #[πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦|]# goodbye{}", LINE_END),
1085+
format!("hello #[|πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦]# goodbye{}", LINE_END),
9931086
))
9941087
.await?;
9951088

@@ -1043,7 +1136,7 @@ async fn delete_nested_open_inside_pair() -> anyhow::Result<()> {
10431136
),
10441137
"i<backspace>",
10451138
format!(
1046-
"{open}#[{close}|]#{eol}",
1139+
"{open}#[|{close}]#{eol}",
10471140
open = pair.0,
10481141
close = pair.1,
10491142
eol = LINE_END
@@ -1074,7 +1167,7 @@ async fn delete_nested_open_inside_pair_multi() -> anyhow::Result<()> {
10741167
),
10751168
"i<backspace>",
10761169
format!(
1077-
"{outer_open}#[{outer_close}|]#{eol}{outer_open}#({outer_close}|)#{eol}{outer_open}#({outer_close}|)#{eol}",
1170+
"{outer_open}#[|{outer_close}]#{eol}{outer_open}#(|{outer_close})#{eol}{outer_open}#(|{outer_close})#{eol}",
10781171
outer_open = outer_pair.0,
10791172
outer_close = outer_pair.1,
10801173
eol = LINE_END
@@ -1158,7 +1251,7 @@ async fn delete_mixed_dedent() -> anyhow::Result<()> {
11581251
)),
11591252
"i<backspace>",
11601253
helpers::platform_line(indoc! {"\
1161-
bar = #[\n|]#
1254+
bar = #[|\n]#
11621255
#(|\n)#
11631256
fo#(|\n)#
11641257
"}),

0 commit comments

Comments
Β (0)