diff options
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 139 |
1 files changed, 124 insertions, 15 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 96983dc52..3c3d82b3b 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -917,7 +917,7 @@ impl Inner { let mut code_lenses = cl.borrow_mut(); // TSC Implementations Code Lens - if self.config.settings.enabled_code_lens_implementations() { + if self.config.settings.code_lens.implementations { let source = CodeLensSource::Implementations; match i.kind { tsc::ScriptElementKind::InterfaceElement => { @@ -941,7 +941,7 @@ impl Inner { } // TSC References Code Lens - if self.config.settings.enabled_code_lens_references() { + if self.config.settings.code_lens.references { let source = CodeLensSource::References; if let Some(parent) = &mp { if parent.kind == tsc::ScriptElementKind::EnumElement { @@ -950,11 +950,7 @@ impl Inner { } match i.kind { tsc::ScriptElementKind::FunctionElement => { - if self - .config - .settings - .enabled_code_lens_references_all_functions() - { + if self.config.settings.code_lens.references_all_functions { code_lenses.push(i.to_code_lens( &line_index, &specifier, @@ -1358,7 +1354,6 @@ impl Inner { let specifier = self .url_map .normalize_url(¶ms.text_document_position.text_document.uri); - // TODO(lucacasonato): handle error correctly let line_index = if let Some(line_index) = self.get_line_index_sync(&specifier) { line_index @@ -1368,13 +1363,22 @@ impl Inner { specifier ))); }; + let trigger_character = if let Some(context) = ¶ms.context { + context.trigger_character.clone() + } else { + None + }; + let position = + line_index.offset_tsc(params.text_document_position.position)?; let req = tsc::RequestMethod::GetCompletions(( - specifier, - line_index.offset_tsc(params.text_document_position.position)?, - tsc::UserPreferences { - // TODO(lucacasonato): enable this. see https://github.com/denoland/deno/pull/8651 - include_completions_with_insert_text: Some(false), - ..Default::default() + specifier.clone(), + position, + tsc::GetCompletionsAtPositionOptions { + user_preferences: tsc::UserPreferences { + include_completions_with_insert_text: Some(true), + ..Default::default() + }, + trigger_character, }, )); let maybe_completion_info: Option<tsc::CompletionInfo> = self @@ -1387,7 +1391,12 @@ impl Inner { })?; if let Some(completions) = maybe_completion_info { - let results = completions.into_completion_response(&line_index); + let results = completions.as_completion_response( + &line_index, + &self.config.settings.suggest, + &specifier, + position, + ); self.performance.measure(mark); Ok(Some(results)) } else { @@ -1396,6 +1405,47 @@ impl Inner { } } + async fn completion_resolve( + &mut self, + params: CompletionItem, + ) -> LspResult<CompletionItem> { + let mark = self.performance.mark("completion_resolve"); + if let Some(data) = ¶ms.data { + let data: tsc::CompletionItemData = serde_json::from_value(data.clone()) + .map_err(|err| { + error!("{}", err); + LspError::invalid_params( + "Could not decode data field of completion item.", + ) + })?; + let req = tsc::RequestMethod::GetCompletionDetails(data.into()); + let maybe_completion_info: Option<tsc::CompletionEntryDetails> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get completion info from TypeScript: {}", err); + LspError::internal_error() + })?; + if let Some(completion_info) = maybe_completion_info { + let completion_item = completion_info.as_completion_item(¶ms); + self.performance.measure(mark); + Ok(completion_item) + } else { + error!( + "Received an undefined response from tsc for completion details." + ); + self.performance.measure(mark); + Ok(params) + } + } else { + self.performance.measure(mark); + Err(LspError::invalid_params( + "The completion item is missing the data field.", + )) + } + } + async fn goto_implementation( &mut self, params: GotoImplementationParams, @@ -1715,6 +1765,13 @@ impl lspower::LanguageServer for LanguageServer { self.0.lock().await.completion(params).await } + async fn completion_resolve( + &self, + params: CompletionItem, + ) -> LspResult<CompletionItem> { + self.0.lock().await.completion_resolve(params).await + } + async fn goto_implementation( &self, params: GotoImplementationParams, @@ -2741,6 +2798,58 @@ mod tests { } #[derive(Deserialize)] + struct CompletionResult { + pub result: Option<CompletionResponse>, + } + + #[tokio::test] + async fn test_completions() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ("did_open_notification_completions.json", LspResponse::None), + ( + "completion_request.json", + LspResponse::RequestAssert(|value| { + let response: CompletionResult = + serde_json::from_value(value).unwrap(); + let result = response.result.unwrap(); + match result { + CompletionResponse::List(list) => { + // there should be at least 90 completions for `Deno.` + assert!(list.items.len() > 90); + } + _ => panic!("unexpected result"), + } + }), + ), + ( + "completion_resolve_request.json", + LspResponse::Request( + 4, + json!({ + "label": "build", + "kind": 6, + "detail": "const Deno.build: {\n target: string;\n arch: \"x86_64\";\n os: \"darwin\" | \"linux\" | \"windows\";\n vendor: string;\n env?: string | undefined;\n}", + "documentation": { + "kind": "markdown", + "value": "Build related information." + }, + "sortText": "1", + "insertTextFormat": 1, + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + + #[derive(Deserialize)] struct PerformanceAverages { averages: Vec<PerformanceAverage>, } |