use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn change_line_above_comment() -> anyhow::Result<()> {
// <https://github.com/helix-editor/helix/issues/12570>
test((
indoc! {"\
#[fn main() {}
|]#// a comment
"},
":lang rust<ret>c",
indoc! {"\
#[
|]#// a comment
"},
))
.await?;
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_newline_many_selections() -> anyhow::Result<()> {
test((
indoc! {"\
#(|o)#ne
#(|t)#wo
#[|t]#hree
"},
"i<ret>",
indoc! {"\
\n#(|o)#ne
#(|t)#wo
#[|t]#hree
"},
))
.await?;
// In this case the global offset that adjusts selections for inserted and deleted text
// should become negative because more text is deleted than is inserted.
test((
indoc! {"\
#[|๐ดโโ ๏ธ]# #(|๐ดโโ ๏ธ)# #(|๐ดโโ ๏ธ)#
#(|๐ดโโ ๏ธ)# #(|๐ดโโ ๏ธ)# #(|๐ดโโ ๏ธ)#
"},
"i<ret>",
indoc! {"\
\n#[|๐ดโโ ๏ธ]#
#(|๐ดโโ ๏ธ)#
#(|๐ดโโ ๏ธ)#
#(|๐ดโโ ๏ธ)#
#(|๐ดโโ ๏ธ)#
#(|๐ดโโ ๏ธ)#
"},
))
.await?;
// <https://github.com/helix-editor/helix/issues/12495>
test((
indoc! {"\
id #(|1)#,Item #(|1)#,cost #(|1)#,location #(|1)#
id #(|2)#,Item #(|2)#,cost #(|2)#,location #(|2)#
id #(|1)##(|0)#,Item #(|1)##(|0)#,cost #(|1)##(|0)#,location #(|1)##[|0]#"},
"i<ret>",
indoc! {"\
id
#(|1)#,Item
#(|1)#,cost
#(|1)#,location
#(|1)#
id
#(|2)#,Item
#(|2)#,cost
#(|2)#,location
#(|2)#
id
#(|1)#
#(|0)#,Item
#(|1)#
#(|0)#,cost
#(|1)#
#(|0)#,location
#(|1)#
#[|0]#"},
))
.await?;
// <https://github.com/helix-editor/helix/issues/12461>
test((
indoc! {"\
real Rใ #(||)# ใreal Rใ @ ใreal Rใ
#(||)# ใreal Rใ + ใureal Rใ i #(||)# ใreal Rใ - ใureal Rใ i
#(||)# ใreal Rใ + i #(||)# ใreal Rใ - i #(||)# ใreal Rใ ใinfnanใ i
#(||)# + ใureal Rใ i #(||)# - ใureal Rใ i
#(||)# ใinfnanใ i #(||)# + i #[||]# - i"},
"i<ret>",
indoc! {"\
real Rใ
#(||)# ใreal Rใ @ ใreal Rใ
#(||)# ใreal Rใ + ใureal Rใ i
#(||)# ใreal Rใ - ใureal Rใ i
#(||)# ใreal Rใ + i
#(||)# ใreal Rใ - i
#(||)# ใreal Rใ ใinfnanใ i
#(||)# + ใureal Rใ i
#(||)# - ใureal Rใ i
#(||)# ใinfnanใ i
#(||)# + i
#[||]# - i"},
))
.await?;
Ok(())
}
#[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?;
// Comment continuation should work on multiple selections.
// <https://github.com/helix-editor/helix/issues/12539>
test((
indoc! {"\
///ยทDocs#[|ยท]#
pubยทstructยทA;
///ยทDocs#(|ยท)#
pubยทstructยทB;
"}
.replace('ยท', " "),
":lang rust<ret>i<ret><ret>",
indoc! {"\
///ยทDocs
///
///ยท#[|ยท]#
pubยทstructยทA;
///ยทDocs
///
///ยท#(|ยท)#
pubยทstructยทB;
"}
.replace('ยท', " "),
))
.await?;
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(())
}
#[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 4<ret>O",
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 rust<ret>O",
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 4<ret>o",
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 rust<ret>o",
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<()> {
// `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(())
}
#[tokio::test(flavor = "multi_thread")]
async fn try_restore_indent() -> anyhow::Result<()> {
// Assert that `helix_view::editor::try_restore_indent` handles line endings correctly
// endings.
test((
indoc! {"\
if true #[|{]#
}
"},
// `try_restore_indent` should remove the indentation when adding a blank line.
":lang rust<ret>o<esc>",
indoc! {"\
if true {
#[
|]#}
"},
))
.await?;
Ok(())
}