diff options
Diffstat (limited to 'helix-term/src/commands')
-rw-r--r-- | helix-term/src/commands/lsp.rs | 8 | ||||
-rw-r--r-- | helix-term/src/commands/typed.rs | 130 |
2 files changed, 100 insertions, 38 deletions
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 1113b44e..3fa5c96f 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -9,7 +9,7 @@ use tui::text::{Span, Spans}; use super::{align_view, push_jump, Align, Context, Editor, Open}; use helix_core::{path, Selection}; -use helix_view::{editor::Action, theme::Style}; +use helix_view::{apply_transaction, editor::Action, theme::Style}; use crate::{ compositor::{self, Compositor}, @@ -596,9 +596,7 @@ pub fn apply_workspace_edit( } }; - let doc = editor - .document_mut(doc_id) - .expect("Document for document_changes not found"); + let doc = doc_mut!(editor, &doc_id); // Need to determine a view for apply/append_changes_to_history let selections = doc.selections(); @@ -619,7 +617,7 @@ pub fn apply_workspace_edit( text_edits, offset_encoding, ); - doc.apply(&transaction, view_id); + apply_transaction(&transaction, doc, view_mut!(editor, view_id)); doc.append_changes_to_history(view_id); }; diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 6d0ced65..1bfc8153 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2,7 +2,10 @@ use std::ops::Deref; use super::*; -use helix_view::editor::{Action, ConfigEvent}; +use helix_view::{ + apply_transaction, + editor::{Action, CloseError, ConfigEvent}, +}; use ui::completers::{self, Completer}; #[derive(Clone)] @@ -71,8 +74,29 @@ fn buffer_close_by_ids_impl( doc_ids: &[DocumentId], force: bool, ) -> anyhow::Result<()> { - for &doc_id in doc_ids { - editor.close_document(doc_id, force)?; + let (modified_ids, modified_names): (Vec<_>, Vec<_>) = doc_ids + .iter() + .filter_map(|&doc_id| { + if let Err(CloseError::BufferModified(name)) = editor.close_document(doc_id, force) { + Some((doc_id, name)) + } else { + None + } + }) + .unzip(); + + if let Some(first) = modified_ids.first() { + let current = doc!(editor); + // If the current document is unmodified, and there are modified + // documents, switch focus to the first modified doc. + if !modified_ids.contains(¤t.id()) { + editor.switch(*first, Action::Replace); + } + bail!( + "{} unsaved buffer(s) remaining: {:?}", + modified_names.len(), + modified_names + ); } Ok(()) @@ -441,7 +465,7 @@ fn set_line_ending( } }), ); - doc.apply(&transaction, view.id); + apply_transaction(&transaction, doc, view); doc.append_changes_to_history(view.id); Ok(()) @@ -459,7 +483,7 @@ fn earlier( let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?; let (view, doc) = current!(cx.editor); - let success = doc.earlier(view.id, uk); + let success = doc.earlier(view, uk); if !success { cx.editor.set_status("Already at oldest change"); } @@ -478,7 +502,7 @@ fn later( let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?; let (view, doc) = current!(cx.editor); - let success = doc.later(view.id, uk); + let success = doc.later(view, uk); if !success { cx.editor.set_status("Already at newest change"); } @@ -513,23 +537,26 @@ fn force_write_quit( force_quit(cx, &[], event) } -/// Results an error if there are modified buffers remaining and sets editor error, -/// otherwise returns `Ok(())` +/// Results in an error if there are modified buffers remaining and sets editor +/// error, otherwise returns `Ok(())`. If the current document is unmodified, +/// and there are modified documents, switches focus to one of them. pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> { - let modified: Vec<_> = editor + let (modified_ids, modified_names): (Vec<_>, Vec<_>) = editor .documents() .filter(|doc| doc.is_modified()) - .map(|doc| { - doc.relative_path() - .map(|path| path.to_string_lossy().to_string()) - .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into()) - }) - .collect(); - if !modified.is_empty() { + .map(|doc| (doc.id(), doc.display_name())) + .unzip(); + if let Some(first) = modified_ids.first() { + let current = doc!(editor); + // If the current document is unmodified, and there are modified + // documents, switch focus to the first modified doc. + if !modified_ids.contains(¤t.id()) { + editor.switch(*first, Action::Replace); + } bail!( "{} unsaved buffer(s) remaining: {:?}", - modified.len(), - modified + modified_names.len(), + modified_names ); } Ok(()) @@ -859,7 +886,7 @@ fn replace_selections_with_clipboard_impl( (range.from(), range.to(), Some(contents.as_str().into())) }); - doc.apply(&transaction, view.id); + apply_transaction(&transaction, doc, view); doc.append_changes_to_history(view.id); Ok(()) } @@ -980,7 +1007,7 @@ fn reload( let scrolloff = cx.editor.config().scrolloff; let (view, doc) = current!(cx.editor); - doc.reload(view.id).map(|_| { + doc.reload(view).map(|_| { view.ensure_cursor_in_view(doc, scrolloff); }) } @@ -1000,7 +1027,7 @@ fn lsp_restart( .context("LSP not defined for the current document")?; let scope = config.scope.clone(); - cx.editor.language_servers.restart(config)?; + cx.editor.language_servers.restart(config, doc.path())?; // This collect is needed because refresh_language_server would need to re-borrow editor. let document_ids_to_refresh: Vec<DocumentId> = cx @@ -1033,7 +1060,21 @@ fn tree_sitter_scopes( let pos = doc.selection(view.id).primary().cursor(text); let scopes = indent::get_scopes(doc.syntax(), text, pos); - cx.editor.set_status(format!("scopes: {:?}", &scopes)); + + let contents = format!("```json\n{:?}\n````", scopes); + + let callback = async move { + let call: job::Callback = + Box::new(move |editor: &mut Editor, compositor: &mut Compositor| { + let contents = ui::Markdown::new(contents, editor.syn_loader.clone()); + let popup = Popup::new("hover", contents).auto_close(true); + compositor.replace_or_push("hover", popup); + }); + Ok(call) + }; + + cx.jobs.callback(callback); + Ok(()) } @@ -1196,18 +1237,41 @@ pub(super) fn goto_line_number( args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { - if event != PromptEvent::Validate { - return Ok(()); + match event { + PromptEvent::Abort => { + if let Some(line_number) = cx.editor.last_line_number { + goto_line_impl(cx.editor, NonZeroUsize::new(line_number)); + let (view, doc) = current!(cx.editor); + view.ensure_cursor_in_view(doc, line_number); + cx.editor.last_line_number = None; + } + return Ok(()); + } + PromptEvent::Validate => { + ensure!(!args.is_empty(), "Line number required"); + cx.editor.last_line_number = None; + } + PromptEvent::Update => { + if args.is_empty() { + if let Some(line_number) = cx.editor.last_line_number { + // When a user hits backspace and there are no numbers left, + // we can bring them back to their original line + goto_line_impl(cx.editor, NonZeroUsize::new(line_number)); + let (view, doc) = current!(cx.editor); + view.ensure_cursor_in_view(doc, line_number); + cx.editor.last_line_number = None; + } + return Ok(()); + } + let (view, doc) = current!(cx.editor); + let text = doc.text().slice(..); + let line = doc.selection(view.id).primary().cursor_line(text); + cx.editor.last_line_number.get_or_insert(line + 1); + } } - - ensure!(!args.is_empty(), "Line number required"); - let line = args[0].parse::<usize>()?; - goto_line_impl(cx.editor, NonZeroUsize::new(line)); - let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, line); Ok(()) } @@ -1351,7 +1415,7 @@ fn sort_impl( .map(|(s, fragment)| (s.from(), s.to(), Some(fragment))), ); - doc.apply(&transaction, view.id); + apply_transaction(&transaction, doc, view); doc.append_changes_to_history(view.id); Ok(()) @@ -1395,7 +1459,7 @@ fn reflow( (range.from(), range.to(), Some(reflowed_text)) }); - doc.apply(&transaction, view.id); + apply_transaction(&transaction, doc, view); doc.append_changes_to_history(view.id); view.ensure_cursor_in_view(doc, scrolloff); @@ -2021,7 +2085,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "insert-output", aliases: &[], - doc: "Run shell command, inserting output after each selection.", + doc: "Run shell command, inserting output before each selection.", fun: insert_output, completer: None, }, |