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

Complete implementation with tests for issue#4087 #11849

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
56 changes: 54 additions & 2 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,44 @@ impl CommandSignature {
}
}

fn xit(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
)-> anyhow::Result<()> {

if event != PromptEvent::Validate {
return Ok(());
}

let (_view, doc) = current!(cx.editor);

if doc.is_modified() {
write_impl(cx, args.first(), false)?;
}
cx.block_try_flush_writes()?;
quit(cx, &[], event)
}

fn force_xit(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
)-> anyhow::Result<()> {

if event != PromptEvent::Validate {
return Ok(());
}

let (_view, doc) = current!(cx.editor);

if doc.is_modified() {
write_impl(cx, args.first(), true)?;
}
cx.block_try_flush_writes()?;
quit(cx, &[], event)
}

fn quit(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
log::debug!("quitting...");

Expand Down Expand Up @@ -2521,6 +2559,20 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
}

pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "xit",
aliases: &["x"],
doc: "Write changes to disk if any are made. Otherwise just close. Doesn't require a path if buffer is not modified.",
fun: xit,
signature: CommandSignature::positional(&[completers::filename]),
},
TypableCommand {
name: "xit!",
aliases: &["x!"],
doc: "Write changes to disk if any are made. Otherwise just close. Doesn't require a path if buffer is not modified. Force write.",
fun: force_xit,
signature: CommandSignature::positional(&[completers::filename]),
},
TypableCommand {
name: "quit",
aliases: &["q"],
Expand Down Expand Up @@ -2673,14 +2725,14 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
},
TypableCommand {
name: "write-quit",
aliases: &["wq", "x"],
aliases: &["wq"],
doc: "Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)",
fun: write_quit,
signature: CommandSignature::positional(&[completers::filename]),
},
TypableCommand {
name: "write-quit!",
aliases: &["wq!", "x!"],
aliases: &["wq!"],
doc: "Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
fun: force_write_quit,
signature: CommandSignature::positional(&[completers::filename]),
Expand Down
99 changes: 98 additions & 1 deletion helix-term/tests/test/commands/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,103 @@ use helix_view::doc;

use super::*;


#[tokio::test(flavor = "multi_thread")]
async fn test_xit_w_buffer_w_path() -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?;
let mut app = helpers::AppBuilder::new()
.with_file(file.path(), None)
.build()?;
//Check for write operation on given path and edited buffer
test_key_sequence(
&mut app,
Some("iBecause of the obvious threat to untold numbers of citizens due to the crisis that is even now developing, this radio station will remain on the air day and night.<ret><esc>:x<ret>"),
None,
true,
)
.await?;


reload_file(&mut file).unwrap();
let mut file_content = String::new();
file.as_file_mut().read_to_string(&mut file_content)?;

assert_eq!(
LineFeedHandling::Native.apply("Because of the obvious threat to untold numbers of citizens due to the crisis that is even now developing, this radio station will remain on the air day and night.\n"),
file_content
);

Ok(())
}


#[tokio::test(flavor = "multi_thread")]
async fn test_xit_wo_buffer_w_path() -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?;
let mut app = helpers::AppBuilder::new()
.with_file(file.path(), None)
.build()?;

helpers::run_event_loop_until_idle(&mut app).await;

file.as_file_mut()
.write_all("extremely important content".as_bytes())?;
file.as_file_mut().flush()?;
file.as_file_mut().sync_all()?;

test_key_sequence(&mut app, Some(":x<ret>"), None, true).await?;

reload_file(&mut file).unwrap();
let mut file_content = String::new();
file.read_to_string(&mut file_content)?;
//check that nothing is written to file
assert_eq!("extremely important content", file_content);

Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn test_xit_wo_buffer_wo_path() -> anyhow::Result<()> {

test_key_sequence(
&mut AppBuilder::new().build()?,
Some(format!(":x<ret>").as_ref()),
None,
true,
)
.await?;

//helpers::assert_file_has_content(&mut file, &LineFeedHandling::Native.apply("hello"))?;

Ok(())
}

#[tokio::test(flavor = "multi_thread")]
async fn test_xit_w_buffer_wo_file() -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?;
test_key_sequence(//try to write without destination
&mut AppBuilder::new().build()?,
Some(format!("itest<esc>:x<ret>").as_ref()),
None,
false
)
.await?;
test_key_sequence(//try to write with path succeeds
&mut AppBuilder::new().build()?,
Some(format!("iMicCheck<esc>:x {}<ret>", file.path().to_string_lossy()).as_ref()),
Some(&|app| {
assert!(!app.editor.is_err());
}),
true,
)
.await?;

helpers::assert_file_has_content(&mut file, &LineFeedHandling::Native.apply("MicCheck"))?;

Ok(())
}


#[tokio::test(flavor = "multi_thread")]
async fn test_write_quit_fail() -> anyhow::Result<()> {
let file = helpers::new_readonly_tempfile()?;
Expand Down Expand Up @@ -144,7 +241,7 @@ async fn test_overwrite_protection() -> anyhow::Result<()> {
file.as_file_mut().flush()?;
file.as_file_mut().sync_all()?;

test_key_sequence(&mut app, Some(":x<ret>"), None, false).await?;
test_key_sequence(&mut app, Some("iOverwriteData<esc>:x<ret>"), None, false).await?;

reload_file(&mut file).unwrap();
let mut file_content = String::new();
Expand Down
Loading