diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3b90648742dd..755a7dc077bb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3474,6 +3474,7 @@ fn open(cx: &mut Context, open: Open) { let text = doc.text().slice(..); let contents = doc.text(); let selection = doc.selection(view.id); + let mut offs = 0; let mut ranges = SmallVec::with_capacity(selection.len()); @@ -3550,7 +3551,7 @@ fn open(cx: &mut Context, open: Open) { let text = text.repeat(count); // calculate new selection ranges - let pos = above_next_line_end_index + above_next_line_end_width; + let pos = offs + above_next_line_end_index + above_next_line_end_width; let comment_len = continue_comment_token .map(|token| token.len() + 1) // `+ 1` for the extra space added .unwrap_or_default(); @@ -3563,6 +3564,9 @@ fn open(cx: &mut Context, open: Open) { )); } + // update the offset for the next range + offs += text.chars().count(); + ( above_next_line_end_index, above_next_line_end_index, diff --git a/helix-term/tests/test/commands/insert.rs b/helix-term/tests/test/commands/insert.rs index f7aa4a020fb4..f23876dfbf8e 100644 --- a/helix-term/tests/test/commands/insert.rs +++ b/helix-term/tests/test/commands/insert.rs @@ -184,6 +184,127 @@ async fn test_open_above() -> anyhow::Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread")] +async fn test_open_above_with_multiple_cursors() -> anyhow::Result<()> { + // the primary cursor is also in the top line + test(( + indoc! {"#[H|]#elix + #(i|)#s + #(c|)#ool"}, + "O", + indoc! { + "#[\n|]# + Helix + #(\n|)# + is + #(\n|)# + cool + " + }, + )) + .await?; + + // now with some additional indentation + test(( + indoc! {"····#[H|]#elix + ····#(i|)#s + ····#(c|)#ool"} + .replace("·", " "), + ":indent-style 4O", + indoc! { + "····#[\n|]# + ····Helix + ····#(\n|)# + ····is + ····#(\n|)# + ····cool + " + } + .replace("·", " "), + )) + .await?; + + // the first line is within a comment, the second not. + // However, if we open above, the first newly added line should start within a comment + // while the other should be a normal line + test(( + indoc! {"fn main() { + // #[VIP|]# comment + l#(e|)#t yes = false; + }"}, + ":lang rustO", + indoc! {"fn main() { + // #[\n|]# + // VIP comment + #(\n|)# + let yes = false; + }"}, + )) + .await?; + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_open_below_with_multiple_cursors() -> anyhow::Result<()> { + // the primary cursor is also in the top line + test(( + indoc! {"#[H|]#elix + #(i|)#s + #(c|)#ool"}, + "o", + indoc! {"Helix + #[\n|]# + is + #(\n|)# + cool + #(\n|)# + " + }, + )) + .await?; + + // now with some additional indentation + test(( + indoc! {"····#[H|]#elix + ····#(i|)#s + ····#(c|)#ool"} + .replace("·", " "), + ":indent-style 4o", + indoc! { + "····Helix + ····#[\n|]# + ····is + ····#(\n|)# + ····cool + ····#(\n|)# + " + } + .replace("·", " "), + )) + .await?; + + // the first line is within a comment, the second not. + // However, if we open below, the first newly added line should start within a comment + // while the other should be a normal line + test(( + indoc! {"fn main() { + // #[VIP|]# comment + l#(e|)#t yes = false; + }"}, + ":lang rusto", + indoc! {"fn main() { + // VIP comment + // #[\n|]# + let yes = false; + #(\n|)# + }"}, + )) + .await?; + + Ok(()) +} + /// NOTE: To make the `open_above` comment-aware, we're setting the language for each test to rust. #[tokio::test(flavor = "multi_thread")] async fn test_open_above_with_comments() -> anyhow::Result<()> {