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:
parent
4f0fa21c04
commit
a979e32127
4 changed files with 62 additions and 21 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue