mirror of
https://github.com/helix-editor/helix.git
synced 2025-04-04 19:37:54 +03:00
Merge b9e8a29acf
into 7ebf650029
This commit is contained in:
commit
b68ed672e3
3 changed files with 234 additions and 10 deletions
|
@ -48,8 +48,10 @@
|
|||
| `:show-directory`, `:pwd` | Show the current working directory. |
|
||||
| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. |
|
||||
| `:character-info`, `:char` | Get info about the character under the primary cursor. |
|
||||
| `:reload`, `:rl` | Discard changes and reload from the source file. |
|
||||
| `:reload-all`, `:rla` | Discard changes and reload all documents from the source files. |
|
||||
| `:reload!`, `:rl!` | Discard changes and reload from the source file |
|
||||
| `:reload`, `:rl` | Reload from the source file, if no changes were made. |
|
||||
| `:reload-all!`, `:rla!` | Discard changes and reload all documents from the source files. |
|
||||
| `:reload-all`, `:rla` | Reload all documents from the source files, if no changes were made. |
|
||||
| `:update`, `:u` | Write changes only if the file has been modified. |
|
||||
| `:lsp-workspace-command` | Open workspace command picker |
|
||||
| `:lsp-restart` | Restarts the given language servers, or all language servers that are used by the current file if no arguments are supplied |
|
||||
|
|
|
@ -1320,13 +1320,22 @@ fn get_character_info(
|
|||
}
|
||||
|
||||
/// Reload the [`Document`] from its source file.
|
||||
fn reload(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
fn reload_impl(
|
||||
cx: &mut compositor::Context,
|
||||
event: PromptEvent,
|
||||
force: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let scrolloff = cx.editor.config().scrolloff;
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
if !force && doc.is_modified() {
|
||||
bail!("Cannot reload unsaved buffer");
|
||||
}
|
||||
|
||||
doc.reload(view, &cx.editor.diff_providers).map(|_| {
|
||||
view.ensure_cursor_in_view(doc, scrolloff);
|
||||
})?;
|
||||
|
@ -1339,11 +1348,29 @@ fn reload(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyh
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn reload_all(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
fn force_reload(
|
||||
cx: &mut compositor::Context,
|
||||
_args: Args,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
reload_impl(cx, event, true)
|
||||
}
|
||||
|
||||
fn reload(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
reload_impl(cx, event, false)
|
||||
}
|
||||
|
||||
fn reload_all_impl(
|
||||
cx: &mut compositor::Context,
|
||||
event: PromptEvent,
|
||||
force: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut unsaved_buffer_count = 0;
|
||||
|
||||
let scrolloff = cx.editor.config().scrolloff;
|
||||
let view_id = view!(cx.editor).id;
|
||||
|
||||
|
@ -1365,6 +1392,13 @@ fn reload_all(cx: &mut compositor::Context, _args: Args, event: PromptEvent) ->
|
|||
for (doc_id, view_ids) in docs_view_ids {
|
||||
let doc = doc_mut!(cx.editor, &doc_id);
|
||||
|
||||
if doc.is_modified() {
|
||||
unsaved_buffer_count += 1;
|
||||
if !force {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Every doc is guaranteed to have at least 1 view at this point.
|
||||
let view = view_mut!(cx.editor, view_ids[0]);
|
||||
|
||||
|
@ -1391,9 +1425,27 @@ fn reload_all(cx: &mut compositor::Context, _args: Args, event: PromptEvent) ->
|
|||
}
|
||||
}
|
||||
|
||||
if !force && unsaved_buffer_count > 0 {
|
||||
bail!(
|
||||
"{}, unsaved buffer(s) remaining, all saved buffers reloaded",
|
||||
unsaved_buffer_count
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn force_reload_all(
|
||||
cx: &mut compositor::Context,
|
||||
_args: Args,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
reload_all_impl(cx, event, true)
|
||||
}
|
||||
|
||||
fn reload_all(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
reload_all_impl(cx, event, false)
|
||||
}
|
||||
/// Update the [`Document`] if it has been modified.
|
||||
fn update(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
|
@ -3104,10 +3156,21 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand{
|
||||
name: "reload!",
|
||||
aliases: &["rl!"],
|
||||
doc: "Discard changes and reload from the source file",
|
||||
fun: force_reload,
|
||||
completer: CommandCompleter::none(),
|
||||
signature: Signature {
|
||||
positionals: (0, Some(0)),
|
||||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "reload",
|
||||
aliases: &["rl"],
|
||||
doc: "Discard changes and reload from the source file.",
|
||||
doc: "Reload from the source file, if no changes were made.",
|
||||
fun: reload,
|
||||
completer: CommandCompleter::none(),
|
||||
signature: Signature {
|
||||
|
@ -3115,10 +3178,21 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "reload-all!",
|
||||
aliases: &["rla!"],
|
||||
doc: "Discard changes and reload all documents from the source files.",
|
||||
fun: force_reload_all,
|
||||
completer: CommandCompleter::none(),
|
||||
signature: Signature {
|
||||
positionals: (0, Some(0)),
|
||||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "reload-all",
|
||||
aliases: &["rla"],
|
||||
doc: "Discard changes and reload all documents from the source files.",
|
||||
doc: "Reload all documents from the source files, if no changes were made.",
|
||||
fun: reload_all,
|
||||
completer: CommandCompleter::none(),
|
||||
signature: Signature {
|
||||
|
|
|
@ -743,6 +743,154 @@ async fn test_hardlink_write() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_reload_no_force() -> anyhow::Result<()> {
|
||||
let mut file = tempfile::NamedTempFile::new()?;
|
||||
let mut app = helpers::AppBuilder::new()
|
||||
.with_file(file.path(), None)
|
||||
.with_input_text("hello#[ |]#")
|
||||
.build()?;
|
||||
|
||||
test_key_sequences(
|
||||
&mut app,
|
||||
vec![
|
||||
(Some("athere<esc>"), None),
|
||||
(
|
||||
Some(":reload<ret>"),
|
||||
Some(&|app| {
|
||||
assert!(app.editor.is_err());
|
||||
|
||||
let doc = app.editor.documents().next().unwrap();
|
||||
assert!(doc.is_modified());
|
||||
assert_eq!(doc.text(), &LineFeedHandling::Native.apply("hello there"));
|
||||
}),
|
||||
),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
helpers::assert_file_has_content(&mut file, "")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_reload_force() -> anyhow::Result<()> {
|
||||
let mut file = tempfile::NamedTempFile::new()?;
|
||||
let mut app = helpers::AppBuilder::new()
|
||||
.with_file(file.path(), None)
|
||||
.with_input_text("hello#[ |]#")
|
||||
.build()?;
|
||||
|
||||
file.as_file_mut().write_all(b"goodbye!")?;
|
||||
|
||||
test_key_sequences(
|
||||
&mut app,
|
||||
vec![
|
||||
(Some("athere<esc>"), None),
|
||||
(
|
||||
Some(":reload!<ret>"),
|
||||
Some(&|app| {
|
||||
assert!(!app.editor.is_err());
|
||||
|
||||
let doc = app.editor.documents().next().unwrap();
|
||||
assert!(!doc.is_modified());
|
||||
assert_eq!(doc.text(), "goodbye!");
|
||||
}),
|
||||
),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
helpers::assert_file_has_content(&mut file, "goodbye!")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_reload_all_no_force() -> anyhow::Result<()> {
|
||||
let file1 = tempfile::NamedTempFile::new()?;
|
||||
let mut file2 = tempfile::NamedTempFile::new()?;
|
||||
let mut app = helpers::AppBuilder::new()
|
||||
.with_file(file1.path(), None)
|
||||
.with_file(file2.path(), None)
|
||||
.with_input_text("#[c|]#hange1")
|
||||
.build()?;
|
||||
|
||||
file2.as_file_mut().write_all(b"change2")?;
|
||||
|
||||
test_key_sequence(
|
||||
&mut app,
|
||||
Some(":reload-all<ret>"),
|
||||
Some(&|app| {
|
||||
assert!(app.editor.is_err());
|
||||
|
||||
let (mut doc1_visited, mut doc2_visited) = (false, false);
|
||||
for doc in app.editor.documents() {
|
||||
if doc.path().unwrap() == file1.path() {
|
||||
assert!(doc.is_modified());
|
||||
assert_eq!(doc.text(), "change1");
|
||||
doc1_visited = true;
|
||||
} else if doc.path().unwrap() == file2.path() {
|
||||
assert!(!doc.is_modified());
|
||||
assert_eq!(doc.text(), "change2");
|
||||
doc2_visited = true;
|
||||
}
|
||||
}
|
||||
assert!(doc1_visited);
|
||||
assert!(doc2_visited);
|
||||
assert_eq!(app.editor.documents().count(), 2);
|
||||
}),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_reload_all_force() -> anyhow::Result<()> {
|
||||
let file1 = tempfile::NamedTempFile::new()?;
|
||||
let mut file2 = tempfile::NamedTempFile::new()?;
|
||||
let mut app = helpers::AppBuilder::new()
|
||||
.with_file(file1.path(), None)
|
||||
.with_file(file2.path(), None)
|
||||
.with_input_text("#[c|]#hange1")
|
||||
.build()?;
|
||||
|
||||
file2.as_file_mut().write_all(b"change2")?;
|
||||
|
||||
test_key_sequence(
|
||||
&mut app,
|
||||
Some(":reload-all!<ret>"),
|
||||
Some(&|app| {
|
||||
assert!(!app.editor.is_err());
|
||||
|
||||
let (mut doc1_visited, mut doc2_visited) = (false, false);
|
||||
for doc in app.editor.documents() {
|
||||
if doc.path().unwrap() == file1.path() {
|
||||
assert!(!doc.is_modified());
|
||||
assert_eq!(doc.text(), "");
|
||||
doc1_visited = true;
|
||||
} else if doc.path().unwrap() == file2.path() {
|
||||
assert!(!doc.is_modified());
|
||||
assert_eq!(doc.text(), "change2");
|
||||
doc2_visited = true;
|
||||
}
|
||||
}
|
||||
assert!(doc1_visited);
|
||||
assert!(doc2_visited);
|
||||
assert_eq!(app.editor.documents().count(), 2);
|
||||
}),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn edit_file_with_content(file_content: &[u8]) -> anyhow::Result<()> {
|
||||
let mut file = tempfile::NamedTempFile::new()?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue