Skip to content

Commit

Permalink
Fix continuing comment token for first line (#12215)
Browse files Browse the repository at this point in the history
  • Loading branch information
TornaxO7 authored Dec 10, 2024
1 parent 51ac3e0 commit 89a7cde
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 31 deletions.
73 changes: 42 additions & 31 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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();
Expand All @@ -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()));
Expand Down
125 changes: 125 additions & 0 deletions helix-term/tests/test/commands/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

0 comments on commit 89a7cde

Please sign in to comment.