mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-23 18:06:18 +04:00
c74b97447f
d6323b7cbc
introduced a regression for shell commands like `|`, `!`, and `<A-!>` which caused the new selections to be incorrect. This caused a panic when piping (`|`) would cause the new range to extend past the document end. The paste version of this bug was fixed in48a3965ab4
. This change also inherits the direction of the new range from the old range and adds integration tests to ensure that the behavior isn't broken in the future.
286 lines
6.7 KiB
Rust
286 lines
6.7 KiB
Rust
use std::ops::RangeInclusive;
|
|
|
|
use helix_core::diagnostic::Severity;
|
|
use helix_term::application::Application;
|
|
|
|
use super::*;
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_write_quit_fail() -> anyhow::Result<()> {
|
|
let file = helpers::new_readonly_tempfile()?;
|
|
let mut app = helpers::AppBuilder::new()
|
|
.with_file(file.path(), None)
|
|
.build()?;
|
|
|
|
test_key_sequence(
|
|
&mut app,
|
|
Some("ihello<esc>:wq<ret>"),
|
|
Some(&|app| {
|
|
let mut docs: Vec<_> = app.editor.documents().collect();
|
|
assert_eq!(1, docs.len());
|
|
|
|
let doc = docs.pop().unwrap();
|
|
assert_eq!(Some(file.path()), doc.path().map(PathBuf::as_path));
|
|
assert_eq!(&Severity::Error, app.editor.get_status().unwrap().1);
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_buffer_close_concurrent() -> anyhow::Result<()> {
|
|
test_key_sequences(
|
|
&mut helpers::AppBuilder::new().build()?,
|
|
vec![
|
|
(
|
|
None,
|
|
Some(&|app| {
|
|
assert_eq!(1, app.editor.documents().count());
|
|
assert!(!app.editor.is_err());
|
|
}),
|
|
),
|
|
(
|
|
Some("ihello<esc>:new<ret>"),
|
|
Some(&|app| {
|
|
assert_eq!(2, app.editor.documents().count());
|
|
assert!(!app.editor.is_err());
|
|
}),
|
|
),
|
|
(
|
|
Some(":buffer<minus>close<ret>"),
|
|
Some(&|app| {
|
|
assert_eq!(1, app.editor.documents().count());
|
|
assert!(!app.editor.is_err());
|
|
}),
|
|
),
|
|
],
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
// verify if writes are queued up, it finishes them before closing the buffer
|
|
let mut file = tempfile::NamedTempFile::new()?;
|
|
let mut command = String::new();
|
|
const RANGE: RangeInclusive<i32> = 1..=1000;
|
|
|
|
for i in RANGE {
|
|
let cmd = format!("%c{}<esc>:w<ret>", i);
|
|
command.push_str(&cmd);
|
|
}
|
|
|
|
command.push_str(":buffer<minus>close<ret>");
|
|
|
|
let mut app = helpers::AppBuilder::new()
|
|
.with_file(file.path(), None)
|
|
.build()?;
|
|
|
|
test_key_sequence(
|
|
&mut app,
|
|
Some(&command),
|
|
Some(&|app| {
|
|
assert!(!app.editor.is_err(), "error: {:?}", app.editor.get_status());
|
|
|
|
let doc = app.editor.document_by_path(file.path());
|
|
assert!(doc.is_none(), "found doc: {:?}", doc);
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
helpers::assert_file_has_content(file.as_file_mut(), &RANGE.end().to_string())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_selection_duplication() -> anyhow::Result<()> {
|
|
// Forward
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[lo|]#rem
|
|
ipsum
|
|
dolor
|
|
"})
|
|
.as_str(),
|
|
"CC",
|
|
platform_line(indoc! {"\
|
|
#(lo|)#rem
|
|
#(ip|)#sum
|
|
#[do|]#lor
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
|
|
// Backward
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[|lo]#rem
|
|
ipsum
|
|
dolor
|
|
"})
|
|
.as_str(),
|
|
"CC",
|
|
platform_line(indoc! {"\
|
|
#(|lo)#rem
|
|
#(|ip)#sum
|
|
#[|do]#lor
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_goto_file_impl() -> anyhow::Result<()> {
|
|
let file = tempfile::NamedTempFile::new()?;
|
|
|
|
fn match_paths(app: &Application, matches: Vec<&str>) -> usize {
|
|
app.editor
|
|
.documents()
|
|
.filter_map(|d| d.path()?.file_name())
|
|
.filter(|n| matches.iter().any(|m| *m == n.to_string_lossy()))
|
|
.count()
|
|
}
|
|
|
|
// Single selection
|
|
test_key_sequence(
|
|
&mut AppBuilder::new().with_file(file.path(), None).build()?,
|
|
Some("ione.js<esc>%gf"),
|
|
Some(&|app| {
|
|
assert_eq!(1, match_paths(app, vec!["one.js"]));
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
// Multiple selection
|
|
test_key_sequence(
|
|
&mut AppBuilder::new().with_file(file.path(), None).build()?,
|
|
Some("ione.js<ret>two.js<esc>%<A-s>gf"),
|
|
Some(&|app| {
|
|
assert_eq!(2, match_paths(app, vec!["one.js", "two.js"]));
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
// Cursor on first quote
|
|
test_key_sequence(
|
|
&mut AppBuilder::new().with_file(file.path(), None).build()?,
|
|
Some("iimport 'one.js'<esc>B;gf"),
|
|
Some(&|app| {
|
|
assert_eq!(1, match_paths(app, vec!["one.js"]));
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
// Cursor on last quote
|
|
test_key_sequence(
|
|
&mut AppBuilder::new().with_file(file.path(), None).build()?,
|
|
Some("iimport 'one.js'<esc>bgf"),
|
|
Some(&|app| {
|
|
assert_eq!(1, match_paths(app, vec!["one.js"]));
|
|
}),
|
|
false,
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_multi_selection_paste() -> anyhow::Result<()> {
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[|lorem]#
|
|
#(|ipsum)#
|
|
#(|dolor)#
|
|
"})
|
|
.as_str(),
|
|
"yp",
|
|
platform_line(indoc! {"\
|
|
lorem#[|lorem]#
|
|
ipsum#(|ipsum)#
|
|
dolor#(|dolor)#
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
|
|
// pipe
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[|lorem]#
|
|
#(|ipsum)#
|
|
#(|dolor)#
|
|
"})
|
|
.as_str(),
|
|
"|echo foo<ret>",
|
|
platform_line(indoc! {"\
|
|
#[|foo
|
|
]#
|
|
#(|foo
|
|
)#
|
|
#(|foo
|
|
)#
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
|
|
// insert-output
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[|lorem]#
|
|
#(|ipsum)#
|
|
#(|dolor)#
|
|
"})
|
|
.as_str(),
|
|
"!echo foo<ret>",
|
|
platform_line(indoc! {"\
|
|
#[|foo
|
|
]#lorem
|
|
#(|foo
|
|
)#ipsum
|
|
#(|foo
|
|
)#dolor
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
|
|
// append-output
|
|
test((
|
|
platform_line(indoc! {"\
|
|
#[|lorem]#
|
|
#(|ipsum)#
|
|
#(|dolor)#
|
|
"})
|
|
.as_str(),
|
|
"<A-!>echo foo<ret>",
|
|
platform_line(indoc! {"\
|
|
lorem#[|foo
|
|
]#
|
|
ipsum#(|foo
|
|
)#
|
|
dolor#(|foo
|
|
)#
|
|
"})
|
|
.as_str(),
|
|
))
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|