Skip to content

Commit 506b321

Browse files
authored
refactor(lsp): refactor completions and add tests (denoland#9789)
1 parent 2ff9b01 commit 506b321

9 files changed

+787
-135
lines changed

cli/lsp/capabilities.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ pub fn server_capabilities(
5555
)),
5656
hover_provider: Some(HoverProviderCapability::Simple(true)),
5757
completion_provider: Some(CompletionOptions {
58-
all_commit_characters: None,
58+
all_commit_characters: Some(vec![
59+
".".to_string(),
60+
",".to_string(),
61+
";".to_string(),
62+
"(".to_string(),
63+
]),
5964
trigger_characters: Some(vec![
6065
".".to_string(),
6166
"\"".to_string(),
@@ -66,7 +71,7 @@ pub fn server_capabilities(
6671
"<".to_string(),
6772
"#".to_string(),
6873
]),
69-
resolve_provider: None,
74+
resolve_provider: Some(true),
7075
work_done_progress_options: WorkDoneProgressOptions {
7176
work_done_progress: None,
7277
},
@@ -77,7 +82,7 @@ pub fn server_capabilities(
7782
"(".to_string(),
7883
"<".to_string(),
7984
]),
80-
retrigger_characters: None,
85+
retrigger_characters: Some(vec![")".to_string()]),
8186
work_done_progress_options: WorkDoneProgressOptions {
8287
work_done_progress: None,
8388
},

cli/lsp/config.rs

+40-32
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct ClientCapabilities {
1515
pub workspace_did_change_watched_files: bool,
1616
}
1717

18-
#[derive(Debug, Default, Clone, Deserialize)]
18+
#[derive(Debug, Clone, Deserialize)]
1919
#[serde(rename_all = "camelCase")]
2020
pub struct CodeLensSettings {
2121
/// Flag for providing implementation code lenses.
@@ -30,13 +30,50 @@ pub struct CodeLensSettings {
3030
pub references_all_functions: bool,
3131
}
3232

33+
impl Default for CodeLensSettings {
34+
fn default() -> Self {
35+
Self {
36+
implementations: false,
37+
references: false,
38+
references_all_functions: false,
39+
}
40+
}
41+
}
42+
43+
#[derive(Debug, Clone, Deserialize)]
44+
#[serde(rename_all = "camelCase")]
45+
pub struct CompletionSettings {
46+
#[serde(default)]
47+
pub complete_function_calls: bool,
48+
#[serde(default)]
49+
pub names: bool,
50+
#[serde(default)]
51+
pub paths: bool,
52+
#[serde(default)]
53+
pub auto_imports: bool,
54+
}
55+
56+
impl Default for CompletionSettings {
57+
fn default() -> Self {
58+
Self {
59+
complete_function_calls: false,
60+
names: true,
61+
paths: true,
62+
auto_imports: true,
63+
}
64+
}
65+
}
66+
3367
#[derive(Debug, Default, Clone, Deserialize)]
3468
#[serde(rename_all = "camelCase")]
3569
pub struct WorkspaceSettings {
3670
pub enable: bool,
3771
pub config: Option<String>,
3872
pub import_map: Option<String>,
39-
pub code_lens: Option<CodeLensSettings>,
73+
#[serde(default)]
74+
pub code_lens: CodeLensSettings,
75+
#[serde(default)]
76+
pub suggest: CompletionSettings,
4077

4178
#[serde(default)]
4279
pub lint: bool,
@@ -48,36 +85,7 @@ impl WorkspaceSettings {
4885
/// Determine if any code lenses are enabled at all. This allows short
4986
/// circuiting when there are no code lenses enabled.
5087
pub fn enabled_code_lens(&self) -> bool {
51-
if let Some(code_lens) = &self.code_lens {
52-
// This should contain all the "top level" code lens references
53-
code_lens.implementations || code_lens.references
54-
} else {
55-
false
56-
}
57-
}
58-
59-
pub fn enabled_code_lens_implementations(&self) -> bool {
60-
if let Some(code_lens) = &self.code_lens {
61-
code_lens.implementations
62-
} else {
63-
false
64-
}
65-
}
66-
67-
pub fn enabled_code_lens_references(&self) -> bool {
68-
if let Some(code_lens) = &self.code_lens {
69-
code_lens.references
70-
} else {
71-
false
72-
}
73-
}
74-
75-
pub fn enabled_code_lens_references_all_functions(&self) -> bool {
76-
if let Some(code_lens) = &self.code_lens {
77-
code_lens.references_all_functions
78-
} else {
79-
false
80-
}
88+
self.code_lens.implementations || self.code_lens.references
8189
}
8290
}
8391

cli/lsp/language_server.rs

+124-15
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ impl Inner {
917917
let mut code_lenses = cl.borrow_mut();
918918

919919
// TSC Implementations Code Lens
920-
if self.config.settings.enabled_code_lens_implementations() {
920+
if self.config.settings.code_lens.implementations {
921921
let source = CodeLensSource::Implementations;
922922
match i.kind {
923923
tsc::ScriptElementKind::InterfaceElement => {
@@ -941,7 +941,7 @@ impl Inner {
941941
}
942942

943943
// TSC References Code Lens
944-
if self.config.settings.enabled_code_lens_references() {
944+
if self.config.settings.code_lens.references {
945945
let source = CodeLensSource::References;
946946
if let Some(parent) = &mp {
947947
if parent.kind == tsc::ScriptElementKind::EnumElement {
@@ -950,11 +950,7 @@ impl Inner {
950950
}
951951
match i.kind {
952952
tsc::ScriptElementKind::FunctionElement => {
953-
if self
954-
.config
955-
.settings
956-
.enabled_code_lens_references_all_functions()
957-
{
953+
if self.config.settings.code_lens.references_all_functions {
958954
code_lenses.push(i.to_code_lens(
959955
&line_index,
960956
&specifier,
@@ -1358,7 +1354,6 @@ impl Inner {
13581354
let specifier = self
13591355
.url_map
13601356
.normalize_url(&params.text_document_position.text_document.uri);
1361-
// TODO(lucacasonato): handle error correctly
13621357
let line_index =
13631358
if let Some(line_index) = self.get_line_index_sync(&specifier) {
13641359
line_index
@@ -1368,13 +1363,22 @@ impl Inner {
13681363
specifier
13691364
)));
13701365
};
1366+
let trigger_character = if let Some(context) = &params.context {
1367+
context.trigger_character.clone()
1368+
} else {
1369+
None
1370+
};
1371+
let position =
1372+
line_index.offset_tsc(params.text_document_position.position)?;
13711373
let req = tsc::RequestMethod::GetCompletions((
1372-
specifier,
1373-
line_index.offset_tsc(params.text_document_position.position)?,
1374-
tsc::UserPreferences {
1375-
// TODO(lucacasonato): enable this. see https://github.com/denoland/deno/pull/8651
1376-
include_completions_with_insert_text: Some(false),
1377-
..Default::default()
1374+
specifier.clone(),
1375+
position,
1376+
tsc::GetCompletionsAtPositionOptions {
1377+
user_preferences: tsc::UserPreferences {
1378+
include_completions_with_insert_text: Some(true),
1379+
..Default::default()
1380+
},
1381+
trigger_character,
13781382
},
13791383
));
13801384
let maybe_completion_info: Option<tsc::CompletionInfo> = self
@@ -1387,7 +1391,12 @@ impl Inner {
13871391
})?;
13881392

13891393
if let Some(completions) = maybe_completion_info {
1390-
let results = completions.into_completion_response(&line_index);
1394+
let results = completions.as_completion_response(
1395+
&line_index,
1396+
&self.config.settings.suggest,
1397+
&specifier,
1398+
position,
1399+
);
13911400
self.performance.measure(mark);
13921401
Ok(Some(results))
13931402
} else {
@@ -1396,6 +1405,47 @@ impl Inner {
13961405
}
13971406
}
13981407

1408+
async fn completion_resolve(
1409+
&mut self,
1410+
params: CompletionItem,
1411+
) -> LspResult<CompletionItem> {
1412+
let mark = self.performance.mark("completion_resolve");
1413+
if let Some(data) = &params.data {
1414+
let data: tsc::CompletionItemData = serde_json::from_value(data.clone())
1415+
.map_err(|err| {
1416+
error!("{}", err);
1417+
LspError::invalid_params(
1418+
"Could not decode data field of completion item.",
1419+
)
1420+
})?;
1421+
let req = tsc::RequestMethod::GetCompletionDetails(data.into());
1422+
let maybe_completion_info: Option<tsc::CompletionEntryDetails> = self
1423+
.ts_server
1424+
.request(self.snapshot(), req)
1425+
.await
1426+
.map_err(|err| {
1427+
error!("Unable to get completion info from TypeScript: {}", err);
1428+
LspError::internal_error()
1429+
})?;
1430+
if let Some(completion_info) = maybe_completion_info {
1431+
let completion_item = completion_info.as_completion_item(&params);
1432+
self.performance.measure(mark);
1433+
Ok(completion_item)
1434+
} else {
1435+
error!(
1436+
"Received an undefined response from tsc for completion details."
1437+
);
1438+
self.performance.measure(mark);
1439+
Ok(params)
1440+
}
1441+
} else {
1442+
self.performance.measure(mark);
1443+
Err(LspError::invalid_params(
1444+
"The completion item is missing the data field.",
1445+
))
1446+
}
1447+
}
1448+
13991449
async fn goto_implementation(
14001450
&mut self,
14011451
params: GotoImplementationParams,
@@ -1715,6 +1765,13 @@ impl lspower::LanguageServer for LanguageServer {
17151765
self.0.lock().await.completion(params).await
17161766
}
17171767

1768+
async fn completion_resolve(
1769+
&self,
1770+
params: CompletionItem,
1771+
) -> LspResult<CompletionItem> {
1772+
self.0.lock().await.completion_resolve(params).await
1773+
}
1774+
17181775
async fn goto_implementation(
17191776
&self,
17201777
params: GotoImplementationParams,
@@ -2740,6 +2797,58 @@ mod tests {
27402797
harness.run().await;
27412798
}
27422799

2800+
#[derive(Deserialize)]
2801+
struct CompletionResult {
2802+
pub result: Option<CompletionResponse>,
2803+
}
2804+
2805+
#[tokio::test]
2806+
async fn test_completions() {
2807+
let mut harness = LspTestHarness::new(vec![
2808+
("initialize_request.json", LspResponse::RequestAny),
2809+
("initialized_notification.json", LspResponse::None),
2810+
("did_open_notification_completions.json", LspResponse::None),
2811+
(
2812+
"completion_request.json",
2813+
LspResponse::RequestAssert(|value| {
2814+
let response: CompletionResult =
2815+
serde_json::from_value(value).unwrap();
2816+
let result = response.result.unwrap();
2817+
match result {
2818+
CompletionResponse::List(list) => {
2819+
// there should be at least 90 completions for `Deno.`
2820+
assert!(list.items.len() > 90);
2821+
}
2822+
_ => panic!("unexpected result"),
2823+
}
2824+
}),
2825+
),
2826+
(
2827+
"completion_resolve_request.json",
2828+
LspResponse::Request(
2829+
4,
2830+
json!({
2831+
"label": "build",
2832+
"kind": 6,
2833+
"detail": "const Deno.build: {\n target: string;\n arch: \"x86_64\";\n os: \"darwin\" | \"linux\" | \"windows\";\n vendor: string;\n env?: string | undefined;\n}",
2834+
"documentation": {
2835+
"kind": "markdown",
2836+
"value": "Build related information."
2837+
},
2838+
"sortText": "1",
2839+
"insertTextFormat": 1,
2840+
}),
2841+
),
2842+
),
2843+
(
2844+
"shutdown_request.json",
2845+
LspResponse::Request(3, json!(null)),
2846+
),
2847+
("exit_notification.json", LspResponse::None),
2848+
]);
2849+
harness.run().await;
2850+
}
2851+
27432852
#[derive(Deserialize)]
27442853
struct PerformanceAverages {
27452854
averages: Vec<PerformanceAverage>,

0 commit comments

Comments
 (0)