aboutsummaryrefslogtreecommitdiff
path: root/helix-view/src
diff options
context:
space:
mode:
authorDmitry Sharshakov2021-09-25 20:14:59 +0000
committerDmitry Sharshakov2021-09-25 20:14:59 +0000
commitbf53aff27d2d90b41bab01f4d628f0bd9fbcd589 (patch)
tree568d745540acd05ae7526e8a3eed7ee8e31e3cea /helix-view/src
parent413e477dc2d4792596f99979140d2879ec3d4f4f (diff)
parentdf55eaae69d0388de26448e82f9ded483fca2f44 (diff)
Merge branch 'master' into debug
Diffstat (limited to 'helix-view/src')
-rw-r--r--helix-view/src/document.rs83
-rw-r--r--helix-view/src/editor.rs52
-rw-r--r--helix-view/src/graphics.rs6
-rw-r--r--helix-view/src/lib.rs2
-rw-r--r--helix-view/src/macros.rs17
-rw-r--r--helix-view/src/register_selection.rs48
-rw-r--r--helix-view/src/theme.rs29
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> {