diff options
Diffstat (limited to 'helix-view/src/editor.rs')
-rw-r--r-- | helix-view/src/editor.rs | 124 |
1 files changed, 94 insertions, 30 deletions
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 60864e9e..591e0492 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2,7 +2,7 @@ use crate::{ clipboard::{get_clipboard_provider, ClipboardProvider}, graphics::{CursorKind, Rect}, theme::{self, Theme}, - tree::Tree, + tree::{self, Tree}, Document, DocumentId, View, ViewId, }; @@ -12,6 +12,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use std::{ collections::HashMap, + collections::BTreeMap, path::{Path, PathBuf}, pin::Pin, sync::Arc, @@ -19,8 +20,6 @@ use std::{ use tokio::time::{sleep, Duration, Instant, Sleep}; -use slotmap::SlotMap; - use anyhow::Error; pub use helix_core::diagnostic::Severity; @@ -63,6 +62,9 @@ pub struct Config { /// Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. Defaults to 400ms. #[serde(skip_serializing, deserialize_with = "deserialize_duration_millis")] pub idle_timeout: Duration, + pub completion_trigger_len: u8, + /// Whether to display infoboxes. Defaults to true. + pub auto_info: bool, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] @@ -92,14 +94,29 @@ impl Default for Config { auto_pairs: true, auto_completion: true, idle_timeout: Duration::from_millis(400), + completion_trigger_len: 2, + auto_info: true, } } } +pub struct Motion(pub Box<dyn Fn(&mut Editor)>); +impl Motion { + pub fn run(&self, e: &mut Editor) { + (self.0)(e) + } +} +impl std::fmt::Debug for Motion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("motion") + } +} + #[derive(Debug)] pub struct Editor { pub tree: Tree, - pub documents: SlotMap<DocumentId, Document>, + pub next_document_id: usize, + pub documents: BTreeMap<DocumentId, Document>, pub count: Option<std::num::NonZeroUsize>, pub selected_register: Option<char>, pub registers: Registers, @@ -124,6 +141,7 @@ pub struct Editor { pub config: Config, pub idle_timer: Pin<Box<Sleep>>, + pub last_motion: Option<Motion>, } #[derive(Debug, Copy, Clone)] @@ -148,7 +166,8 @@ impl Editor { Self { tree: Tree::new(area), - documents: SlotMap::with_key(), + next_document_id: 0, + documents: BTreeMap::new(), count: None, selected_register: None, theme: themes.default(), @@ -166,6 +185,7 @@ impl Editor { clipboard_provider: get_clipboard_provider(), status_msg: None, idle_timer: Box::pin(sleep(config.idle_timeout)), + last_motion: None, config, } } @@ -221,7 +241,7 @@ impl Editor { fn _refresh(&mut self) { for (view, _) in self.tree.views_mut() { - let doc = &self.documents[view.doc]; + let doc = &self.documents[&view.doc]; view.ensure_cursor_in_view(doc, self.config.scrolloff) } } @@ -230,22 +250,38 @@ impl Editor { use crate::tree::Layout; use helix_core::Selection; - if !self.documents.contains_key(id) { + if !self.documents.contains_key(&id) { log::error!("cannot switch to document that does not exist (anymore)"); return; } match action { Action::Replace => { - let view = view!(self); - let jump = ( - view.doc, - self.documents[view.doc].selection(view.id).clone(), - ); - + let (view, doc) = current_ref!(self); + // If the current view is an empty scratch buffer and is not displayed in any other views, delete it. + // Boolean value is determined before the call to `view_mut` because the operation requires a borrow + // of `self.tree`, which is mutably borrowed when `view_mut` is called. + let remove_empty_scratch = !doc.is_modified() + // If the buffer has no path and is not modified, it is an empty scratch buffer. + && doc.path().is_none() + // If the buffer we are changing to is not this buffer + && id != doc.id + // Ensure the buffer is not displayed in any other splits. + && !self + .tree + .traverse() + .any(|(_, v)| v.doc == doc.id && v.id != view.id); let view = view_mut!(self); - view.jumps.push(jump); - view.last_accessed_doc = Some(view.doc); + if remove_empty_scratch { + // Copy `doc.id` into a variable before calling `self.documents.remove`, which requires a mutable + // borrow, invalidating direct access to `doc.id`. + let id = doc.id; + self.documents.remove(&id); + } else { + let jump = (view.doc, doc.selection(view.id).clone()); + view.jumps.push(jump); + view.last_accessed_doc = Some(view.doc); + } view.doc = id; view.offset = Position::default(); @@ -272,14 +308,14 @@ impl Editor { let view = View::new(id); let view_id = self.tree.split(view, Layout::Horizontal); // initialize selection for view - let doc = &mut self.documents[id]; + let doc = self.documents.get_mut(&id).unwrap(); doc.selections.insert(view_id, Selection::point(0)); } Action::VerticalSplit => { let view = View::new(id); let view_id = self.tree.split(view, Layout::Vertical); // initialize selection for view - let doc = &mut self.documents[id]; + let doc = self.documents.get_mut(&id).unwrap(); doc.selections.insert(view_id, Selection::point(0)); } } @@ -288,9 +324,11 @@ impl Editor { } pub fn new_file(&mut self, action: Action) -> DocumentId { - let doc = Document::default(); - let id = self.documents.insert(doc); - self.documents[id].id = id; + let id = DocumentId(self.next_document_id); + self.next_document_id += 1; + let mut doc = Document::default(); + doc.id = id; + self.documents.insert(id, doc); self.switch(id, action); id } @@ -313,7 +351,11 @@ impl Editor { self.language_servers .get(language) .map_err(|e| { - log::error!("Failed to get LSP, {}, for `{}`", e, language.scope()) + log::error!( + "Failed to initialize the LSP for `{}` {{ {} }}", + language.scope(), + e + ) }) .ok() }); @@ -336,8 +378,10 @@ impl Editor { doc.set_language_server(Some(language_server)); } - let id = self.documents.insert(doc); - self.documents[id].id = id; + let id = DocumentId(self.next_document_id); + self.next_document_id += 1; + doc.id = id; + self.documents.insert(id, doc); id }; @@ -348,16 +392,20 @@ impl Editor { pub fn close(&mut self, id: ViewId, close_buffer: bool) { let view = self.tree.get(self.tree.focus); // remove selection - self.documents[view.doc].selections.remove(&id); + self.documents + .get_mut(&view.doc) + .unwrap() + .selections + .remove(&id); if close_buffer { // get around borrowck issues - let doc = &self.documents[view.doc]; + let doc = &self.documents[&view.doc]; if let Some(language_server) = doc.language_server() { tokio::spawn(language_server.text_document_did_close(doc.identifier())); } - self.documents.remove(view.doc); + self.documents.remove(&view.doc); } self.tree.remove(id); @@ -374,24 +422,40 @@ impl Editor { self.tree.focus_next(); } + pub fn focus_right(&mut self) { + self.tree.focus_direction(tree::Direction::Right); + } + + pub fn focus_left(&mut self) { + self.tree.focus_direction(tree::Direction::Left); + } + + pub fn focus_up(&mut self) { + self.tree.focus_direction(tree::Direction::Up); + } + + pub fn focus_down(&mut self) { + self.tree.focus_direction(tree::Direction::Down); + } + pub fn should_close(&self) -> bool { self.tree.is_empty() } pub fn ensure_cursor_in_view(&mut self, id: ViewId) { let view = self.tree.get_mut(id); - let doc = &self.documents[view.doc]; + let doc = &self.documents[&view.doc]; view.ensure_cursor_in_view(doc, self.config.scrolloff) } #[inline] pub fn document(&self, id: DocumentId) -> Option<&Document> { - self.documents.get(id) + self.documents.get(&id) } #[inline] pub fn document_mut(&mut self, id: DocumentId) -> Option<&mut Document> { - self.documents.get_mut(id) + self.documents.get_mut(&id) } #[inline] @@ -416,7 +480,7 @@ impl Editor { pub fn cursor(&self) -> (Option<Position>, CursorKind) { let view = view!(self); - let doc = &self.documents[view.doc]; + let doc = &self.documents[&view.doc]; let cursor = doc .selection(view.id) .primary() |