Skip to content

Commit 9102dae

Browse files
jpttrssnjunglerobba
andcommitted
allow textDocument/didChange notification to be synchronous
Co-authored-by: junglerobba <junglerobba@jngl.one>
1 parent 109c812 commit 9102dae

File tree

10 files changed

+172
-39
lines changed

10 files changed

+172
-39
lines changed

helix-lsp/src/client.rs

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::lsp::{
1212
};
1313
use helix_core::{find_workspace, syntax::config::LanguageServerFeature, ChangeSet, Rope};
1414
use helix_loader::VERSION_AND_GIT_HASH;
15+
use helix_lsp_types::TextDocumentContentChangeEvent;
1516
use helix_stdx::path;
1617
use parking_lot::Mutex;
1718
use serde::Deserialize;
@@ -517,6 +518,27 @@ impl Client {
517518
}
518519
}
519520

521+
pub fn notify_sync<R: lsp::notification::Notification>(&self, params: R::Params) -> Result<()>
522+
where
523+
R::Params: serde::Serialize,
524+
{
525+
let server_tx = self.server_tx.clone();
526+
527+
let params = serde_json::to_value(params)?;
528+
529+
let notification = jsonrpc::Notification {
530+
jsonrpc: Some(jsonrpc::Version::V2),
531+
method: R::METHOD.to_string(),
532+
params: Self::value_into_params(params),
533+
};
534+
535+
server_tx
536+
.send(Payload::Notification(notification))
537+
.map_err(|e| Error::Other(e.into()))?;
538+
539+
Ok(())
540+
}
541+
520542
/// Reply to a language server RPC call.
521543
pub fn reply(
522544
&self,
@@ -958,6 +980,51 @@ impl Client {
958980
new_text: &Rope,
959981
changes: &ChangeSet,
960982
) -> Option<()> {
983+
if let Some(content_changes) =
984+
self.text_document_did_change_impl(old_text, new_text, changes)
985+
{
986+
return {
987+
self.notify::<lsp::notification::DidChangeTextDocument>(
988+
lsp::DidChangeTextDocumentParams {
989+
text_document,
990+
content_changes,
991+
},
992+
);
993+
Some(())
994+
};
995+
}
996+
None
997+
}
998+
999+
/// Will send textDocument/didChange notification synchronously
1000+
pub fn text_document_did_change_sync(
1001+
&self,
1002+
text_document: lsp::VersionedTextDocumentIdentifier,
1003+
old_text: &Rope,
1004+
new_text: &Rope,
1005+
changes: &ChangeSet,
1006+
) -> Option<Result<()>> {
1007+
if let Some(content_changes) =
1008+
self.text_document_did_change_impl(old_text, new_text, changes)
1009+
{
1010+
return Some(
1011+
self.notify_sync::<lsp::notification::DidChangeTextDocument>(
1012+
lsp::DidChangeTextDocumentParams {
1013+
text_document,
1014+
content_changes,
1015+
},
1016+
),
1017+
);
1018+
}
1019+
None
1020+
}
1021+
1022+
pub fn text_document_did_change_impl(
1023+
&self,
1024+
old_text: &Rope,
1025+
new_text: &Rope,
1026+
changes: &ChangeSet,
1027+
) -> Option<Vec<TextDocumentContentChangeEvent>> {
9611028
let capabilities = self.capabilities.get().unwrap();
9621029

9631030
// Return early if the server does not support document sync.
@@ -989,11 +1056,7 @@ impl Client {
9891056
kind => unimplemented!("{:?}", kind),
9901057
};
9911058

992-
self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
993-
text_document,
994-
content_changes: changes,
995-
});
996-
Some(())
1059+
Some(changes)
9971060
}
9981061

9991062
pub fn text_document_did_close(&self, text_document: lsp::TextDocumentIdentifier) {

helix-term/src/handlers/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub(super) fn register_hooks(handlers: &Handlers) {
4444
if event
4545
.doc
4646
.has_language_server_with_feature(LanguageServerFeature::PullDiagnostics)
47-
&& !event.ghost_transaction
47+
&& event.emit_lsp_notification.is_some()
4848
{
4949
// Cancel the ongoing request, if present.
5050
event.doc.pull_diagnostic_controller.cancel();

helix-term/src/handlers/document_colors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ pub(super) fn register_hooks(handlers: &Handlers) {
174174
// Avoid re-requesting document colors if the change is a ghost transaction (completion)
175175
// because the language server will not know about the updates to the document and will
176176
// give out-of-date locations.
177-
if !event.ghost_transaction {
177+
if event.emit_lsp_notification.is_some() {
178178
// Cancel the ongoing request, if present.
179179
event.doc.color_swatch_controller.cancel();
180180
helix_event::send_blocking(&tx, DocumentColorsEvent(event.doc.id()));

helix-term/src/handlers/signature_help.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ pub(super) fn register_hooks(handlers: &Handlers) {
353353

354354
let tx = handlers.signature_hints.clone();
355355
register_hook!(move |event: &mut DocumentDidChange<'_>| {
356-
if event.doc.config.load().lsp.auto_signature_help && !event.ghost_transaction {
356+
if event.doc.config.load().lsp.auto_signature_help && event.emit_lsp_notification.is_some()
357+
{
357358
send_blocking(&tx, SignatureHelpEvent::ReTrigger);
358359
}
359360
Ok(())

helix-term/src/ui/completion.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ impl Completion {
178178
let item = item.unwrap();
179179
let context = &editor.handlers.completions.active_completions[&item.provider()];
180180
// if more text was entered, remove it
181-
doc.restore(view, &context.savepoint, false);
181+
doc.restore(view, &context.savepoint, None);
182182
// always present here
183183

184184
match item {
@@ -203,13 +203,17 @@ impl Completion {
203203
if let Some(CompleteAction::Selected { savepoint }) =
204204
editor.last_completion.take()
205205
{
206-
doc.restore(view, &savepoint, false);
206+
doc.restore(view, &savepoint, None);
207207
}
208208

209209
let item = item.unwrap();
210210
let context = &editor.handlers.completions.active_completions[&item.provider()];
211211
// if more text was entered, remove it
212-
doc.restore(view, &context.savepoint, true);
212+
doc.restore(
213+
view,
214+
&context.savepoint,
215+
Some(helix_view::document::EmitLspNotification::Async),
216+
);
213217
// save an undo checkpoint before the completion
214218
doc.append_changes_to_history(view);
215219

helix-term/src/ui/editor.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,11 @@ impl EditorView {
976976
let (view, doc) = current!(cxt.editor);
977977

978978
if let Some(last_savepoint) = last_savepoint.as_deref() {
979-
doc.restore(view, last_savepoint, true);
979+
doc.restore(
980+
view,
981+
last_savepoint,
982+
Some(helix_view::document::EmitLspNotification::Async),
983+
);
980984
}
981985

982986
let text = doc.text().slice(..);
@@ -1082,7 +1086,7 @@ impl EditorView {
10821086
}
10831087
CompleteAction::Selected { savepoint } => {
10841088
let (view, doc) = current!(editor);
1085-
doc.restore(view, &savepoint, false);
1089+
doc.restore(view, &savepoint, None);
10861090
}
10871091
}
10881092
}

helix-view/src/document.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ pub enum DocumentOpenError {
138138
IoError(#[from] io::Error),
139139
}
140140

141+
#[derive(Debug)]
142+
pub enum EmitLspNotification {
143+
Async,
144+
Sync,
145+
}
146+
141147
pub struct Document {
142148
pub(crate) id: DocumentId,
143149
text: Rope,
@@ -1381,7 +1387,7 @@ impl Document {
13811387
&mut self,
13821388
transaction: &Transaction,
13831389
view_id: ViewId,
1384-
emit_lsp_notification: bool,
1390+
emit_lsp_notification: Option<EmitLspNotification>,
13851391
) -> bool {
13861392
use helix_core::Assoc;
13871393

@@ -1530,7 +1536,7 @@ impl Document {
15301536
view: view_id,
15311537
old_text: &old_doc,
15321538
changes,
1533-
ghost_transaction: !emit_lsp_notification,
1539+
emit_lsp_notification,
15341540
});
15351541

15361542
// if specified, the current selection should instead be replaced by transaction.selection
@@ -1552,7 +1558,7 @@ impl Document {
15521558
&mut self,
15531559
transaction: &Transaction,
15541560
view_id: ViewId,
1555-
emit_lsp_notification: bool,
1561+
emit_lsp_notification: Option<EmitLspNotification>,
15561562
) -> bool {
15571563
// store the state just before any changes are made. This allows us to undo to the
15581564
// state just before a transaction was applied.
@@ -1575,14 +1581,20 @@ impl Document {
15751581
}
15761582
/// Apply a [`Transaction`] to the [`Document`] to change its text.
15771583
pub fn apply(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
1578-
self.apply_inner(transaction, view_id, true)
1584+
self.apply_inner(transaction, view_id, Some(EmitLspNotification::Async))
1585+
}
1586+
1587+
/// Apply a [`Transaction`] to the [`Document`] to change its text and
1588+
/// emit the lsp notifcation synchronously
1589+
pub fn apply_sync_notification(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
1590+
self.apply_inner(transaction, view_id, Some(EmitLspNotification::Sync))
15791591
}
15801592

15811593
/// Apply a [`Transaction`] to the [`Document`] to change its text
15821594
/// without notifying the language servers. This is useful for temporary transactions
15831595
/// that must not influence the server.
15841596
pub fn apply_temporary(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
1585-
self.apply_inner(transaction, view_id, false)
1597+
self.apply_inner(transaction, view_id, None)
15861598
}
15871599

15881600
fn undo_redo_impl(&mut self, view: &mut View, undo: bool) -> bool {
@@ -1594,7 +1606,7 @@ impl Document {
15941606
let mut history = self.history.take();
15951607
let txn = if undo { history.undo() } else { history.redo() };
15961608
let success = if let Some(txn) = txn {
1597-
self.apply_impl(txn, view.id, true)
1609+
self.apply_impl(txn, view.id, Some(EmitLspNotification::Async))
15981610
} else {
15991611
false
16001612
};
@@ -1650,7 +1662,12 @@ impl Document {
16501662
savepoint
16511663
}
16521664

1653-
pub fn restore(&mut self, view: &mut View, savepoint: &SavePoint, emit_lsp_notification: bool) {
1665+
pub fn restore(
1666+
&mut self,
1667+
view: &mut View,
1668+
savepoint: &SavePoint,
1669+
emit_lsp_notification: Option<EmitLspNotification>,
1670+
) {
16541671
assert_eq!(
16551672
savepoint.view, view.id,
16561673
"Savepoint must not be used with a different view!"
@@ -1683,7 +1700,7 @@ impl Document {
16831700
};
16841701
let mut success = false;
16851702
for txn in txns {
1686-
if self.apply_impl(&txn, view.id, true) {
1703+
if self.apply_impl(&txn, view.id, Some(EmitLspNotification::Async)) {
16871704
success = true;
16881705
}
16891706
}

helix-view/src/events.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use helix_core::{ChangeSet, Rope};
22
use helix_event::events;
33
use helix_lsp::LanguageServerId;
44

5-
use crate::{editor::Config, Document, DocumentId, Editor, ViewId};
5+
use crate::{document::EmitLspNotification, editor::Config, Document, DocumentId, Editor, ViewId};
66

77
events! {
88
DocumentDidOpen<'a> {
@@ -14,7 +14,7 @@ events! {
1414
view: ViewId,
1515
old_text: &'a Rope,
1616
changes: &'a ChangeSet,
17-
ghost_transaction: bool
17+
emit_lsp_notification: Option<EmitLspNotification>
1818
}
1919
DocumentDidClose<'a> {
2020
editor: &'a mut Editor,

0 commit comments

Comments
 (0)