mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-23 09:56:19 +04:00
48a3965ab4
* Fix range offsets in multi-selection paste
d6323b7cbc
introduced a regression with
multi-selection paste where pasting would not adjust the ranges
correctly. To fix it, we need to track the total number of characters
inserted in each changed selection and use that offset to slide each
new range forwards.
* Inherit selection directions on paste
* Add an integration-test for multi-selection pasting
218 lines
5.4 KiB
Rust
218 lines
5.4 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(())
|
|
}
|