Simplify LSP formatting, feature gate lsp in helix-view

gui
Blaž Hrastnik 2 years ago
parent dcd1e9eaa3
commit 8694d60ab3
No known key found for this signature in database
GPG Key ID: 1238B9C4AD889640

@ -199,22 +199,6 @@ pub mod util {
}), }),
) )
} }
/// The result of asking the language server to format the document. This can be turned into a
/// `Transaction`, but the advantage of not doing that straight away is that this one is
/// `Send` and `Sync`.
#[derive(Clone, Debug)]
pub struct LspFormatting {
pub doc: Rope,
pub edits: Vec<lsp::TextEdit>,
pub offset_encoding: OffsetEncoding,
}
impl From<LspFormatting> for Transaction {
fn from(fmt: LspFormatting) -> Transaction {
generate_transaction_from_edits(&fmt.doc, fmt.edits, fmt.offset_encoding)
}
}
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]

@ -2289,7 +2289,7 @@ async fn make_format_callback(
doc_id: DocumentId, doc_id: DocumentId,
doc_version: i32, doc_version: i32,
modified: Modified, modified: Modified,
format: impl Future<Output = helix_lsp::util::LspFormatting> + Send + 'static, format: impl Future<Output = Transaction> + Send + 'static,
) -> anyhow::Result<job::Callback> { ) -> anyhow::Result<job::Callback> {
let format = format.await; let format = format.await;
let call: job::Callback = Box::new(move |editor, _compositor| { let call: job::Callback = Box::new(move |editor, _compositor| {

@ -10,7 +10,8 @@ repository = "https://github.com/helix-editor/helix"
homepage = "https://helix-editor.com" homepage = "https://helix-editor.com"
[features] [features]
# default = ["dap"] default = ["dap", "lsp"]
lsp = ["helix-lsp"]
dap = ["helix-dap", "tokio-stream"] dap = ["helix-dap", "tokio-stream"]
term = ["crossterm"] term = ["crossterm"]
@ -18,7 +19,7 @@ term = ["crossterm"]
bitflags = "1.3" bitflags = "1.3"
anyhow = "1" anyhow = "1"
helix-core = { version = "0.6", path = "../helix-core" } helix-core = { version = "0.6", path = "../helix-core" }
helix-lsp = { version = "0.6", path = "../helix-lsp" } helix-lsp = { version = "0.6", path = "../helix-lsp", optional = true }
helix-dap = { version = "0.6", path = "../helix-dap", optional = true } helix-dap = { version = "0.6", path = "../helix-dap", optional = true }
tokio-stream = { version = "0.1", optional = true } tokio-stream = { version = "0.1", optional = true }
@ -30,7 +31,7 @@ url = "2"
arc-swap = { version = "1.5.0" } arc-swap = { version = "1.5.0" }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
slotmap = "1" slotmap = "1"

@ -19,7 +19,9 @@ use helix_core::{
ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction, ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, State, Syntax, Transaction,
DEFAULT_LINE_ENDING, DEFAULT_LINE_ENDING,
}; };
use helix_lsp::util::LspFormatting;
#[cfg(feature = "lsp")]
use helix_lsp::lsp;
use crate::{DocumentId, Editor, ViewId}; use crate::{DocumentId, Editor, ViewId};
@ -119,6 +121,7 @@ pub struct Document {
pub(crate) modified_since_accessed: bool, pub(crate) modified_since_accessed: bool,
diagnostics: Vec<Diagnostic>, diagnostics: Vec<Diagnostic>,
#[cfg(feature = "lsp")]
language_server: Option<Arc<helix_lsp::Client>>, language_server: Option<Arc<helix_lsp::Client>>,
} }
@ -142,7 +145,6 @@ impl fmt::Debug for Document {
.field("version", &self.version) .field("version", &self.version)
.field("modified_since_accessed", &self.modified_since_accessed) .field("modified_since_accessed", &self.modified_since_accessed)
.field("diagnostics", &self.diagnostics) .field("diagnostics", &self.diagnostics)
// .field("language_server", &self.language_server)
.finish() .finish()
} }
} }
@ -330,7 +332,6 @@ where
*mut_ref = f(mem::take(mut_ref)); *mut_ref = f(mem::take(mut_ref));
} }
use helix_lsp::lsp;
use url::Url; use url::Url;
impl Document { impl Document {
@ -359,6 +360,7 @@ impl Document {
savepoint: None, savepoint: None,
last_saved_revision: 0, last_saved_revision: 0,
modified_since_accessed: false, modified_since_accessed: false,
#[cfg(feature = "lsp")]
language_server: None, language_server: None,
} }
} }
@ -394,9 +396,10 @@ impl Document {
Ok(doc) Ok(doc)
} }
#[cfg(feature = "lsp")]
/// The same as [`format`], but only returns formatting changes if auto-formatting /// The same as [`format`], but only returns formatting changes if auto-formatting
/// is configured. /// is configured.
pub fn auto_format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> { pub fn auto_format(&self) -> Option<impl Future<Output = Transaction> + 'static> {
if self.language_config()?.auto_format { if self.language_config()?.auto_format {
self.format() self.format()
} else { } else {
@ -404,9 +407,12 @@ impl Document {
} }
} }
#[cfg(feature = "lsp")]
/// If supported, returns the changes that should be applied to this document in order /// If supported, returns the changes that should be applied to this document in order
/// to format it nicely. /// to format it nicely.
pub fn format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> { pub fn format(&self) -> Option<impl Future<Output = Transaction> + 'static> {
use helix_lsp::util::generate_transaction_from_edits;
let language_server = self.language_server()?; let language_server = self.language_server()?;
let text = self.text.clone(); let text = self.text.clone();
let offset_encoding = language_server.offset_encoding(); let offset_encoding = language_server.offset_encoding();
@ -425,11 +431,7 @@ impl Document {
log::warn!("LSP formatting failed: {}", e); log::warn!("LSP formatting failed: {}", e);
Default::default() Default::default()
}); });
LspFormatting { generate_transaction_from_edits(&text, edits, offset_encoding)
doc: text,
edits,
offset_encoding,
}
}; };
Some(fut) Some(fut)
} }
@ -438,9 +440,10 @@ impl Document {
self.save_impl::<futures_util::future::Ready<_>>(None, force) self.save_impl::<futures_util::future::Ready<_>>(None, force)
} }
#[cfg(feature = "lsp")]
pub fn format_and_save( pub fn format_and_save(
&mut self, &mut self,
formatting: Option<impl Future<Output = LspFormatting>>, formatting: Option<impl Future<Output = Transaction>>,
force: bool, force: bool,
) -> impl Future<Output = anyhow::Result<()>> { ) -> impl Future<Output = anyhow::Result<()>> {
self.save_impl(formatting, force) self.save_impl(formatting, force)
@ -452,7 +455,7 @@ impl Document {
/// at its `path()`. /// at its `path()`.
/// ///
/// If `formatting` is present, it supplies some changes that we apply to the text before saving. /// If `formatting` is present, it supplies some changes that we apply to the text before saving.
fn save_impl<F: Future<Output = LspFormatting>>( fn save_impl<F: Future<Output = Transaction>>(
&mut self, &mut self,
formatting: Option<F>, formatting: Option<F>,
force: bool, force: bool,
@ -462,8 +465,10 @@ impl Document {
let mut text = self.text().clone(); let mut text = self.text().clone();
let path = self.path.clone().expect("Can't save with no path set!"); let path = self.path.clone().expect("Can't save with no path set!");
let identifier = self.identifier();
#[cfg(feature = "lsp")]
let identifier = self.identifier();
#[cfg(feature = "lsp")]
let language_server = self.language_server.clone(); let language_server = self.language_server.clone();
// mark changes up to now as saved // mark changes up to now as saved
@ -486,7 +491,8 @@ impl Document {
} }
if let Some(fmt) = formatting { if let Some(fmt) = formatting {
let success = Transaction::from(fmt.await).changes().apply(&mut text); let transaction = fmt.await;
let success = transaction.changes().apply(&mut text);
if !success { if !success {
// This shouldn't happen, because the transaction changes were generated // This shouldn't happen, because the transaction changes were generated
// from the same text we're saving. // from the same text we're saving.
@ -497,6 +503,7 @@ impl Document {
let mut file = File::create(path).await?; let mut file = File::create(path).await?;
to_writer(&mut file, encoding, &text).await?; to_writer(&mut file, encoding, &text).await?;
#[cfg(feature = "lsp")]
if let Some(language_server) = language_server { if let Some(language_server) = language_server {
if !language_server.is_initialized() { if !language_server.is_initialized() {
return Ok(()); return Ok(());
@ -613,6 +620,7 @@ impl Document {
self.set_language(language_config, Some(config_loader)); self.set_language(language_config, Some(config_loader));
} }
#[cfg(feature = "lsp")]
/// Set the programming language for the file if you know the language but don't have the /// Set the programming language for the file if you know the language but don't have the
/// [`syntax::LanguageConfiguration`] for it. /// [`syntax::LanguageConfiguration`] for it.
pub fn set_language_by_language_id( pub fn set_language_by_language_id(
@ -624,6 +632,7 @@ impl Document {
self.set_language(language_config, Some(config_loader)); self.set_language(language_config, Some(config_loader));
} }
#[cfg(feature = "lsp")]
/// Set the LSP. /// Set the LSP.
pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) { pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {
self.language_server = language_server; self.language_server = language_server;
@ -692,6 +701,7 @@ impl Document {
} }
// emit lsp notification // emit lsp notification
#[cfg(feature = "lsp")]
if let Some(language_server) = self.language_server() { if let Some(language_server) = self.language_server() {
let notify = language_server.text_document_did_change( let notify = language_server.text_document_did_change(
self.versioned_identifier(), self.versioned_identifier(),
@ -869,6 +879,7 @@ impl Document {
self.version self.version
} }
#[cfg(feature = "lsp")]
/// Language server if it has been initialized. /// Language server if it has been initialized.
pub fn language_server(&self) -> Option<&helix_lsp::Client> { pub fn language_server(&self) -> Option<&helix_lsp::Client> {
let server = self.language_server.as_deref()?; let server = self.language_server.as_deref()?;
@ -935,15 +946,18 @@ impl Document {
// -- LSP methods // -- LSP methods
#[cfg(feature = "lsp")]
#[inline] #[inline]
pub fn identifier(&self) -> lsp::TextDocumentIdentifier { pub fn identifier(&self) -> lsp::TextDocumentIdentifier {
lsp::TextDocumentIdentifier::new(self.url().unwrap()) lsp::TextDocumentIdentifier::new(self.url().unwrap())
} }
#[cfg(feature = "lsp")]
pub fn versioned_identifier(&self) -> lsp::VersionedTextDocumentIdentifier { pub fn versioned_identifier(&self) -> lsp::VersionedTextDocumentIdentifier {
lsp::VersionedTextDocumentIdentifier::new(self.url().unwrap(), self.version) lsp::VersionedTextDocumentIdentifier::new(self.url().unwrap(), self.version)
} }
#[cfg(feature = "lsp")]
pub fn position( pub fn position(
&self, &self,
view_id: ViewId, view_id: ViewId,
@ -1003,6 +1017,7 @@ impl Default for Document {
mod test { mod test {
use super::*; use super::*;
#[cfg(feature = "lsp")]
#[test] #[test]
fn changeset_to_changes_ignore_line_endings() { fn changeset_to_changes_ignore_line_endings() {
use helix_lsp::{lsp, Client, OffsetEncoding}; use helix_lsp::{lsp, Client, OffsetEncoding};
@ -1037,6 +1052,7 @@ mod test {
); );
} }
#[cfg(feature = "lsp")]
#[test] #[test]
fn changeset_to_changes() { fn changeset_to_changes() {
use helix_lsp::{lsp, Client, OffsetEncoding}; use helix_lsp::{lsp, Client, OffsetEncoding};

@ -9,8 +9,6 @@ use crate::{
Document, DocumentId, View, ViewId, Document, DocumentId, View, ViewId,
}; };
use futures_util::future;
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
@ -37,6 +35,9 @@ use helix_core::{
}; };
use helix_core::{Position, Selection}; use helix_core::{Position, Selection};
#[cfg(feature = "lsp")]
use futures_util::future;
#[cfg(feature = "dap")] #[cfg(feature = "dap")]
use futures_util::stream::select_all::SelectAll; use futures_util::stream::select_all::SelectAll;
#[cfg(feature = "dap")] #[cfg(feature = "dap")]
@ -436,6 +437,7 @@ pub struct Editor {
pub registers: Registers, pub registers: Registers,
pub macro_recording: Option<(char, Vec<KeyEvent>)>, pub macro_recording: Option<(char, Vec<KeyEvent>)>,
pub theme: Theme, pub theme: Theme,
#[cfg(feature = "lsp")]
pub language_servers: helix_lsp::Registry, pub language_servers: helix_lsp::Registry,
#[cfg(feature = "dap")] #[cfg(feature = "dap")]
@ -495,7 +497,6 @@ impl Editor {
syn_loader: Arc<syntax::Loader>, syn_loader: Arc<syntax::Loader>,
config: Box<dyn DynAccess<Config>>, config: Box<dyn DynAccess<Config>>,
) -> Self { ) -> Self {
let language_servers = helix_lsp::Registry::new();
let conf = config.load(); let conf = config.load();
let auto_pairs = (&conf.auto_pairs).into(); let auto_pairs = (&conf.auto_pairs).into();
@ -510,7 +511,8 @@ impl Editor {
selected_register: None, selected_register: None,
macro_recording: None, macro_recording: None,
theme: theme_loader.default(), theme: theme_loader.default(),
language_servers, #[cfg(feature = "lsp")]
language_servers: helix_lsp::Registry::new(),
#[cfg(feature = "dap")] #[cfg(feature = "dap")]
debugger: None, debugger: None,
#[cfg(feature = "dap")] #[cfg(feature = "dap")]
@ -581,12 +583,14 @@ impl Editor {
self._refresh(); self._refresh();
} }
#[cfg(feature = "lsp")]
/// Refreshes the language server for a given document /// Refreshes the language server for a given document
pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> { pub fn refresh_language_server(&mut self, doc_id: DocumentId) -> Option<()> {
let doc = self.documents.get_mut(&doc_id)?; let doc = self.documents.get_mut(&doc_id)?;
Self::launch_language_server(&mut self.language_servers, doc) Self::launch_language_server(&mut self.language_servers, doc)
} }
#[cfg(feature = "lsp")]
/// Launch a language server for a given document /// Launch a language server for a given document
fn launch_language_server(ls: &mut helix_lsp::Registry, doc: &mut Document) -> Option<()> { fn launch_language_server(ls: &mut helix_lsp::Registry, doc: &mut Document) -> Option<()> {
// if doc doesn't have a URL it's a scratch buffer, ignore it // if doc doesn't have a URL it's a scratch buffer, ignore it
@ -624,7 +628,7 @@ impl Editor {
doc.set_language_server(Some(language_server)); doc.set_language_server(Some(language_server));
} }
} }
Some(()) Some(()) // TODO: what's the deal with the return type
} }
fn _refresh(&mut self) { fn _refresh(&mut self) {
@ -768,6 +772,7 @@ impl Editor {
} else { } else {
let mut doc = Document::open(&path, None, Some(self.syn_loader.clone()))?; let mut doc = Document::open(&path, None, Some(self.syn_loader.clone()))?;
#[cfg(feature = "lsp")]
let _ = Self::launch_language_server(&mut self.language_servers, &mut doc); let _ = Self::launch_language_server(&mut self.language_servers, &mut doc);
self.new_document(doc) self.new_document(doc)
@ -805,6 +810,7 @@ impl Editor {
); );
} }
#[cfg(feature = "lsp")]
if let Some(language_server) = doc.language_server() { if let Some(language_server) = doc.language_server() {
tokio::spawn(language_server.text_document_did_close(doc.identifier())); tokio::spawn(language_server.text_document_did_close(doc.identifier()));
} }
@ -954,6 +960,7 @@ impl Editor {
} }
} }
#[cfg(feature = "lsp")]
/// Closes language servers with timeout. The default timeout is 500 ms, use /// Closes language servers with timeout. The default timeout is 500 ms, use
/// `timeout` parameter to override this. /// `timeout` parameter to override this.
pub async fn close_language_servers( pub async fn close_language_servers(

Loading…
Cancel
Save