From c7a056632064c3ead1a2833d65003175eff8b69b Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Mon, 4 Dec 2023 20:38:41 +0100 Subject: [PATCH 1/7] fix: #8977 fixes the issue that lines with only spaces are getting joined as well --- helix-term/src/commands.rs | 40 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index cd053266c2e0..d2431463ae4a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4390,28 +4390,39 @@ fn format_selections(cx: &mut Context) { fn join_selections_impl(cx: &mut Context, select_space: bool) { use movement::skip_while; let (view, doc) = current!(cx.editor); - let text = doc.text(); - let slice = doc.text().slice(..); let mut changes = Vec::new(); - let fragment = Tendril::from(" "); for selection in doc.selection(view.id) { - let (start, mut end) = selection.line_range(slice); - if start == end { - end = (end + 1).min(text.len_lines() - 1); - } - let lines = start..end; + let slice = doc.text().slice(..); + let text = doc.text(); - changes.reserve(lines.len()); + let selected_lines = { + let (start, mut end) = selection.line_range(slice); + if start == end { + end = (end + 1).min(text.len_lines() - 1); + } + start..end + }; - for line in lines { - let start = line_end_char_index(&slice, line); - let mut end = text.line_to_char(line + 1); + for selected_line in selected_lines { + let start = line_end_char_index(&slice, selected_line); + let mut end = text.line_to_char(selected_line + 1); end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end); // need to skip from start, not end - let change = (start, end, Some(fragment.clone())); + let change = { + let separator = { + let line_contains_only_space = text.char(end) == '\n'; + if line_contains_only_space { + None + } else { + Some(Tendril::from(" ")) + } + }; + + (start, end, separator) + }; changes.push(change); } } @@ -4424,9 +4435,6 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { changes.sort_unstable_by_key(|(from, _to, _text)| *from); changes.dedup(); - // TODO: joining multiple empty lines should be replaced by a single space. - // need to merge change ranges that touch - // select inserted spaces let transaction = if select_space { let ranges: SmallVec<_> = changes From 2b12ada9c296b4c9f82974dfd1b29f452c9d1568 Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Tue, 5 Dec 2023 03:22:04 +0100 Subject: [PATCH 2/7] reverting some renamings --- helix-term/src/commands.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d2431463ae4a..3410514af367 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4390,24 +4390,24 @@ fn format_selections(cx: &mut Context) { fn join_selections_impl(cx: &mut Context, select_space: bool) { use movement::skip_while; let (view, doc) = current!(cx.editor); + let text = doc.text(); + let slice = text.slice(..); let mut changes = Vec::new(); for selection in doc.selection(view.id) { - let slice = doc.text().slice(..); - let text = doc.text(); - - let selected_lines = { + let lines = { let (start, mut end) = selection.line_range(slice); if start == end { end = (end + 1).min(text.len_lines() - 1); } start..end }; + changes.reserve(lines.len()); - for selected_line in selected_lines { - let start = line_end_char_index(&slice, selected_line); - let mut end = text.line_to_char(selected_line + 1); + for line in lines { + let start = line_end_char_index(&slice, line); + let mut end = text.line_to_char(line + 1); end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end); // need to skip from start, not end @@ -4446,9 +4446,9 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { }) .collect(); let selection = Selection::new(ranges, 0); - Transaction::change(doc.text(), changes.into_iter()).with_selection(selection) + Transaction::change(text, changes.into_iter()).with_selection(selection) } else { - Transaction::change(doc.text(), changes.into_iter()) + Transaction::change(text, changes.into_iter()) }; doc.apply(&transaction, view.id); From 67b2855cd482b0f999e029893bbad2523837e4a7 Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Tue, 5 Dec 2023 03:34:05 +0100 Subject: [PATCH 3/7] improve empty line check --- helix-term/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3410514af367..b0185b123d7e 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4413,7 +4413,7 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { // need to skip from start, not end let change = { let separator = { - let line_contains_only_space = text.char(end) == '\n'; + let line_contains_only_space = LineEnding::from_char(text.char(end)).is_some(); if line_contains_only_space { None } else { From 24c5f613ad362e3a2dd5186162ee403f5ea2eec4 Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Tue, 5 Dec 2023 03:54:28 +0100 Subject: [PATCH 4/7] adding integration test --- helix-term/tests/test/commands.rs | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index b3e135510632..e52b142c6b68 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -480,3 +480,49 @@ fn bar() {#(\n|)#\ Ok(()) } + +#[tokio::test(flavor = "multi_thread")] +async fn test_join_selections() -> anyhow::Result<()> { + // normal join + test(( + platform_line(indoc! {"\ + #[a|]#bc + def + "}), + "J", + platform_line(indoc! {"\ + #[a|]#bc def + "}), + )) + .await?; + + // join with empty line + test(( + platform_line(indoc! {"\ + #[a|]#bc + + def + "}), + "JJ", + platform_line(indoc! {"\ + #[a|]#bc def + "}), + )) + .await?; + + // join with additional space in non-empty line + test(( + platform_line(indoc! {"\ + #[a|]#bc + + def + "}), + "JJ", + platform_line(indoc! {"\ + #[a|]#bc def + "}), + )) + .await?; + + Ok(()) +} From c845c964dea3185c2c95dbbb7bb0c415107478f2 Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Tue, 5 Dec 2023 16:28:00 +0100 Subject: [PATCH 5/7] reverting code block --- helix-term/src/commands.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b0185b123d7e..67b5c8cbdceb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4396,13 +4396,12 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { let mut changes = Vec::new(); for selection in doc.selection(view.id) { - let lines = { - let (start, mut end) = selection.line_range(slice); - if start == end { - end = (end + 1).min(text.len_lines() - 1); - } - start..end - }; + let (start, mut end) = selection.line_range(slice); + if start == end { + end = (end + 1).min(text.len_lines() - 1); + } + let lines = start..end; + changes.reserve(lines.len()); for line in lines { From d8f62f7cd86e29816a3fd0d0cfa80f40055b63fb Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Tue, 5 Dec 2023 19:27:41 +0100 Subject: [PATCH 6/7] fix conditon check for line end --- helix-term/src/commands.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 67b5c8cbdceb..0c69da255372 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4412,7 +4412,8 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { // need to skip from start, not end let change = { let separator = { - let line_contains_only_space = LineEnding::from_char(text.char(end)).is_some(); + let line_end_index = line_end_char_index(&slice, line + 1); + let line_contains_only_space = end == line_end_index; if line_contains_only_space { None } else { From f2f6bc605fe18e7343bcd5ff28218f5873de3106 Mon Sep 17 00:00:00 2001 From: TornaxO7 Date: Wed, 6 Dec 2023 15:23:05 +0100 Subject: [PATCH 7/7] applying suggested style --- helix-term/src/commands.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 0c69da255372..e557977adf88 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4409,21 +4409,13 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { let mut end = text.line_to_char(line + 1); end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end); - // need to skip from start, not end - let change = { - let separator = { - let line_end_index = line_end_char_index(&slice, line + 1); - let line_contains_only_space = end == line_end_index; - if line_contains_only_space { - None - } else { - Some(Tendril::from(" ")) - } - }; - - (start, end, separator) + let separator = if end == line_end_char_index(&slice, line + 1) { + // the joining line contains only space-characters => don't include a whitespace when joining + None + } else { + Some(Tendril::from(" ")) }; - changes.push(change); + changes.push((start, end, separator)); } }