Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trim all trailing whitespace on insert_newline #12177

Merged
merged 2 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3961,10 +3961,7 @@ pub mod insert {
let curr = contents.get_char(pos).unwrap_or(' ');

let current_line = text.char_to_line(pos);
let line_is_only_whitespace = text
.line(current_line)
.chars()
.all(|char| char.is_ascii_whitespace());
let line_start = text.line_to_char(current_line);

let mut new_text = String::new();

Expand All @@ -3973,15 +3970,10 @@ pub mod insert {
.and_then(|config| config.comment_tokens.as_ref())
.and_then(|tokens| comment::get_comment_token(text, tokens, current_line));

// If the current line is all whitespace, insert a line ending at the beginning of
// the current line. This makes the current line empty and the new line contain the
// indentation of the old line.
let (from, to, local_offs) = if line_is_only_whitespace {
let line_start = text.line_to_char(current_line);
new_text.push_str(doc.line_ending.as_str());

(line_start, line_start, new_text.chars().count())
} else {
let (from, to, local_offs) = if let Some(idx) =
text.slice(line_start..pos).last_non_whitespace_char()
{
let first_trailing_whitespace_char = (line_start + idx + 1).min(pos);
let line = text.line(current_line);

let indent = match line.first_non_whitespace_char() {
Expand Down Expand Up @@ -4034,20 +4026,34 @@ pub mod insert {
new_text.chars().count()
};

(pos, pos, local_offs)
(
first_trailing_whitespace_char,
pos,
// Note that `first_trailing_whitespace_char` is at least `pos` so the
// unsigned subtraction (`pos - first_trailing_whitespace_char`) cannot
// underflow.
local_offs as isize - (pos - first_trailing_whitespace_char) as isize,
)
} else {
// If the current line is all whitespace, insert a line ending at the beginning of
// the current line. This makes the current line empty and the new line contain the
// indentation of the old line.
new_text.push_str(doc.line_ending.as_str());

(line_start, line_start, new_text.chars().count() as isize)
};

let new_range = if range.cursor(text) > range.anchor {
// when appending, extend the range by local_offs
Range::new(
range.anchor + global_offs,
range.head + local_offs + global_offs,
(range.head as isize + local_offs) as usize + global_offs,
)
} else {
// when inserting, slide the range by local_offs
Range::new(
range.anchor + local_offs + global_offs,
range.head + local_offs + global_offs,
(range.anchor as isize + local_offs) as usize + global_offs,
(range.head as isize + local_offs) as usize + global_offs,
)
};

Expand Down
1 change: 1 addition & 0 deletions helix-term/tests/test/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use helix_term::application::Application;

use super::*;

mod insert;
mod movement;
mod write;

Expand Down
121 changes: 121 additions & 0 deletions helix-term/tests/test/commands/insert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use super::*;

#[tokio::test(flavor = "multi_thread")]
async fn insert_newline_trim_trailing_whitespace() -> anyhow::Result<()> {
// Trailing whitespace is trimmed.
test((
indoc! {"\
hello·······#[|
]#world
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[|
]#world
"}
.replace('·', " "),
))
.await?;

// Whitespace that would become trailing is trimmed too.
test((
indoc! {"\
hello········#[|w]#orld
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[|w]#orld
"}
.replace('·', " "),
))
.await?;

// Only whitespace before the cursor is trimmed.
test((
indoc! {"\
hello········#[|·]#····world
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[|·]#····world
"}
.replace('·', " "),
))
.await?;

Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn insert_newline_continue_line_comment() -> anyhow::Result<()> {
// `insert_newline` continues a single line comment
test((
indoc! {"\
// Hello world!#[|
]#
"},
":lang rust<ret>i<ret>",
indoc! {"\
// Hello world!
// #[|
]#
"},
))
.await?;

// The comment is not continued if the cursor is before the comment token. (Note that we
// are entering insert-mode with `I`.)
test((
indoc! {"\
// Hello world!#[|
]#
"},
":lang rust<ret>I<ret>",
indoc! {"\
\n#[/|]#/ Hello world!
"},
))
.await?;

// `insert_newline` again clears the whitespace on the first continued comment and continues
// the comment again.
test((
indoc! {"\
// Hello world!
// #[|
]#
"},
":lang rust<ret>i<ret>",
indoc! {"\
// Hello world!
//
// #[|
]#
"},
))
.await?;

// Line comment continuation and trailing whitespace is also trimmed when using
// `insert_newline` in the middle of a comment.
test((
indoc! {"\
//·hello····#[|·]#····world
"}
.replace('·', " "),
":lang rust<ret>i<ret>",
indoc! {"\
//·hello
//·#[|·]#····world
"}
.replace('·', " "),
))
.await?;

Ok(())
}
2 changes: 1 addition & 1 deletion helix-term/tests/test/languages/yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ async fn auto_indent() -> anyhow::Result<()> {
"##},
"i<ret>",
indoc! {"\
foo:
foo:
#[|b]#ar
"},
),
Expand Down
Loading