Utilize LSP completion itemDefaults a bit

Tailwind likes to throw a lot of completion data at us, this gets it to
send less. Previously it would respond to a completion with 2.5 MB JSON
blob, now it is more like 0.8 MB.

Relies on a local copy of lsp-types with the `itemDefaults` field added.
I don't have write perms to push to our fork of the crate atm, sorry :)
This commit is contained in:
Julia 2023-08-17 21:57:39 -04:00
parent 4f0fa21c04
commit a979e32127
4 changed files with 62 additions and 21 deletions

4
Cargo.lock generated
View file

@ -4185,9 +4185,7 @@ dependencies = [
[[package]] [[package]]
name = "lsp-types" name = "lsp-types"
version = "0.94.0" version = "0.94.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"serde", "serde",

View file

@ -20,7 +20,7 @@ anyhow.workspace = true
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true } async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true }
futures.workspace = true futures.workspace = true
log.workspace = true log.workspace = true
lsp-types = "0.94" lsp-types = { path = "../../../lsp-types" }
parking_lot.workspace = true parking_lot.workspace = true
postage.workspace = true postage.workspace = true
serde.workspace = true serde.workspace = true

View file

@ -423,6 +423,14 @@ impl LanguageServer {
}), }),
..Default::default() ..Default::default()
}), }),
completion_list: Some(CompletionListCapability {
item_defaults: Some(vec![
"commitCharacters".to_owned(),
"editRange".to_owned(),
"insertTextMode".to_owned(),
"data".to_owned(),
]),
}),
..Default::default() ..Default::default()
}), }),
rename: Some(RenameClientCapabilities { rename: Some(RenameClientCapabilities {

View file

@ -15,7 +15,10 @@ use language::{
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction,
Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
}; };
use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities}; use lsp::{
CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId,
ServerCapabilities,
};
use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions { pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
@ -1341,10 +1344,16 @@ impl LspCommand for GetCompletions {
server_id: LanguageServerId, server_id: LanguageServerId,
cx: AsyncAppContext, cx: AsyncAppContext,
) -> Result<Vec<Completion>> { ) -> Result<Vec<Completion>> {
let mut response_list = None;
let completions = if let Some(completions) = completions { let completions = if let Some(completions) = completions {
match completions { match completions {
lsp::CompletionResponse::Array(completions) => completions, lsp::CompletionResponse::Array(completions) => completions,
lsp::CompletionResponse::List(list) => list.items,
lsp::CompletionResponse::List(mut list) => {
let items = std::mem::take(&mut list.items);
response_list = Some(list);
items
}
} }
} else { } else {
Default::default() Default::default()
@ -1354,6 +1363,7 @@ impl LspCommand for GetCompletions {
let language = buffer.language().cloned(); let language = buffer.language().cloned();
let snapshot = buffer.snapshot(); let snapshot = buffer.snapshot();
let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left); let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
let mut range_for_token = None; let mut range_for_token = None;
completions completions
.into_iter() .into_iter()
@ -1374,6 +1384,7 @@ impl LspCommand for GetCompletions {
edit.new_text.clone(), edit.new_text.clone(),
) )
} }
// If the language server does not provide a range, then infer // If the language server does not provide a range, then infer
// the range based on the syntax tree. // the range based on the syntax tree.
None => { None => {
@ -1381,27 +1392,51 @@ impl LspCommand for GetCompletions {
log::info!("completion out of expected range"); log::info!("completion out of expected range");
return None; return None;
} }
let Range { start, end } = range_for_token
.get_or_insert_with(|| { let default_edit_range = response_list
let offset = self.position.to_offset(&snapshot); .as_ref()
let (range, kind) = snapshot.surrounding_word(offset); .and_then(|list| list.item_defaults.as_ref())
if kind == Some(CharKind::Word) { .and_then(|defaults| defaults.edit_range.as_ref())
range .and_then(|range| match range {
} else { CompletionListItemDefaultsEditRange::Range(r) => Some(r),
offset..offset _ => None,
} });
})
.clone(); let range = if let Some(range) = default_edit_range {
let range = range_from_lsp(range.clone());
let start = snapshot.clip_point_utf16(range.start, Bias::Left);
let end = snapshot.clip_point_utf16(range.end, Bias::Left);
if start != range.start.0 || end != range.end.0 {
log::info!("completion out of expected range");
return None;
}
snapshot.anchor_before(start)..snapshot.anchor_after(end)
} else {
range_for_token
.get_or_insert_with(|| {
let offset = self.position.to_offset(&snapshot);
let (range, kind) = snapshot.surrounding_word(offset);
let range = if kind == Some(CharKind::Word) {
range
} else {
offset..offset
};
snapshot.anchor_before(range.start)
..snapshot.anchor_after(range.end)
})
.clone()
};
let text = lsp_completion let text = lsp_completion
.insert_text .insert_text
.as_ref() .as_ref()
.unwrap_or(&lsp_completion.label) .unwrap_or(&lsp_completion.label)
.clone(); .clone();
( (range, text)
snapshot.anchor_before(start)..snapshot.anchor_after(end),
text,
)
} }
Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => { Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
log::info!("unsupported insert/replace completion"); log::info!("unsupported insert/replace completion");
return None; return None;