insert double whitespace inside pair

pull/7269/head
Skyler Hawthorne 1 year ago
parent 00c75e6808
commit 8711b9e4fa

@ -127,6 +127,8 @@ pub fn hook_insert(
// && char_at pos == close // && char_at pos == close
return handle_insert_close(doc, range, pair); return handle_insert_close(doc, range, pair);
} }
} else if ch.is_whitespace() {
return handle_insert_whitespace(doc, range, ch, pairs);
} }
None None
@ -139,12 +141,33 @@ pub fn hook_delete(doc: &Rope, range: &Range, pairs: &AutoPairs) -> Option<(Dele
let cur = doc.get_char(cursor)?; let cur = doc.get_char(cursor)?;
let prev = prev_char(doc, cursor)?; let prev = prev_char(doc, cursor)?;
// check for whitespace surrounding a pair
if doc.len_chars() >= 4 && prev.is_whitespace() && cur.is_whitespace() {
let second_prev = doc.get_char(graphemes::nth_prev_grapheme_boundary(text, cursor, 2))?;
let second_next = doc.get_char(graphemes::next_grapheme_boundary(text, cursor))?;
log::debug!("second_prev: {}, second_next: {}", second_prev, second_next);
if let Some(pair) = pairs.get(second_prev) {
if pair.open == second_prev && pair.close == second_next {
return handle_delete(doc, range);
}
}
}
let pair = pairs.get(cur)?; let pair = pairs.get(cur)?;
if pair.open != prev { if pair.open != prev || pair.close != cur {
return None; return None;
} }
handle_delete(doc, range)
}
pub fn handle_delete(doc: &Rope, range: &Range) -> Option<(Deletion, Range)> {
let text = doc.slice(..);
let cursor = range.cursor(text);
let end_next = graphemes::next_grapheme_boundary(text, cursor); let end_next = graphemes::next_grapheme_boundary(text, cursor);
let end_prev = graphemes::prev_grapheme_boundary(text, cursor); let end_prev = graphemes::prev_grapheme_boundary(text, cursor);
@ -162,6 +185,30 @@ pub fn hook_delete(doc: &Rope, range: &Range, pairs: &AutoPairs) -> Option<(Dele
Some((delete, next_range)) Some((delete, next_range))
} }
fn handle_insert_whitespace(
doc: &Rope,
range: &Range,
ch: char,
pairs: &AutoPairs,
) -> Option<(Change, Range)> {
let text = doc.slice(..);
let cursor = range.cursor(text);
let cur = doc.get_char(cursor)?;
let prev = prev_char(doc, cursor)?;
let pair = pairs.get(cur)?;
if pair.open != prev || pair.close != cur {
return None;
}
let whitespace_pair = Pair {
open: ch,
close: ch,
};
handle_insert_same(doc, range, &whitespace_pair)
}
fn prev_char(doc: &Rope, pos: usize) -> Option<char> { fn prev_char(doc: &Rope, pos: usize) -> Option<char> {
if pos == 0 { if pos == 0 {
return None; return None;

@ -16,10 +16,119 @@ fn matching_pairs() -> impl Iterator<Item = &'static (char, char)> {
async fn insert_basic() -> anyhow::Result<()> { async fn insert_basic() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS { for pair in DEFAULT_PAIRS {
test(( test((
format!("#[{}|]#", LINE_END), "#[\n|]#",
format!("i{}", pair.0), format!("i{}", pair.0),
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END), format!("{}#[|{}]#", pair.0, pair.1),
LineFeedHandling::AsIs, ))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{}#[|{}]#", pair.0, pair.1),
"i ",
format!("{} #[| ]#{}", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!(
indoc! {"\
{open}#[|{close}]#
{open}#(|{open})#{close}{close}
{open}{open}#(|{close}{close})#
foo#(|\n)#
"},
open = pair.0,
close = pair.1,
),
"i ",
format!(
indoc! {"\
{open} #[| ]#{close}
{open} #(|{open})#{close}{close}
{open}{open} #(| {close}{close})#
foo #(|\n)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn append_whitespace_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!(
indoc! {"\
#[|{open}]#{close}
#(|{open})#{open}{close}{close}
#(|{open}{open})#{close}{close}
#(|foo)#
"},
open = pair.0,
close = pair.1,
),
"a ",
format!(
indoc! {"\
#[{open} |]#{close}
#({open} {open}|)#{close}{close}
#({open}{open} |)#{close}{close}
#(foo \n|)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_no_pair() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
// sanity check - do not insert extra whitespace unless immediately
// surrounded by a pair
test((
format!("{} #[|{}]#", pair.0, pair.1),
"i ",
format!("{} #[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_no_matching_pair() -> anyhow::Result<()> {
for pair in differing_pairs() {
// sanity check - verify whitespace does not insert unless both pairs
// are matches, i.e. no two different openers
test((
format!("{}#[|{}]#", pair.0, pair.0),
"i ",
format!("{} #[|{}]#", pair.0, pair.0),
)) ))
.await?; .await?;
} }
@ -596,6 +705,112 @@ async fn delete_multi() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{} #[| ]#{}", pair.0, pair.1),
"i<backspace>",
format!("{}#[{}|]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
indoc! {"\
{open} #[| ]#{close}
{open} #(|{open})#{close}{close}
{open}{open} #(| {close}{close})#
foo #(|\n)#
"},
open = pair.0,
close = pair.1,
),
"i<backspace>",
format!(
indoc! {"\
{open}#[{close}|]#
{open}#(|{open})#{close}{close}
{open}{open}#(|{close}{close})#
foo#(|\n)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_whitespace_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
indoc! {"\
#[{open} |]# {close}
#({open} |)#{open}{close}{close}
#({open}{open} |)# {close}{close}
#(foo |)#
"},
open = pair.0,
close = pair.1,
),
"a<backspace>",
format!(
indoc! {"\
#[{open}{close}|]#
#({open}{open}|)#{close}{close}
#({open}{open}{close}|)#{close}
#(foo\n|)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_no_pair() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{} #[|{}]#", pair.0, pair.1),
"i<backspace>",
format!("{} #[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_no_matching_pair() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!("{} #[|{}]#", pair.0, pair.0),
"i<backspace>",
format!("{}#[|{}]#", pair.0, pair.0),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> { async fn delete_configured_multi_byte_chars() -> anyhow::Result<()> {
// NOTE: these are multi-byte Unicode characters // NOTE: these are multi-byte Unicode characters
@ -827,6 +1042,7 @@ async fn delete_at_end_of_document() -> anyhow::Result<()> {
in_keys: String::from("i<backspace>"), in_keys: String::from("i<backspace>"),
out_text: String::from(LINE_END), out_text: String::from(LINE_END),
out_selection: Selection::single(LINE_END.len(), LINE_END.len()), out_selection: Selection::single(LINE_END.len(), LINE_END.len()),
line_feed_handling: LineFeedHandling::AsIs,
}) })
.await?; .await?;
@ -836,6 +1052,7 @@ async fn delete_at_end_of_document() -> anyhow::Result<()> {
in_keys: String::from("i<backspace>"), in_keys: String::from("i<backspace>"),
out_text: format!("foo{}", LINE_END), out_text: format!("foo{}", LINE_END),
out_selection: Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()), out_selection: Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()),
line_feed_handling: LineFeedHandling::AsIs,
}) })
.await?; .await?;
} }
@ -960,57 +1177,57 @@ async fn delete_append_end_of_word() -> anyhow::Result<()> {
async fn delete_mixed_dedent() -> anyhow::Result<()> { async fn delete_mixed_dedent() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS { for pair in DEFAULT_PAIRS {
test(( test((
helpers::platform_line(&format!( format!(
indoc! {"\ indoc! {"\
bar = {}#[|{}]# bar = {}#[|{}]#
#(|\n)# #(|\n)#
foo#(|\n)# foo#(|\n)#
"}, "},
pair.0, pair.1, pair.0, pair.1,
)), ),
"i<backspace>", "i<backspace>",
helpers::platform_line(indoc! {"\ indoc! {"\
bar = #[\n|]# bar = #[\n|]#
#(|\n)# #(|\n)#
fo#(|\n)# fo#(|\n)#
"}), "},
)) ))
.await?; .await?;
test(( test((
helpers::platform_line(&format!( format!(
indoc! {"\ indoc! {"\
bar = {}#[|{}woop]# bar = {}#[|{}woop]#
#(|word)# #(|word)#
fo#(|o)# fo#(|o)#
"}, "},
pair.0, pair.1, pair.0, pair.1,
)), ),
"i<backspace>", "i<backspace>",
helpers::platform_line(indoc! {"\ indoc! {"\
bar = #[|woop]# bar = #[|woop]#
#(|word)# #(|word)#
f#(|o)# f#(|o)#
"}), "},
)) ))
.await?; .await?;
// delete from the right with append // delete from the right with append
test(( test((
helpers::platform_line(&format!( format!(
indoc! {"\ indoc! {"\
bar = #[|woop{}]#{} bar = #[|woop{}]#{}
#(| )#word #(| )#word
#(|fo)#o #(|fo)#o
"}, "},
pair.0, pair.1, pair.0, pair.1,
)), ),
"a<backspace>", "a<backspace>",
helpers::platform_line(indoc! {"\ indoc! {"\
bar = #[woop\n|]# bar = #[woop\n|]#
#(w|)#ord #(w|)#ord
#(fo|)# #(fo|)#
"}), "},
)) ))
.await?; .await?;
} }

Loading…
Cancel
Save