diff options
Diffstat (limited to 'helix-view/src')
-rw-r--r-- | helix-view/src/document.rs | 83 | ||||
-rw-r--r-- | helix-view/src/editor.rs | 52 | ||||
-rw-r--r-- | helix-view/src/graphics.rs | 6 | ||||
-rw-r--r-- | helix-view/src/lib.rs | 2 | ||||
-rw-r--r-- | helix-view/src/macros.rs | 17 | ||||
-rw-r--r-- | helix-view/src/register_selection.rs | 48 | ||||
-rw-r--r-- | helix-view/src/theme.rs | 29 |
7 files changed, 145 insertions, 92 deletions
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index e890a336..1f1b1f5f 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -386,21 +386,24 @@ impl Document { /// If supported, returns the changes that should be applied to this document in order /// to format it nicely. pub fn format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> { - if let Some(language_server) = self.language_server.clone() { + if let Some(language_server) = self.language_server() { let text = self.text.clone(); - let id = self.identifier(); + let offset_encoding = language_server.offset_encoding(); + let request = language_server.text_document_formatting( + self.identifier(), + lsp::FormattingOptions::default(), + None, + )?; + let fut = async move { - let edits = language_server - .text_document_formatting(id, lsp::FormattingOptions::default(), None) - .await - .unwrap_or_else(|e| { - log::warn!("LSP formatting failed: {}", e); - Default::default() - }); + let edits = request.await.unwrap_or_else(|e| { + log::warn!("LSP formatting failed: {}", e); + Default::default() + }); LspFormatting { doc: text, edits, - offset_encoding: language_server.offset_encoding(), + offset_encoding, } }; Some(fut) @@ -469,9 +472,14 @@ impl Document { to_writer(&mut file, encoding, &text).await?; if let Some(language_server) = language_server { - language_server - .text_document_did_save(identifier, &text) - .await?; + if !language_server.is_initialized() { + return Ok(()); + } + if let Some(notification) = + language_server.text_document_did_save(identifier, &text) + { + notification.await?; + } } Ok(()) @@ -646,7 +654,7 @@ impl Document { // } // emit lsp notification - if let Some(language_server) = &self.language_server { + if let Some(language_server) = self.language_server() { let notify = language_server.text_document_did_change( self.versioned_identifier(), &old_doc, @@ -795,9 +803,18 @@ impl Document { self.version } - #[inline] pub fn language_server(&self) -> Option<&helix_lsp::Client> { - self.language_server.as_deref() + let server = self.language_server.as_deref(); + let initialized = server + .map(|server| server.is_initialized()) + .unwrap_or(false); + + // only resolve language_server if it's initialized + if initialized { + server + } else { + None + } } #[inline] @@ -892,6 +909,40 @@ mod test { use super::*; #[test] + fn changeset_to_changes_ignore_line_endings() { + use helix_lsp::{lsp, Client, OffsetEncoding}; + let text = Rope::from("hello\r\nworld"); + let mut doc = Document::from(text, None); + let view = ViewId::default(); + doc.set_selection(view, Selection::single(0, 0)); + + let transaction = + Transaction::change(doc.text(), vec![(5, 7, Some("\n".into()))].into_iter()); + let old_doc = doc.text().clone(); + doc.apply(&transaction, view); + let changes = Client::changeset_to_changes( + &old_doc, + doc.text(), + transaction.changes(), + OffsetEncoding::Utf8, + ); + + assert_eq!(doc.text(), "hello\nworld"); + + assert_eq!( + changes, + &[lsp::TextDocumentContentChangeEvent { + range: Some(lsp::Range::new( + lsp::Position::new(0, 5), + lsp::Position::new(1, 0) + )), + text: "\n".into(), + range_length: None, + }] + ); + } + + #[test] fn changeset_to_changes() { use helix_lsp::{lsp, Client, OffsetEncoding}; let text = Rope::from("hello"); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index adc40eb4..72140ea8 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -3,7 +3,7 @@ use crate::{ graphics::{CursorKind, Rect}, theme::{self, Theme}, tree::Tree, - Document, DocumentId, RegisterSelection, View, ViewId, + Document, DocumentId, View, ViewId, }; use futures_util::future; @@ -44,6 +44,10 @@ pub struct Config { pub line_number: LineNumber, /// Middle click paste support. Defaults to true pub middle_click_paste: bool, + /// Smart case: Case insensitive searching unless pattern contains upper case characters. Defaults to true. + pub smart_case: bool, + /// Automatic insertion of pairs to parentheses, brackets, etc. Defaults to true. + pub auto_pairs: bool, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] @@ -69,6 +73,8 @@ impl Default for Config { }, line_number: LineNumber::Absolute, middle_click_paste: true, + smart_case: true, + auto_pairs: true, } } } @@ -78,7 +84,7 @@ pub struct Editor { pub tree: Tree, pub documents: SlotMap<DocumentId, Document>, pub count: Option<std::num::NonZeroUsize>, - pub selected_register: RegisterSelection, + pub selected_register: Option<char>, pub registers: Registers, pub theme: Theme, pub language_servers: helix_lsp::Registry, @@ -125,7 +131,7 @@ impl Editor { tree: Tree::new(area), documents: SlotMap::with_key(), count: None, - selected_register: RegisterSelection::default(), + selected_register: None, theme: themes.default(), language_servers, debugger: None, @@ -270,26 +276,31 @@ impl Editor { let mut doc = Document::open(&path, None, Some(&self.theme), Some(&self.syn_loader))?; // try to find a language server based on the language name - let language_server = doc - .language - .as_ref() - .and_then(|language| self.language_servers.get(language).ok()); + let language_server = doc.language.as_ref().and_then(|language| { + self.language_servers + .get(language) + .map_err(|e| { + log::error!("Failed to get LSP, {}, for `{}`", e, language.scope()) + }) + .ok() + }); if let Some(language_server) = language_server { - doc.set_language_server(Some(language_server.clone())); - let language_id = doc .language() .and_then(|s| s.split('.').last()) // source.rust .map(ToOwned::to_owned) .unwrap_or_default(); + // TODO: this now races with on_init code if the init happens too quickly tokio::spawn(language_server.text_document_did_open( doc.url().unwrap(), doc.version(), doc.text(), language_id, )); + + doc.set_language_server(Some(language_server)); } let id = self.documents.insert(doc); @@ -308,14 +319,9 @@ impl Editor { if close_buffer { // get around borrowck issues - let language_servers = &mut self.language_servers; let doc = &self.documents[view.doc]; - let language_server = doc - .language - .as_ref() - .and_then(|language| language_servers.get(language).ok()); - if let Some(language_server) = language_server { + if let Some(language_server) = doc.language_server() { tokio::spawn(language_server.text_document_did_close(doc.identifier())); } self.documents.remove(view.doc); @@ -345,20 +351,24 @@ impl Editor { view.ensure_cursor_in_view(doc, self.config.scrolloff) } + #[inline] pub fn document(&self, id: DocumentId) -> Option<&Document> { self.documents.get(id) } + #[inline] pub fn document_mut(&mut self, id: DocumentId) -> Option<&mut Document> { self.documents.get_mut(id) } + #[inline] pub fn documents(&self) -> impl Iterator<Item = &Document> { - self.documents.iter().map(|(_id, doc)| doc) + self.documents.values() } + #[inline] pub fn documents_mut(&mut self) -> impl Iterator<Item = &mut Document> { - self.documents.iter_mut().map(|(_id, doc)| doc) + self.documents.values_mut() } pub fn document_by_path<P: AsRef<Path>>(&self, path: P) -> Option<&Document> { @@ -366,10 +376,10 @@ impl Editor { .find(|doc| doc.path().map(|p| p == path.as_ref()).unwrap_or(false)) } - // pub fn current_document(&self) -> Document { - // let id = self.view().doc; - // let doc = &mut editor.documents[id]; - // } + pub fn document_by_path_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut Document> { + self.documents_mut() + .find(|doc| doc.path().map(|p| p == path.as_ref()).unwrap_or(false)) + } pub fn cursor(&self) -> (Option<Position>, CursorKind) { let view = view!(self); diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index 66013ee5..0bfca04a 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -224,13 +224,13 @@ pub enum Color { Magenta,
Cyan,
Gray,
- DarkGray,
LightRed,
LightGreen,
LightYellow,
LightBlue,
LightMagenta,
LightCyan,
+ LightGray,
White,
Rgb(u8, u8, u8),
Indexed(u8),
@@ -250,14 +250,14 @@ impl From<Color> for crossterm::style::Color { Color::Blue => CColor::DarkBlue,
Color::Magenta => CColor::DarkMagenta,
Color::Cyan => CColor::DarkCyan,
- Color::Gray => CColor::Grey,
- Color::DarkGray => CColor::DarkGrey,
+ Color::Gray => CColor::DarkGrey,
Color::LightRed => CColor::Red,
Color::LightGreen => CColor::Green,
Color::LightBlue => CColor::Blue,
Color::LightYellow => CColor::Yellow,
Color::LightMagenta => CColor::Magenta,
Color::LightCyan => CColor::Cyan,
+ Color::LightGray => CColor::Grey,
Color::White => CColor::White,
Color::Indexed(i) => CColor::AnsiValue(i),
Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 9bcc0b7d..c37474d6 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -8,7 +8,6 @@ pub mod graphics; pub mod info; pub mod input; pub mod keyboard; -pub mod register_selection; pub mod theme; pub mod tree; pub mod view; @@ -20,6 +19,5 @@ slotmap::new_key_type! { pub use document::Document; pub use editor::Editor; -pub use register_selection::RegisterSelection; pub use theme::Theme; pub use view::View; diff --git a/helix-view/src/macros.rs b/helix-view/src/macros.rs index a06d37e7..c9a04270 100644 --- a/helix-view/src/macros.rs +++ b/helix-view/src/macros.rs @@ -1,3 +1,14 @@ +//! These are macros to make getting very nested fields in the `Editor` struct easier +//! These are macros instead of functions because functions will have to take `&mut self` +//! However, rust doesn't know that you only want a partial borrow instead of borrowing the +//! entire struct which `&mut self` says. This makes it impossible to do other mutable +//! stuff to the struct because it is already borrowed. Because macros are expanded, +//! this circumvents the problem because it is just like indexing fields by hand and then +//! putting a `&mut` in front of it. This way rust can see that we are only borrowing a +//! part of the struct and not the entire thing. + +/// Get the current view and document mutably as a tuple. +/// Returns `(&mut View, &mut Document)` #[macro_export] macro_rules! current { ( $( $editor:ident ).+ ) => {{ @@ -7,6 +18,8 @@ macro_rules! current { }}; } +/// Get the current document mutably. +/// Returns `&mut Document` #[macro_export] macro_rules! doc_mut { ( $( $editor:ident ).+ ) => {{ @@ -14,6 +27,8 @@ macro_rules! doc_mut { }}; } +/// Get the current view mutably. +/// Returns `&mut View` #[macro_export] macro_rules! view_mut { ( $( $editor:ident ).+ ) => {{ @@ -21,6 +36,8 @@ macro_rules! view_mut { }}; } +/// Get the current view immutably +/// Returns `&View` #[macro_export] macro_rules! view { ( $( $editor:ident ).+ ) => {{ diff --git a/helix-view/src/register_selection.rs b/helix-view/src/register_selection.rs deleted file mode 100644 index a2b78f72..00000000 --- a/helix-view/src/register_selection.rs +++ /dev/null @@ -1,48 +0,0 @@ -/// Register selection and configuration -/// -/// This is a kind a of specialized `Option<char>` for register selection. -/// Point is to keep whether the register selection has been explicitely -/// set or not while being convenient by knowing the default register name. -#[derive(Debug)] -pub struct RegisterSelection { - selected: char, - default_name: char, -} - -impl RegisterSelection { - pub fn new(default_name: char) -> Self { - Self { - selected: default_name, - default_name, - } - } - - pub fn select(&mut self, name: char) { - self.selected = name; - } - - pub fn take(&mut self) -> Self { - Self { - selected: std::mem::replace(&mut self.selected, self.default_name), - default_name: self.default_name, - } - } - - pub fn is_default(&self) -> bool { - self.selected == self.default_name - } - - pub fn name(&self) -> char { - self.selected - } -} - -impl Default for RegisterSelection { - fn default() -> Self { - let default_name = '"'; - Self { - selected: default_name, - default_name, - } - } -} diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index 74b817d0..9c33685b 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -5,6 +5,7 @@ use std::{ }; use anyhow::Context; +use helix_core::hashmap; use log::warn; use once_cell::sync::Lazy; use serde::{Deserialize, Deserializer}; @@ -142,13 +143,37 @@ struct ThemePalette { impl Default for ThemePalette { fn default() -> Self { - Self::new(HashMap::new()) + Self { + palette: hashmap! { + "black".to_string() => Color::Black, + "red".to_string() => Color::Red, + "green".to_string() => Color::Green, + "yellow".to_string() => Color::Yellow, + "blue".to_string() => Color::Blue, + "magenta".to_string() => Color::Magenta, + "cyan".to_string() => Color::Cyan, + "gray".to_string() => Color::Gray, + "light-red".to_string() => Color::LightRed, + "light-green".to_string() => Color::LightGreen, + "light-yellow".to_string() => Color::LightYellow, + "light-blue".to_string() => Color::LightBlue, + "light-magenta".to_string() => Color::LightMagenta, + "light-cyan".to_string() => Color::LightCyan, + "light-gray".to_string() => Color::LightGray, + "white".to_string() => Color::White, + }, + } } } impl ThemePalette { pub fn new(palette: HashMap<String, Color>) -> Self { - Self { palette } + let ThemePalette { + palette: mut default, + } = ThemePalette::default(); + + default.extend(palette); + Self { palette: default } } pub fn hex_string_to_rgb(s: &str) -> Result<Color, String> { |