diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2021-02-12 15:17:48 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-12 15:17:48 +1100 |
commit | d6c05b09dd7cbaba0fcae65929a2c2dd55e9d994 (patch) | |
tree | 1d813f7f0acc543f6da96c429a5cc2fd19b606f1 /cli/lsp/language_server.rs | |
parent | 46da7c6aff3c10af6a067f4229644d2de444a3a7 (diff) |
feat(lsp): add deno cache code actions (#9471)
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 184 |
1 files changed, 123 insertions, 61 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 3f1d5f6c2..7ae4de978 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -65,6 +65,7 @@ pub struct LanguageServer(Arc<tokio::sync::Mutex<Inner>>); pub struct StateSnapshot { pub assets: HashMap<ModuleSpecifier, Option<AssetDocument>>, pub documents: DocumentCache, + pub performance: Performance, pub sources: Sources, } @@ -190,7 +191,7 @@ impl Inner { /// Only searches already cached assets and documents for a line index. If /// the line index cannot be found, `None` is returned. fn get_line_index_sync( - &mut self, + &self, specifier: &ModuleSpecifier, ) -> Option<LineIndex> { let mark = self.performance.mark("get_line_index_sync"); @@ -389,6 +390,7 @@ impl Inner { StateSnapshot { assets: self.assets.clone(), documents: self.documents.clone(), + performance: self.performance.clone(), sources: self.sources.clone(), } } @@ -514,7 +516,7 @@ impl Inner { } pub(crate) fn document_version( - &mut self, + &self, specifier: ModuleSpecifier, ) -> Option<i32> { self.documents.version(&specifier) @@ -851,7 +853,7 @@ impl Inner { } } - async fn hover(&mut self, params: HoverParams) -> LspResult<Option<Hover>> { + async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { if !self.enabled() { return Ok(None); } @@ -912,7 +914,10 @@ impl Inner { } _ => false, }, - // currently only processing `deno-ts` quick fixes + "deno" => match &d.code { + Some(NumberOrString::String(code)) => code == "no-cache", + _ => false, + }, _ => false, }, None => false, @@ -923,53 +928,65 @@ impl Inner { return Ok(None); } let line_index = self.get_line_index_sync(&specifier).unwrap(); + let mut code_actions = CodeActionCollection::default(); let file_diagnostics: Vec<Diagnostic> = self .diagnostics .diagnostics_for(&specifier, &DiagnosticSource::TypeScript) .cloned() .collect(); - let mut code_actions = CodeActionCollection::default(); for diagnostic in &fixable_diagnostics { - let code = match &diagnostic.code.clone().unwrap() { - NumberOrString::String(code) => code.to_string(), - NumberOrString::Number(code) => code.to_string(), - }; - let codes = vec![code]; - let req = tsc::RequestMethod::GetCodeFixes(( - specifier.clone(), - line_index.offset_tsc(diagnostic.range.start)?, - line_index.offset_tsc(diagnostic.range.end)?, - codes, - )); - let res = - self - .ts_server - .request(self.snapshot(), req) - .await - .map_err(|err| { - error!("Error getting actions from TypeScript: {}", err); - LspError::internal_error() - })?; - let actions: Vec<tsc::CodeFixAction> = - from_value(res).map_err(|err| { - error!("Cannot decode actions from TypeScript: {}", err); - LspError::internal_error() - })?; - for action in actions { - code_actions - .add_ts_fix_action(&action, diagnostic, self) - .await - .map_err(|err| { - error!("Unable to convert fix: {}", err); - LspError::internal_error() - })?; - if code_actions.is_fix_all_action( - &action, - diagnostic, - &file_diagnostics, - ) { - code_actions.add_ts_fix_all_action(&action, &specifier, diagnostic); + match diagnostic.source.as_deref() { + Some("deno-ts") => { + let code = match diagnostic.code.as_ref().unwrap() { + NumberOrString::String(code) => code.to_string(), + NumberOrString::Number(code) => code.to_string(), + }; + let codes = vec![code]; + let req = tsc::RequestMethod::GetCodeFixes(( + specifier.clone(), + line_index.offset_tsc(diagnostic.range.start)?, + line_index.offset_tsc(diagnostic.range.end)?, + codes, + )); + let res = + self.ts_server.request(self.snapshot(), req).await.map_err( + |err| { + error!("Error getting actions from TypeScript: {}", err); + LspError::internal_error() + }, + )?; + let actions: Vec<tsc::CodeFixAction> = + from_value(res).map_err(|err| { + error!("Cannot decode actions from TypeScript: {}", err); + LspError::internal_error() + })?; + for action in actions { + code_actions + .add_ts_fix_action(&action, diagnostic, self) + .await + .map_err(|err| { + error!("Unable to convert fix: {}", err); + LspError::internal_error() + })?; + if code_actions.is_fix_all_action( + &action, + diagnostic, + &file_diagnostics, + ) { + code_actions + .add_ts_fix_all_action(&action, &specifier, diagnostic); + } + } } + Some("deno") => { + code_actions + .add_deno_fix_action(diagnostic) + .map_err(|err| { + error!("{}", err); + LspError::internal_error() + })? + } + _ => (), } } code_actions.set_preferred_fixes(); @@ -1020,9 +1037,8 @@ impl Inner { Ok(code_action) } } else { - Err(LspError::invalid_params( - "The CodeAction's data is missing.", - )) + // The code action doesn't need to be resolved + Ok(params) }; self.performance.measure(mark); result @@ -1343,7 +1359,7 @@ impl Inner { } async fn document_highlight( - &mut self, + &self, params: DocumentHighlightParams, ) -> LspResult<Option<Vec<DocumentHighlight>>> { if !self.enabled() { @@ -1481,7 +1497,7 @@ impl Inner { } async fn completion( - &mut self, + &self, params: CompletionParams, ) -> LspResult<Option<CompletionResponse>> { if !self.enabled() { @@ -1830,7 +1846,12 @@ impl lspower::LanguageServer for LanguageServer { #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct CacheParams { - text_document: TextDocumentIdentifier, + /// The document currently open in the editor. If there are no `uris` + /// supplied, the referrer will be cached. + referrer: TextDocumentIdentifier, + /// Any documents that have been specifically asked to be cached via the + /// command. + uris: Vec<TextDocumentIdentifier>, } #[derive(Debug, Deserialize, Serialize)] @@ -1841,19 +1862,38 @@ struct VirtualTextDocumentParams { // These are implementations of custom commands supported by the LSP impl Inner { + /// Similar to `deno cache` on the command line, where modules will be cached + /// in the Deno cache, including any of their dependencies. async fn cache(&mut self, params: CacheParams) -> LspResult<bool> { let mark = self.performance.mark("cache"); - let specifier = utils::normalize_url(params.text_document.uri); - let maybe_import_map = self.maybe_import_map.clone(); - sources::cache(specifier.clone(), maybe_import_map) - .await - .map_err(|err| { - error!("{}", err); - LspError::internal_error() - })?; - if self.documents.contains(&specifier) { - self.diagnostics.invalidate(&specifier); + let referrer = utils::normalize_url(params.referrer.uri); + if !params.uris.is_empty() { + for identifier in ¶ms.uris { + let specifier = utils::normalize_url(identifier.uri.clone()); + sources::cache(&specifier, &self.maybe_import_map) + .await + .map_err(|err| { + error!("{}", err); + LspError::internal_error() + })?; + } + } else { + sources::cache(&referrer, &self.maybe_import_map) + .await + .map_err(|err| { + error!("{}", err); + LspError::internal_error() + })?; } + // now that we have dependencies loaded, we need to re-analyze them and + // invalidate some diagnostics + if self.documents.contains(&referrer) { + if let Some(source) = self.documents.content(&referrer).unwrap() { + self.analyze_dependencies(&referrer, &source); + } + self.diagnostics.invalidate(&referrer); + } + self.prepare_diagnostics().await.map_err(|err| { error!("{}", err); LspError::internal_error() @@ -2685,6 +2725,28 @@ mod tests { harness.run().await; } + #[tokio::test] + async fn test_code_actions_deno_cache() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ("did_open_notification_cache.json", LspResponse::None), + ( + "code_action_request_cache.json", + LspResponse::RequestFixture( + 2, + "code_action_response_cache.json".to_string(), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + #[derive(Deserialize)] struct PerformanceAverages { averages: Vec<PerformanceAverage>, @@ -2730,7 +2792,7 @@ mod tests { LspResponse::RequestAssert(|value| { let resp: PerformanceResponse = serde_json::from_value(value).unwrap(); - assert_eq!(resp.result.averages.len(), 10); + assert_eq!(resp.result.averages.len(), 12); }), ), ( |