Skip to content

Commit 26c340d

Browse files
jpttrssnjunglerobba
andcommitted
allow textDocument/didChange notification to be synchronous
Co-authored-by: junglerobba <[email protected]>
1 parent 37fe42d commit 26c340d

File tree

9 files changed

+180
-39
lines changed

9 files changed

+180
-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;
@@ -516,6 +517,27 @@ impl Client {
516517
}
517518
}
518519

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

9551022
// Return early if the server does not support document sync.
@@ -981,11 +1048,7 @@ impl Client {
9811048
kind => unimplemented!("{:?}", kind),
9821049
};
9831050

984-
self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
985-
text_document,
986-
content_changes: changes,
987-
});
988-
Some(())
1051+
Some(changes)
9891052
}
9901053

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

helix-term/src/handlers/document_colors.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ 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 matches!(
178+
event.emit_lsp_notification,
179+
Some(helix_view::document::EmitLspNotification::Sync)
180+
) {
178181
// Cancel the ongoing request, if present.
179182
event.doc.color_swatch_controller.cancel();
180183
helix_event::send_blocking(&tx, DocumentColorsEvent(event.doc.id()));

helix-term/src/handlers/signature_help.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use helix_core::syntax::config::LanguageServerFeature;
55
use helix_event::{cancelable_future, register_hook, send_blocking, TaskController, TaskHandle};
66
use helix_lsp::lsp::{self, SignatureInformation};
77
use helix_stdx::rope::RopeSliceExt;
8-
use helix_view::document::Mode;
8+
use helix_view::document::{EmitLspNotification, Mode};
99
use helix_view::events::{DocumentDidChange, SelectionDidChange};
1010
use helix_view::handlers::lsp::{SignatureHelpEvent, SignatureHelpInvoked};
1111
use helix_view::Editor;
@@ -353,7 +353,9 @@ 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
357+
&& matches!(event.emit_lsp_notification, Some(EmitLspNotification::Sync))
358+
{
357359
send_blocking(&tx, SignatureHelpEvent::ReTrigger);
358360
}
359361
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,
@@ -1376,7 +1382,7 @@ impl Document {
13761382
&mut self,
13771383
transaction: &Transaction,
13781384
view_id: ViewId,
1379-
emit_lsp_notification: bool,
1385+
emit_lsp_notification: Option<EmitLspNotification>,
13801386
) -> bool {
13811387
use helix_core::Assoc;
13821388

@@ -1525,7 +1531,7 @@ impl Document {
15251531
view: view_id,
15261532
old_text: &old_doc,
15271533
changes,
1528-
ghost_transaction: !emit_lsp_notification,
1534+
emit_lsp_notification,
15291535
});
15301536

15311537
// if specified, the current selection should instead be replaced by transaction.selection
@@ -1547,7 +1553,7 @@ impl Document {
15471553
&mut self,
15481554
transaction: &Transaction,
15491555
view_id: ViewId,
1550-
emit_lsp_notification: bool,
1556+
emit_lsp_notification: Option<EmitLspNotification>,
15511557
) -> bool {
15521558
// store the state just before any changes are made. This allows us to undo to the
15531559
// state just before a transaction was applied.
@@ -1570,14 +1576,20 @@ impl Document {
15701576
}
15711577
/// Apply a [`Transaction`] to the [`Document`] to change its text.
15721578
pub fn apply(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
1573-
self.apply_inner(transaction, view_id, true)
1579+
self.apply_inner(transaction, view_id, Some(EmitLspNotification::Async))
1580+
}
1581+
1582+
/// Apply a [`Transaction`] to the [`Document`] to change its text and
1583+
/// emit the lsp notifcation synchronously
1584+
pub fn apply_sync_notification(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
1585+
self.apply_inner(transaction, view_id, Some(EmitLspNotification::Sync))
15741586
}
15751587

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

15831595
fn undo_redo_impl(&mut self, view: &mut View, undo: bool) -> bool {
@@ -1589,7 +1601,7 @@ impl Document {
15891601
let mut history = self.history.take();
15901602
let txn = if undo { history.undo() } else { history.redo() };
15911603
let success = if let Some(txn) = txn {
1592-
self.apply_impl(txn, view.id, true)
1604+
self.apply_impl(txn, view.id, Some(EmitLspNotification::Async))
15931605
} else {
15941606
false
15951607
};
@@ -1645,7 +1657,12 @@ impl Document {
16451657
savepoint
16461658
}
16471659

1648-
pub fn restore(&mut self, view: &mut View, savepoint: &SavePoint, emit_lsp_notification: bool) {
1660+
pub fn restore(
1661+
&mut self,
1662+
view: &mut View,
1663+
savepoint: &SavePoint,
1664+
emit_lsp_notification: Option<EmitLspNotification>,
1665+
) {
16491666
assert_eq!(
16501667
savepoint.view, view.id,
16511668
"Savepoint must not be used with a different view!"
@@ -1678,7 +1695,7 @@ impl Document {
16781695
};
16791696
let mut success = false;
16801697
for txn in txns {
1681-
if self.apply_impl(&txn, view.id, true) {
1698+
if self.apply_impl(&txn, view.id, Some(EmitLspNotification::Async)) {
16821699
success = true;
16831700
}
16841701
}

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)