Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--helix-term/src/commands.rs73
-rw-r--r--helix-term/tests/test/commands/insert.rs125
2 files changed, 167 insertions, 31 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 27c852a7..6a35e812 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -3458,43 +3458,42 @@ fn open(cx: &mut Context, open: Open) {
let selection = doc.selection(view.id);
let mut ranges = SmallVec::with_capacity(selection.len());
- let mut offs = 0;
let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
- let cursor_line = text.char_to_line(match open {
+ // the line number, where the cursor is currently
+ let curr_line_num = text.char_to_line(match open {
Open::Below => graphemes::prev_grapheme_boundary(text, range.to()),
Open::Above => range.from(),
});
- let new_line = match open {
- // adjust position to the end of the line (next line - 1)
- Open::Below => cursor_line + 1,
- // adjust position to the end of the previous line (current line - 1)
- Open::Above => cursor_line,
+ // the next line number, where the cursor will be, after finishing the transaction
+ let next_new_line_num = match open {
+ Open::Below => curr_line_num + 1,
+ Open::Above => curr_line_num,
};
- let line_num = new_line.saturating_sub(1);
+ let above_next_new_line_num = next_new_line_num.saturating_sub(1);
+
+ let continue_comment_token = if doc.config.load().continue_comments {
+ doc.language_config()
+ .and_then(|config| config.comment_tokens.as_ref())
+ .and_then(|tokens| comment::get_comment_token(text, tokens, curr_line_num))
+ } else {
+ None
+ };
// Index to insert newlines after, as well as the char width
// to use to compensate for those inserted newlines.
- let (line_end_index, line_end_offset_width) = if new_line == 0 {
+ let (above_next_line_end_index, above_next_line_end_width) = if next_new_line_num == 0 {
(0, 0)
} else {
(
- line_end_char_index(&text, line_num),
+ line_end_char_index(&text, above_next_new_line_num),
doc.line_ending.len_chars(),
)
};
- let continue_comment_token = if doc.config.load().continue_comments {
- doc.language_config()
- .and_then(|config| config.comment_tokens.as_ref())
- .and_then(|tokens| comment::get_comment_token(text, tokens, cursor_line))
- } else {
- None
- };
-
- let line = text.line(cursor_line);
+ let line = text.line(curr_line_num);
let indent = match line.first_non_whitespace_char() {
Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(),
_ => indent::indent_for_newline(
@@ -3504,26 +3503,36 @@ fn open(cx: &mut Context, open: Open) {
&doc.indent_style,
doc.tab_width(),
text,
- line_num,
- line_end_index,
- cursor_line,
+ above_next_new_line_num,
+ above_next_line_end_index,
+ curr_line_num,
),
};
let indent_len = indent.len();
let mut text = String::with_capacity(1 + indent_len);
- text.push_str(doc.line_ending.as_str());
- text.push_str(&indent);
- if let Some(token) = continue_comment_token {
- text.push_str(token);
- text.push(' ');
+ if open == Open::Above && next_new_line_num == 0 {
+ text.push_str(&indent);
+ if let Some(token) = continue_comment_token {
+ text.push_str(token);
+ text.push(' ');
+ }
+ text.push_str(doc.line_ending.as_str());
+ } else {
+ text.push_str(doc.line_ending.as_str());
+ text.push_str(&indent);
+
+ if let Some(token) = continue_comment_token {
+ text.push_str(token);
+ text.push(' ');
+ }
}
let text = text.repeat(count);
// calculate new selection ranges
- let pos = offs + line_end_index + line_end_offset_width;
+ let pos = 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();
@@ -3536,9 +3545,11 @@ fn open(cx: &mut Context, open: Open) {
));
}
- offs += text.chars().count();
-
- (line_end_index, line_end_index, Some(text.into()))
+ (
+ above_next_line_end_index,
+ above_next_line_end_index,
+ Some(text.into()),
+ )
});
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
diff --git a/helix-term/tests/test/commands/insert.rs b/helix-term/tests/test/commands/insert.rs
index ac2d179a..f7aa4a02 100644
--- a/helix-term/tests/test/commands/insert.rs
+++ b/helix-term/tests/test/commands/insert.rs
@@ -119,3 +119,128 @@ async fn insert_newline_continue_line_comment() -> anyhow::Result<()> {
Ok(())
}
+
+/// NOTE: Language is set to markdown to check if the indentation is correct for the new line
+#[tokio::test(flavor = "multi_thread")]
+async fn test_open_above() -> anyhow::Result<()> {
+ // `O` is pressed in the first line
+ test((
+ indoc! {"Helix #[is|]# cool"},
+ ":lang markdown<ret>O",
+ indoc! {"\
+ #[\n|]#
+ Helix is cool
+ "},
+ ))
+ .await?;
+
+ // `O` is pressed in the first line, but the current line has some indentation
+ test((
+ indoc! {"\
+ ··This line has 2 spaces in front of it#[\n|]#
+ "}
+ .replace('·', " "),
+ ":lang markdown<ret>Oa",
+ indoc! {"\
+ ··a#[\n|]#
+ ··This line has 2 spaces in front of it
+ "}
+ .replace('·', " "),
+ ))
+ .await?;
+
+ // `O` is pressed but *not* in the first line
+ test((
+ indoc! {"\
+ I use
+ b#[t|]#w.
+ "},
+ ":lang markdown<ret>Oarch",
+ indoc! {"\
+ I use
+ arch#[\n|]#
+ btw.
+ "},
+ ))
+ .await?;
+
+ // `O` is pressed but *not* in the first line and the line has some indentation
+ test((
+ indoc! {"\
+ I use
+ ····b#[t|]#w.
+ "}
+ .replace("·", " "),
+ ":lang markdown<ret>Ohelix",
+ indoc! {"\
+ I use
+ ····helix#[\n|]#
+ ····btw.
+ "}
+ .replace("·", " "),
+ ))
+ .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<()> {
+ // `O` is pressed in the first line inside a line comment
+ test((
+ indoc! {"// a commen#[t|]#"},
+ ":lang rust<ret>O",
+ indoc! {"\
+ // #[\n|]#
+ // a comment
+ "},
+ ))
+ .await?;
+
+ // `O` is pressed in the first line inside a line comment, but with indentation
+ test((
+ indoc! {"····// a comm#[e|]#nt"}.replace("·", " "),
+ ":lang rust<ret>O",
+ indoc! {"\
+ ····// #[\n|]#
+ ····// a comment
+ "}
+ .replace("·", " "),
+ ))
+ .await?;
+
+ // `O` is pressed but not in the first line but inside a line comment
+ test((
+ indoc! {"\
+ fn main() { }
+ // yeetus deletus#[\n|]#
+ "},
+ ":lang rust<ret>O",
+ indoc! {"\
+ fn main() { }
+ // #[\n|]#
+ // yeetus deletus
+ "},
+ ))
+ .await?;
+
+ // `O` is pressed but not in the first line but inside a line comment and with indentation
+ test((
+ indoc! {"\
+ fn main() { }
+ ····// yeetus deletus#[\n|]#
+ "}
+ .replace("·", " "),
+ ":lang rust<ret>O",
+ indoc! {"\
+ fn main() { }
+ ····// #[\n|]#
+ ····// yeetus deletus
+ "}
+ .replace("·", " "),
+ ))
+ .await?;
+
+ Ok(())
+}