mirror of https://github.com/helix-editor/helix
Simplify LSP formatting, feature gate lsp in helix-view
parent
dcd1e9eaa3
commit
8694d60ab3
|
@ -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…
Reference in New Issue