account for overlapping deletes

pull/7269/head
Skyler Hawthorne 1 year ago
parent 8711b9e4fa
commit 2456a6571f

@ -136,6 +136,8 @@ pub fn hook_insert(
#[must_use] #[must_use]
pub fn hook_delete(doc: &Rope, range: &Range, pairs: &AutoPairs) -> Option<(Deletion, Range)> { pub fn hook_delete(doc: &Rope, range: &Range, pairs: &AutoPairs) -> Option<(Deletion, Range)> {
log::trace!("autopairs delete hook range: {:#?}", range);
let text = doc.slice(..); let text = doc.slice(..);
let cursor = range.cursor(text); let cursor = range.cursor(text);
@ -175,12 +177,27 @@ pub fn handle_delete(doc: &Rope, range: &Range) -> Option<(Deletion, Range)> {
let size_delete = end_next - end_prev; let size_delete = end_next - end_prev;
let next_head = graphemes::next_grapheme_boundary(text, range.head) - size_delete; let next_head = graphemes::next_grapheme_boundary(text, range.head) - size_delete;
let next_range = match range.direction() { // if the range is a single grapheme cursor, we do not want to shrink the
Direction::Forward => Range::new(range.anchor, next_head), // range, just move it, so we only subtract the size of the closing pair char
Direction::Backward => Range::new(range.anchor - size_delete, next_head), let next_anchor = match (range.direction(), range.is_single_grapheme(text)) {
// single grapheme forward needs to move, but only the width of the
// character under the cursor, which is the closer
(Direction::Forward, true) => range.anchor - (end_next - cursor),
(Direction::Backward, true) => range.anchor - (cursor - end_prev),
(Direction::Forward, false) => range.anchor,
(Direction::Backward, false) => range.anchor - size_delete,
}; };
log::trace!("auto pair delete: {:?}, range: {:?}", delete, range,); let next_range = Range::new(next_anchor, next_head);
log::trace!(
"auto pair delete: {:?}, range: {:?}, next_range: {:?}, text len: {}",
delete,
range,
next_range,
text.len_chars()
);
Some((delete, next_range)) Some((delete, next_range))
} }

@ -813,10 +813,13 @@ impl Transaction {
{ {
let mut end_ranges = SmallVec::with_capacity(selection.len()); let mut end_ranges = SmallVec::with_capacity(selection.len());
let mut offset = 0; let mut offset = 0;
let mut last = 0;
let transaction = Transaction::delete_by_selection(doc, selection, |start_range| { let transaction = Transaction::delete_by_selection(doc, selection, |start_range| {
let ((from, to), end_range) = f(start_range); let ((from, to), end_range) = f(start_range);
let change_size = to - from;
// must account for possibly overlapping deletes
let change_size = if last > from { to - last } else { to - from };
let new_range = if let Some(end_range) = end_range { let new_range = if let Some(end_range) = end_range {
end_range end_range
@ -830,10 +833,18 @@ impl Transaction {
new_range.head.saturating_sub(offset), new_range.head.saturating_sub(offset),
); );
log::trace!(
"delete from: {}, to: {}, offset: {}, new_range: {:?}, offset_range: {:?}",
from,
to,
offset,
new_range,
offset_range
);
end_ranges.push(offset_range); end_ranges.push(offset_range);
offset += change_size; offset += change_size;
last = to;
log::trace!("delete from: {}, to: {}, offset: {}", from, to, offset);
(from, to) (from, to)
}); });

@ -683,7 +683,7 @@ async fn delete_basic() -> anyhow::Result<()> {
test(( test((
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END), format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
"i<backspace>", "i<backspace>",
format!("#[{}|]#", LINE_END), format!("#[|{}]#", LINE_END),
)) ))
.await?; .await?;
} }
@ -695,9 +695,21 @@ async fn delete_basic() -> anyhow::Result<()> {
async fn delete_multi() -> anyhow::Result<()> { async fn delete_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS { for pair in DEFAULT_PAIRS {
test(( test((
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END), format!(
indoc! {"\
{open}#[|{close}]#
{open}#(|{close})#
{open}#(|{close})#
"},
open = pair.0,
close = pair.1,
),
"i<backspace>", "i<backspace>",
format!("#[{}|]#", LINE_END), indoc! {"\
#[|\n]#
#(|\n)#
#(|\n)#
"},
)) ))
.await?; .await?;
} }
@ -711,7 +723,21 @@ async fn delete_whitespace() -> anyhow::Result<()> {
test(( test((
format!("{} #[| ]#{}", pair.0, pair.1), format!("{} #[| ]#{}", pair.0, pair.1),
"i<backspace>", "i<backspace>",
format!("{}#[{}|]#", pair.0, pair.1), format!("{}#[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_after_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("foo{} #[| ]#{}", pair.0, pair.1),
"i<backspace>",
format!("foo{}#[|{}]#", pair.0, pair.1),
)) ))
.await?; .await?;
} }
@ -736,7 +762,7 @@ async fn delete_whitespace_multi() -> anyhow::Result<()> {
"i<backspace>", "i<backspace>",
format!( format!(
indoc! {"\ indoc! {"\
{open}#[{close}|]# {open}#[|{close}]#
{open}#(|{open})#{close}{close} {open}#(|{open})#{close}{close}
{open}{open}#(|{close}{close})# {open}{open}#(|{close}{close})#
foo#(|\n)# foo#(|\n)#
@ -830,7 +856,7 @@ async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> {
( (
format!("{}#[|{}]#{}", open, close, LINE_END), format!("{}#[|{}]#{}", open, close, LINE_END),
"i<backspace>", "i<backspace>",
format!("#[{}|]#", LINE_END), format!("#[|{}]#", LINE_END),
), ),
) )
.await?; .await?;
@ -843,9 +869,95 @@ async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> {
async fn delete_after_word() -> anyhow::Result<()> { async fn delete_after_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS { for pair in DEFAULT_PAIRS {
test(( test((
format!("foo{}#[|{}]#{}", pair.0, pair.1, LINE_END), &format!("foo{}#[|{}]#", pair.0, pair.1),
"i<backspace>", "i<backspace>",
format!("foo#[{}|]#", LINE_END), "foo#[|\n]#",
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_then_delete() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
"#[\n|]#\n",
format!("ofoo{}<backspace>", pair.0),
"\nfoo#[\n|]#\n",
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_then_delete_whitespace() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
"foo#[\n|]#",
format!("i{}<space><backspace><backspace>", pair.0),
"foo#[|\n]#",
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_then_delete_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
indoc! {"\
through a day#[\n|]#
in and out of weeks#(\n|)#
over a year#(\n|)#
"},
format!("i{}<space><backspace><backspace>", pair.0),
indoc! {"\
through a day#[|\n]#
in and out of weeks#(|\n)#
over a year#(|\n)#
"},
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn append_then_delete() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
"fo#[o|]#",
format!("a{}<space><backspace><backspace>", pair.0),
"fo#[o\n|]#",
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn append_then_delete_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
indoc! {"\
#[through a day|]#
#(in and out of weeks|)#
#(over a year|)#
"},
format!("a{}<space><backspace><backspace>", pair.0),
indoc! {"\
#[through a day\n|]#
#(in and out of weeks\n|)#
#(over a year\n|)#
"},
)) ))
.await?; .await?;
} }
@ -876,7 +988,7 @@ async fn delete_before_word() -> anyhow::Result<()> {
test(( test((
format!("{}#[|{}]#foo{}", pair.0, pair.1, LINE_END), format!("{}#[|{}]#foo{}", pair.0, pair.1, LINE_END),
"i<backspace>", "i<backspace>",
format!("#[f|]#oo{}", LINE_END), format!("#[|f]#oo{}", LINE_END),
)) ))
.await?; .await?;
} }
@ -940,7 +1052,7 @@ async fn delete_before_eol() -> anyhow::Result<()> {
close = pair.1 close = pair.1
), ),
"i<backspace>", "i<backspace>",
format!("{0}#[{0}|]#", LINE_END), format!("{0}#[|{0}]#", LINE_END),
)) ))
.await?; .await?;
} }
@ -971,25 +1083,6 @@ async fn delete_auto_pairs_disabled() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread")]
async fn delete_multi_range() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"{open}#[|{close}]#{eol}{open}#(|{close})#{eol}{open}#(|{close})#{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"i<backspace>",
format!("#[{eol}|]##({eol}|)##({eol}|)#", eol = LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> { async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS { for pair in DEFAULT_PAIRS {
@ -1016,7 +1109,7 @@ async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> {
pair.0, pair.1, LINE_END pair.0, pair.1, LINE_END
), ),
"i<backspace>", "i<backspace>",
format!("hello #[👨‍👩‍👧‍👦|]# goodbye{}", LINE_END), format!("hello #[|👨‍👩‍👧‍👦]# goodbye{}", LINE_END),
)) ))
.await?; .await?;
@ -1072,7 +1165,7 @@ async fn delete_nested_open_inside_pair() -> anyhow::Result<()> {
), ),
"i<backspace>", "i<backspace>",
format!( format!(
"{open}#[{close}|]#{eol}", "{open}#[|{close}]#{eol}",
open = pair.0, open = pair.0,
close = pair.1, close = pair.1,
eol = LINE_END eol = LINE_END
@ -1103,7 +1196,7 @@ async fn delete_nested_open_inside_pair_multi() -> anyhow::Result<()> {
), ),
"i<backspace>", "i<backspace>",
format!( format!(
"{outer_open}#[{outer_close}|]#{eol}{outer_open}#({outer_close}|)#{eol}{outer_open}#({outer_close}|)#{eol}", "{outer_open}#[|{outer_close}]#{eol}{outer_open}#(|{outer_close})#{eol}{outer_open}#(|{outer_close})#{eol}",
outer_open = outer_pair.0, outer_open = outer_pair.0,
outer_close = outer_pair.1, outer_close = outer_pair.1,
eol = LINE_END eol = LINE_END
@ -1187,7 +1280,7 @@ async fn delete_mixed_dedent() -> anyhow::Result<()> {
), ),
"i<backspace>", "i<backspace>",
indoc! {"\ indoc! {"\
bar = #[\n|]# bar = #[|\n]#
#(|\n)# #(|\n)#
fo#(|\n)# fo#(|\n)#
"}, "},

Loading…
Cancel
Save