-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(languages): add willSave and willSaveWaitUntil LSP support #14407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
dba253f
000a4f6
819c02e
078485b
edc3e2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -994,7 +994,59 @@ impl Client { | |
}) | ||
} | ||
|
||
// will_save / will_save_wait_until | ||
pub fn text_document_will_save( | ||
&self, | ||
text_document: lsp::TextDocumentIdentifier, | ||
reason: lsp::TextDocumentSaveReason, | ||
) -> Option<()> { | ||
let capabilities = self.capabilities.get().unwrap(); | ||
|
||
match &capabilities.text_document_sync.as_ref()? { | ||
lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { | ||
will_save: enabled, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can match explicitly on |
||
.. | ||
}) => { | ||
if !*enabled.as_ref()? { | ||
return None; | ||
} | ||
} | ||
_ => return None, | ||
}; | ||
|
||
self.notify::<lsp::notification::WillSaveTextDocument>(lsp::WillSaveTextDocumentParams { | ||
text_document, | ||
reason, | ||
}); | ||
Some(()) | ||
} | ||
|
||
pub fn text_document_will_save_wait_until( | ||
&self, | ||
text_document: lsp::TextDocumentIdentifier, | ||
reason: lsp::TextDocumentSaveReason, | ||
) -> Option<impl Future<Output = Result<Option<Vec<lsp::TextEdit>>>>> { | ||
let capabilities = self.capabilities.get().unwrap(); | ||
|
||
match &capabilities.text_document_sync.as_ref()? { | ||
lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions { | ||
will_save_wait_until: enabled, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly here about pattern matching |
||
.. | ||
}) => { | ||
if !*enabled.as_ref()? { | ||
return None; | ||
} | ||
} | ||
_ => return None, | ||
}; | ||
|
||
Some(self.call_with_timeout::<lsp::request::WillSaveWaitUntil>( | ||
&lsp::WillSaveTextDocumentParams { | ||
text_document, | ||
reason, | ||
}, | ||
5, | ||
)) | ||
} | ||
|
||
pub fn text_document_did_save( | ||
&self, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ use helix_vcs::DiffProviderRegistry; | |
|
||
use futures_util::stream::select_all::SelectAll; | ||
use futures_util::{future, StreamExt}; | ||
use helix_lsp::{Call, LanguageServerId}; | ||
use helix_lsp::{lsp::TextDocumentSaveReason, Call, LanguageServerId}; | ||
use tokio_stream::wrappers::UnboundedReceiverStream; | ||
|
||
use std::{ | ||
|
@@ -1948,12 +1948,54 @@ impl Editor { | |
doc_id: DocumentId, | ||
path: Option<P>, | ||
force: bool, | ||
reason: TextDocumentSaveReason, | ||
) -> anyhow::Result<()> { | ||
// convert a channel of futures to pipe into main queue one by one | ||
// via stream.then() ? then push into main future | ||
|
||
let path = path.map(|path| path.into()); | ||
let view_id = self.get_synced_view_id(doc_id); | ||
let doc = doc_mut!(self, &doc_id); | ||
let view = view_mut!(self, view_id); | ||
let url = match &path { | ||
Some(path) => url::Url::from_file_path(path).ok(), | ||
None => doc.url(), | ||
}; | ||
if let Some(url) = url { | ||
let identifier = lsp::TextDocumentIdentifier::new(url.clone()); | ||
let language_servers: Vec<_> = self | ||
.language_servers | ||
.iter_clients() | ||
Comment on lines
+1966
to
+1968
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will send the will-saves to all language servers even if the document doesn't belong to a language server. Instead this can iterate over |
||
.filter(|client| client.is_initialized()) | ||
.cloned() | ||
.collect(); | ||
for language_server in language_servers { | ||
language_server.text_document_will_save(identifier.clone(), reason); | ||
|
||
let Some(request) = | ||
language_server.text_document_will_save_wait_until(identifier.clone(), reason) | ||
else { | ||
continue; | ||
}; | ||
let edits = match helix_lsp::block_on(request) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will block the main thread and it could take a while if a language server sends edits when saving many documents with |
||
Ok(Some(edits)) => edits, | ||
Ok(None) => continue, | ||
Err(err) => { | ||
log::error!("invalid willSaveWaitUntil response: {err:?}"); | ||
continue; | ||
} | ||
}; | ||
let transaction = helix_lsp::util::generate_transaction_from_edits( | ||
doc.text(), | ||
edits, | ||
language_server.offset_encoding(), | ||
); | ||
|
||
doc.apply(&transaction, view.id); | ||
doc.append_changes_to_history(view); | ||
} | ||
} | ||
|
||
let doc_save_future = doc.save(path, force)?; | ||
|
||
// When a file is written to, notify the file event handler. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To match other notification-style functions in the client let's not return anything here,
Similarly see
text_document_did_open