Wait for request and response version before resolving completions

This commit is contained in:
Antonio Scandurra 2022-02-15 10:16:29 +01:00
parent 809b843ceb
commit 17b13b9362
3 changed files with 72 additions and 55 deletions

View File

@ -1334,18 +1334,18 @@ impl Project {
cx.spawn(|_, cx| async move { cx.spawn(|_, cx| async move {
let completions = lang_server let completions = lang_server
.request::<lsp::request::Completion>(lsp::CompletionParams { .request::<lsp::request::Completion>(lsp::CompletionParams {
text_document_position: lsp::TextDocumentPositionParams::new( text_document_position: lsp::TextDocumentPositionParams::new(
lsp::TextDocumentIdentifier::new( lsp::TextDocumentIdentifier::new(
lsp::Url::from_file_path(buffer_abs_path).unwrap(), lsp::Url::from_file_path(buffer_abs_path).unwrap(),
),
position.to_lsp_position(),
), ),
position.to_lsp_position(), context: Default::default(),
), work_done_progress_params: Default::default(),
context: Default::default(), partial_result_params: Default::default(),
work_done_progress_params: Default::default(), })
partial_result_params: Default::default(), .await?;
})
.await?;
let completions = if let Some(completions) = completions { let completions = if let Some(completions) = completions {
match completions { match completions {
@ -1357,41 +1357,54 @@ impl Project {
}; };
source_buffer_handle.read_with(&cx, |this, _| { source_buffer_handle.read_with(&cx, |this, _| {
Ok(completions.into_iter().filter_map(|lsp_completion| { Ok(completions
let (old_range, new_text) = match lsp_completion.text_edit.as_ref()? { .into_iter()
lsp::CompletionTextEdit::Edit(edit) => (range_from_lsp(edit.range), edit.new_text.clone()), .filter_map(|lsp_completion| {
lsp::CompletionTextEdit::InsertAndReplace(_) => { let (old_range, new_text) = match lsp_completion.text_edit.as_ref()? {
log::info!("received an insert and replace completion but we don't yet support that"); lsp::CompletionTextEdit::Edit(edit) => {
return None (range_from_lsp(edit.range), edit.new_text.clone())
}, }
}; lsp::CompletionTextEdit::InsertAndReplace(_) => {
log::info!("unsupported insert/replace completion");
return None;
}
};
let clipped_start = this.clip_point_utf16(old_range.start, Bias::Left); let clipped_start = this.clip_point_utf16(old_range.start, Bias::Left);
let clipped_end = this.clip_point_utf16(old_range.end, Bias::Left) ; let clipped_end = this.clip_point_utf16(old_range.end, Bias::Left);
if clipped_start == old_range.start && clipped_end == old_range.end { if clipped_start == old_range.start && clipped_end == old_range.end {
Some(Completion { Some(Completion {
old_range: this.anchor_before(old_range.start)..this.anchor_after(old_range.end), old_range: this.anchor_before(old_range.start)
new_text, ..this.anchor_after(old_range.end),
label: language.as_ref().and_then(|l| l.label_for_completion(&lsp_completion)).unwrap_or_else(|| CompletionLabel::plain(&lsp_completion)), new_text,
lsp_completion, label: language
}) .as_ref()
} else { .and_then(|l| l.label_for_completion(&lsp_completion))
None .unwrap_or_else(|| CompletionLabel::plain(&lsp_completion)),
} lsp_completion,
}).collect()) })
} else {
None
}
})
.collect())
}) })
}) })
} else if let Some(project_id) = self.remote_id() { } else if let Some(project_id) = self.remote_id() {
let rpc = self.client.clone(); let rpc = self.client.clone();
cx.foreground().spawn(async move { let message = proto::GetCompletions {
let response = rpc project_id,
.request(proto::GetCompletions { buffer_id,
project_id, position: Some(language::proto::serialize_anchor(&anchor)),
buffer_id, version: (&source_buffer.version()).into(),
position: Some(language::proto::serialize_anchor(&anchor)), };
cx.spawn_weak(|_, mut cx| async move {
let response = rpc.request(message).await?;
source_buffer_handle
.update(&mut cx, |buffer, _| {
buffer.wait_for_version(response.version.into())
}) })
.await?; .await;
response response
.completions .completions
.into_iter() .into_iter()
@ -2326,21 +2339,27 @@ impl Project {
.position .position
.and_then(language::proto::deserialize_anchor) .and_then(language::proto::deserialize_anchor)
.ok_or_else(|| anyhow!("invalid position"))?; .ok_or_else(|| anyhow!("invalid position"))?;
let completions = this.update(&mut cx, |this, cx| { let version = clock::Global::from(envelope.payload.version);
let buffer = this let buffer = this.read_with(&cx, |this, _| {
.shared_buffers this.shared_buffers
.get(&sender_id) .get(&sender_id)
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned()) .and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?; .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
Ok::<_, anyhow::Error>(this.completions(&buffer, position, cx))
})?; })?;
buffer
.update(&mut cx, |buffer, _| buffer.wait_for_version(version))
.await;
let version = buffer.read_with(&cx, |buffer, _| buffer.version());
let completions = this
.update(&mut cx, |this, cx| this.completions(&buffer, position, cx))
.await?;
Ok(proto::GetCompletionsResponse { Ok(proto::GetCompletionsResponse {
completions: completions completions: completions
.await?
.iter() .iter()
.map(language::proto::serialize_completion) .map(language::proto::serialize_completion)
.collect(), .collect(),
version: (&version).into(),
}) })
} }

View File

@ -222,10 +222,12 @@ message GetCompletions {
uint64 project_id = 1; uint64 project_id = 1;
uint64 buffer_id = 2; uint64 buffer_id = 2;
Anchor position = 3; Anchor position = 3;
repeated VectorClockEntry version = 4;
} }
message GetCompletionsResponse { message GetCompletionsResponse {
repeated Completion completions = 1; repeated Completion completions = 1;
repeated VectorClockEntry version = 2;
} }
message ApplyCompletionAdditionalEdits { message ApplyCompletionAdditionalEdits {

View File

@ -2336,9 +2336,10 @@ mod tests {
.await; .await;
// Confirm a completion on the guest. // Confirm a completion on the guest.
editor_b.next_notification(&cx_b).await; editor_b
.condition(&cx_b, |editor, _| editor.context_menu_visible())
.await;
editor_b.update(&mut cx_b, |editor, cx| { editor_b.update(&mut cx_b, |editor, cx| {
assert!(editor.context_menu_visible());
editor.confirm_completion(&ConfirmCompletion(Some(0)), cx); editor.confirm_completion(&ConfirmCompletion(Some(0)), cx);
assert_eq!(editor.text(cx), "fn main() { a.first_method() }"); assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
}); });
@ -2363,22 +2364,17 @@ mod tests {
} }
}); });
// The additional edit is applied.
buffer_a buffer_a
.condition(&cx_a, |buffer, _| { .condition(&cx_a, |buffer, _| {
buffer.text() == "fn main() { a.first_method() }" buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
}) })
.await; .await;
// The additional edit is applied.
buffer_b buffer_b
.condition(&cx_b, |buffer, _| { .condition(&cx_b, |buffer, _| {
buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }" buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
}) })
.await; .await;
assert_eq!(
buffer_a.read_with(&cx_a, |buffer, _| buffer.text()),
buffer_b.read_with(&cx_b, |buffer, _| buffer.text()),
);
} }
#[gpui::test(iterations = 10)] #[gpui::test(iterations = 10)]