From 9c83086d6c28b1751cb6e32b9e9e67f9a533498c Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Mon, 7 Jun 2021 14:24:42 +1000 Subject: [PATCH 1/3] feat(lsp): add test code lens --- cli/lsp/README.md | 27 +- cli/lsp/code_lens.rs | 298 +++++++++++++++++- cli/lsp/config.rs | 49 ++- cli/lsp/language_server.rs | 21 +- cli/tests/integration_tests_lsp.rs | 80 ++++- cli/tests/lsp/code_lens_response_test.json | 162 ++++++++++ .../lsp/did_open_params_test_code_lens.json | 8 + cli/tests/lsp/initialize_params.json | 3 +- ...ialize_params_code_lens_test_disabled.json | 61 ++++ 9 files changed, 691 insertions(+), 18 deletions(-) create mode 100644 cli/tests/lsp/code_lens_response_test.json create mode 100644 cli/tests/lsp/did_open_params_test_code_lens.json create mode 100644 cli/tests/lsp/initialize_params_code_lens_test_disabled.json diff --git a/cli/lsp/README.md b/cli/lsp/README.md index 28ae50c6a69f3b..764998fb1129aa 100644 --- a/cli/lsp/README.md +++ b/cli/lsp/README.md @@ -24,6 +24,7 @@ There are several settings that the language server supports for a workspace: - `deno.codeLens.implementations` - `deno.codeLens.references` - `deno.codeLens.referencesAllFunctions` +- `deno.codeLens.test` - `deno.suggest.completeFunctionCalls` - `deno.suggest.names` - `deno.suggest.paths` @@ -33,10 +34,11 @@ There are several settings that the language server supports for a workspace: - `deno.lint` - `deno.unstable` -There are settings that are support on a per resource basis by the language +There are settings that are supported on a per resource basis by the language server: - `deno.enable` +- `deno.codeLens.test` There are several points in the process where Deno analyzes these settings. First, when the `initialize` request from the client, the @@ -68,7 +70,24 @@ settings. If the client does not have the `workspaceConfiguration` capability, the language server will assume the workspace setting applies to all resources. -## Custom requests +## Commands + +There are several commands that might be issued by the language server to the +client, which the client is expected to implement: + +- `deno.cache` - This is sent as a resolution code action when there is an + un-cached module specifier that is being imported into a module. It will be + sent with and argument that contains the resolved specifier as a string to be + cached. +- `deno.showReferences` - This is sent as the command on some code lenses to + show locations of references. The arguments contain the specifier that is the + subject of the command, the start position of the target and the locations of + the references to show. +- `deno.test` - This is sent as part of a test code lens to, of which the client + is expected to run a test based on the arguments, which are the specifier the + test is contained in and the name of the test to filter the tests on. + +## Requests The LSP currently supports the following custom requests. A client should implement these in order to have a fully functioning client that integrates well @@ -115,9 +134,9 @@ with Deno: } ``` -## Custom notifications +## Notifications -There is currently one custom notification that is send from the server to the +There is currently one custom notification that is sent from the server to the client: - `deno/registryStatus` - when `deno.suggest.imports.autoDiscover` is `true` and diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index 9be5bed2c81f57..3ad644af355339 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -1,5 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use super::analysis; use super::language_server; use super::tsc; @@ -14,7 +15,14 @@ use deno_core::ModuleSpecifier; use lspower::lsp; use regex::Regex; use std::cell::RefCell; +use std::collections::HashSet; use std::rc::Rc; +use swc_common::SourceMap; +use swc_common::Span; +use swc_ecmascript::ast; +use swc_ecmascript::visit::Node; +use swc_ecmascript::visit::Visit; +use swc_ecmascript::visit::VisitWith; lazy_static::lazy_static! { static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap(); @@ -36,6 +44,174 @@ pub struct CodeLensData { pub specifier: ModuleSpecifier, } +fn span_to_range(span: &Span, source_map: Rc) -> lsp::Range { + let start = source_map.lookup_char_pos(span.lo); + let end = source_map.lookup_char_pos(span.hi); + lsp::Range { + start: lsp::Position { + line: (start.line - 1) as u32, + character: start.col_display as u32, + }, + end: lsp::Position { + line: (end.line - 1) as u32, + character: end.col_display as u32, + }, + } +} + +struct DenoTestCollector { + code_lenses: Vec, + source_map: Rc, + specifier: ModuleSpecifier, + test_vars: HashSet, +} + +impl DenoTestCollector { + pub fn new(specifier: ModuleSpecifier, source_map: Rc) -> Self { + Self { + code_lenses: Vec::new(), + source_map, + specifier, + test_vars: HashSet::new(), + } + } + + fn add_code_lens>(&mut self, name: N, span: &Span) { + let range = span_to_range(span, self.source_map.clone()); + self.code_lenses.push(lsp::CodeLens { + range, + command: Some(lsp::Command { + title: "▶\u{fe0e} Run Test".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![json!(self.specifier), json!(name.as_ref())]), + }), + data: None, + }); + } + + fn check_call_expr(&mut self, node: &ast::CallExpr, span: &Span) { + if let Some(expr) = node.args.get(0).map(|es| es.expr.as_ref()) { + match expr { + ast::Expr::Object(obj_lit) => { + for prop in &obj_lit.props { + if let ast::PropOrSpread::Prop(prop) = prop { + if let ast::Prop::KeyValue(key_value_prop) = prop.as_ref() { + if let ast::PropName::Ident(ident) = &key_value_prop.key { + if ident.sym.to_string() == "name" { + if let ast::Expr::Lit(ast::Lit::Str(lit_str)) = + key_value_prop.value.as_ref() + { + let name = lit_str.value.to_string(); + self.add_code_lens(name, &span); + } + } + } + } + } + } + } + ast::Expr::Lit(ast::Lit::Str(lit_str)) => { + let name = lit_str.value.to_string(); + self.add_code_lens(name, &span); + } + _ => (), + } + } + } + + /// Move out the code lenses from the collector. + fn take(self) -> Vec { + self.code_lenses + } +} + +impl Visit for DenoTestCollector { + fn visit_call_expr(&mut self, node: &ast::CallExpr, _parent: &dyn Node) { + if let ast::ExprOrSuper::Expr(callee_expr) = &node.callee { + match callee_expr.as_ref() { + ast::Expr::Ident(ident) => { + if self.test_vars.contains(&ident.sym.to_string()) { + self.check_call_expr(node, &ident.span); + } + } + ast::Expr::Member(member_expr) => { + if let ast::Expr::Ident(ns_prop_ident) = member_expr.prop.as_ref() { + if ns_prop_ident.sym.to_string() == "test" { + if let ast::ExprOrSuper::Expr(obj_expr) = &member_expr.obj { + if let ast::Expr::Ident(ident) = obj_expr.as_ref() { + if ident.sym.to_string() == "Deno" { + self.check_call_expr(node, &ns_prop_ident.span); + } + } + } + } + } + } + _ => (), + } + } + } + + fn visit_var_decl(&mut self, node: &ast::VarDecl, _parent: &dyn Node) { + for decl in &node.decls { + if let Some(init) = &decl.init { + match init.as_ref() { + // Identify destructured assignments of `test` from `Deno` + ast::Expr::Ident(ident) => { + if ident.sym.to_string() == "Deno" { + if let ast::Pat::Object(object_pat) = &decl.name { + for prop in &object_pat.props { + match prop { + ast::ObjectPatProp::Assign(prop) => { + let name = prop.key.sym.to_string(); + if name == "test" { + self.test_vars.insert(name); + } + } + ast::ObjectPatProp::KeyValue(prop) => { + if let ast::PropName::Ident(key_ident) = &prop.key { + if key_ident.sym.to_string() == "test" { + if let ast::Pat::Ident(value_ident) = + &prop.value.as_ref() + { + self + .test_vars + .insert(value_ident.id.sym.to_string()); + } + } + } + } + _ => (), + } + } + } + } + } + // Identify variable assignments where the init is `Deno.test` + ast::Expr::Member(member_expr) => { + if let ast::ExprOrSuper::Expr(expr) = &member_expr.obj { + if let ast::Expr::Ident(obj_ident) = expr.as_ref() { + if obj_ident.sym.to_string() == "Deno" { + if let ast::Expr::Ident(prop_ident) = + &member_expr.prop.as_ref() + { + if prop_ident.sym.to_string() == "test" { + if let ast::Pat::Ident(binding_ident) = &decl.name { + self.test_vars.insert(binding_ident.id.sym.to_string()); + } + } + } + } + } + } + } + _ => (), + } + } + } + } +} + async fn resolve_implementation_code_lens( code_lens: lsp::CodeLens, data: CodeLensData, @@ -189,8 +365,51 @@ pub(crate) async fn resolve_code_lens( } } +pub(crate) async fn collect( + specifier: &ModuleSpecifier, + language_server: &mut language_server::Inner, +) -> Result, AnyError> { + let mut code_lenses = collect_test(specifier, language_server)?; + code_lenses.extend(collect_tsc(specifier, language_server).await?); + + Ok(code_lenses) +} + +fn collect_test( + specifier: &ModuleSpecifier, + language_server: &mut language_server::Inner, +) -> Result, AnyError> { + if language_server.config.specifier_code_lens_test(specifier) { + let source = language_server + .get_text_content(specifier) + .ok_or_else(|| anyhow!("Missing text content: {}", specifier))?; + let media_type = language_server + .get_media_type(specifier) + .ok_or_else(|| anyhow!("Missing media type: {}", specifier))?; + // we swallow parsed errors, as they are meaningless here. + // TODO(@kitsonk) consider caching previous code_lens results to return if + // there is a parse error to avoid issues of lenses popping in and out + if let Ok(parsed_module) = + analysis::parse_module(specifier, &source, &media_type) + { + let mut collector = DenoTestCollector::new( + specifier.clone(), + parsed_module.source_map.clone(), + ); + parsed_module.module.visit_with( + &ast::Invalid { + span: swc_common::DUMMY_SP, + }, + &mut collector, + ); + return Ok(collector.take()); + } + } + Ok(Vec::new()) +} + /// Return tsc navigation tree code lenses. -pub(crate) async fn tsc_code_lenses( +async fn collect_tsc( specifier: &ModuleSpecifier, language_server: &mut language_server::Inner, ) -> Result, AnyError> { @@ -282,3 +501,80 @@ pub(crate) async fn tsc_code_lenses( }); Ok(Rc::try_unwrap(code_lenses).unwrap().into_inner()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::media_type::MediaType; + + #[test] + fn test_deno_test_collector() { + let specifier = resolve_url("https://deno.land/x/mod.ts").unwrap(); + let source = r#" + Deno.test({ + name: "test a", + fn() {} + }); + + Deno.test("test b", function anotherTest() {}); + "#; + let parsed_module = + analysis::parse_module(&specifier, source, &MediaType::TypeScript) + .unwrap(); + let mut collector = + DenoTestCollector::new(specifier, parsed_module.source_map.clone()); + parsed_module.module.visit_with( + &ast::Invalid { + span: swc_common::DUMMY_SP, + }, + &mut collector, + ); + assert_eq!( + collector.take(), + vec![ + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 1, + character: 11 + }, + end: lsp::Position { + line: 1, + character: 15 + } + }, + command: Some(lsp::Command { + title: "▶\u{fe0e} Run Test".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test a"), + ]) + }), + data: None, + }, + lsp::CodeLens { + range: lsp::Range { + start: lsp::Position { + line: 6, + character: 11 + }, + end: lsp::Position { + line: 6, + character: 15 + } + }, + command: Some(lsp::Command { + title: "▶\u{fe0e} Run Test".to_string(), + command: "deno.test".to_string(), + arguments: Some(vec![ + json!("https://deno.land/x/mod.ts"), + json!("test b"), + ]) + }), + data: None, + } + ] + ); + } +} diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index e46e8dd5272859..554bfd1e9fb3ad 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -28,6 +28,10 @@ pub struct ClientCapabilities { pub line_folding_only: bool, } +fn is_true() -> bool { + true +} + #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct CodeLensSettings { @@ -41,6 +45,10 @@ pub struct CodeLensSettings { /// an impact, the `references` flag needs to be `true`. #[serde(default)] pub references_all_functions: bool, + /// Flag for providing test code lens on `Deno.test` statements. There is + /// also the `test_args` setting, but this is not used by the server. + #[serde(default = "is_true")] + pub test: bool, } impl Default for CodeLensSettings { @@ -49,12 +57,24 @@ impl Default for CodeLensSettings { implementations: false, references: false, references_all_functions: false, + test: true, } } } -fn is_true() -> bool { - true +#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct CodeLensSpecifierSettings { + /// Flag for providing test code lens on `Deno.test` statements. There is + /// also the `test_args` setting, but this is not used by the server. + #[serde(default = "is_true")] + pub test: bool, +} + +impl Default for CodeLensSpecifierSettings { + fn default() -> Self { + Self { test: true } + } } #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] @@ -109,9 +129,13 @@ impl Default for ImportCompletionSettings { /// Deno language server specific settings that can be applied uniquely to a /// specifier. #[derive(Debug, Default, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct SpecifierSettings { /// A flag that indicates if Deno is enabled for this specifier or not. pub enable: bool, + /// Code lens specific settings for the resource. + #[serde(default)] + pub code_lens: CodeLensSpecifierSettings, } /// Deno language server specific settings that are applied to a workspace. @@ -324,11 +348,21 @@ impl Config { pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool { let settings = self.settings.read().unwrap(); - if let Some(specifier_settings) = settings.specifiers.get(specifier) { - specifier_settings.1.enable - } else { - settings.workspace.enable - } + settings + .specifiers + .get(specifier) + .map(|(_, s)| s.enable) + .unwrap_or_else(|| settings.workspace.enable) + } + + pub fn specifier_code_lens_test(&self, specifier: &ModuleSpecifier) -> bool { + let settings = self.settings.read().unwrap(); + let value = settings + .specifiers + .get(specifier) + .map(|(_, s)| s.code_lens.test) + .unwrap_or_else(|| settings.workspace.code_lens.test); + value } #[allow(clippy::redundant_closure_call)] @@ -449,6 +483,7 @@ mod tests { implementations: false, references: false, references_all_functions: false, + test: true, }, internal_debug: false, lint: false, diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 9e4e6af149a647..7525551ea2b018 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -243,7 +243,10 @@ impl Inner { // moment /// Searches already cached assets and documents and returns its text /// content. If not found, `None` is returned. - fn get_text_content(&self, specifier: &ModuleSpecifier) -> Option { + pub(crate) fn get_text_content( + &self, + specifier: &ModuleSpecifier, + ) -> Option { if specifier.scheme() == "asset" { self .assets @@ -256,6 +259,17 @@ impl Inner { } } + pub(crate) fn get_media_type( + &self, + specifier: &ModuleSpecifier, + ) -> Option { + if specifier.scheme() == "asset" || self.documents.contains_key(specifier) { + Some(MediaType::from(specifier)) + } else { + self.sources.get_media_type(specifier) + } + } + pub(crate) async fn get_navigation_tree( &mut self, specifier: &ModuleSpecifier, @@ -1105,9 +1119,8 @@ impl Inner { } let mark = self.performance.mark("code_lens", Some(¶ms)); - let code_lenses = code_lens::tsc_code_lenses(&specifier, self) - .await - .map_err(|err| { + let code_lenses = + code_lens::collect(&specifier, self).await.map_err(|err| { error!("Error getting code lenses for \"{}\": {}", specifier, err); LspError::internal_error() })?; diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index b6d9cde54737c0..20fc816033f44a 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -45,7 +45,15 @@ where let (id, method, _) = client.read_request::().unwrap(); assert_eq!(method, "workspace/configuration"); client - .write_response(id, json!({ "enable": true })) + .write_response( + id, + json!({ + "enable": true, + "codeLens": { + "test": true + } + }), + ) .unwrap(); let mut diagnostics = vec![]; @@ -1229,6 +1237,76 @@ fn lsp_code_lens_impl() { shutdown(&mut client); } +#[test] +fn lsp_code_lens_test() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + load_fixture("did_open_params_test_code_lens.json"), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeLens", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_lens_response_test.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_lens_test_disabled() { + let mut client = init("initialize_params_code_lens_test_disabled.json"); + client + .write_notification( + "textDocument/didOpen", + load_fixture("did_open_params_test_code_lens.json"), + ) + .unwrap(); + + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response( + id, + json!({ + "enable": true, + "codeLens": { + "test": false + } + }), + ) + .unwrap(); + + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeLens", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!(maybe_res, Some(json!([]))); + shutdown(&mut client); +} + #[test] fn lsp_code_lens_non_doc_nav_tree() { let mut client = init("initialize_params.json"); diff --git a/cli/tests/lsp/code_lens_response_test.json b/cli/tests/lsp/code_lens_response_test.json new file mode 100644 index 00000000000000..b2cb4588a73b5c --- /dev/null +++ b/cli/tests/lsp/code_lens_response_test.json @@ -0,0 +1,162 @@ +[ + { + "range": { + "start": { + "line": 4, + "character": 5 + }, + "end": { + "line": 4, + "character": 9 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test a" + ] + } + }, + { + "range": { + "start": { + "line": 5, + "character": 5 + }, + "end": { + "line": 5, + "character": 9 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test b" + ] + } + }, + { + "range": { + "start": { + "line": 9, + "character": 0 + }, + "end": { + "line": 9, + "character": 4 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test c" + ] + } + }, + { + "range": { + "start": { + "line": 13, + "character": 0 + }, + "end": { + "line": 13, + "character": 4 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test d" + ] + } + }, + { + "range": { + "start": { + "line": 14, + "character": 0 + }, + "end": { + "line": 14, + "character": 5 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test e" + ] + } + }, + { + "range": { + "start": { + "line": 18, + "character": 0 + }, + "end": { + "line": 18, + "character": 5 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test f" + ] + } + }, + { + "range": { + "start": { + "line": 19, + "character": 0 + }, + "end": { + "line": 19, + "character": 5 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test g" + ] + } + }, + { + "range": { + "start": { + "line": 23, + "character": 0 + }, + "end": { + "line": 23, + "character": 5 + } + }, + "command": { + "title": "▶︎ Run Test", + "command": "deno.test", + "arguments": [ + "file:///a/file.ts", + "test h" + ] + } + } +] diff --git a/cli/tests/lsp/did_open_params_test_code_lens.json b/cli/tests/lsp/did_open_params_test_code_lens.json new file mode 100644 index 00000000000000..dcb9e11f30af42 --- /dev/null +++ b/cli/tests/lsp/did_open_params_test_code_lens.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n" + } +} diff --git a/cli/tests/lsp/initialize_params.json b/cli/tests/lsp/initialize_params.json index 98ec53aa6f494a..01b334cd5c0a04 100644 --- a/cli/tests/lsp/initialize_params.json +++ b/cli/tests/lsp/initialize_params.json @@ -9,7 +9,8 @@ "enable": true, "codeLens": { "implementations": true, - "references": true + "references": true, + "test": true }, "importMap": null, "lint": true, diff --git a/cli/tests/lsp/initialize_params_code_lens_test_disabled.json b/cli/tests/lsp/initialize_params_code_lens_test_disabled.json new file mode 100644 index 00000000000000..febec5695bd512 --- /dev/null +++ b/cli/tests/lsp/initialize_params_code_lens_test_disabled.json @@ -0,0 +1,61 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "codeLens": { + "implementations": true, + "references": true, + "test": false + }, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": {} + } + }, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true + } + } +} From b39497aff6f2d8d6ce5192995cf0e208f9da2454 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Mon, 7 Jun 2021 15:12:55 +1000 Subject: [PATCH 2/3] fix logic error in enablement --- cli/lsp/code_lens.rs | 1 + cli/lsp/language_server.rs | 3 +- cli/tests/integration_tests_lsp.rs | 2 +- .../lsp/initialize_params_code_lens_test.json | 56 +++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 cli/tests/lsp/initialize_params_code_lens_test.json diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index 3ad644af355339..eade345e53bfd8 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -369,6 +369,7 @@ pub(crate) async fn collect( specifier: &ModuleSpecifier, language_server: &mut language_server::Inner, ) -> Result, AnyError> { + log::info!("collect: {}", specifier); let mut code_lenses = collect_test(specifier, language_server)?; code_lenses.extend(collect_tsc(specifier, language_server).await?); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 7525551ea2b018..cd952b548bdd73 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1113,7 +1113,8 @@ impl Inner { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); if !self.documents.is_diagnosable(&specifier) || !self.config.specifier_enabled(&specifier) - || !self.config.get_workspace_settings().enabled_code_lens() + || !(self.config.get_workspace_settings().enabled_code_lens() + || self.config.specifier_code_lens_test(&specifier)) { return Ok(None); } diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index 20fc816033f44a..a445a6b4ea974c 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1239,7 +1239,7 @@ fn lsp_code_lens_impl() { #[test] fn lsp_code_lens_test() { - let mut client = init("initialize_params.json"); + let mut client = init("initialize_params_code_lens_test.json"); did_open( &mut client, load_fixture("did_open_params_test_code_lens.json"), diff --git a/cli/tests/lsp/initialize_params_code_lens_test.json b/cli/tests/lsp/initialize_params_code_lens_test.json new file mode 100644 index 00000000000000..792928462e1991 --- /dev/null +++ b/cli/tests/lsp/initialize_params_code_lens_test.json @@ -0,0 +1,56 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": {} + } + }, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true + } + } +} From fb7faccbcfbbcde07cb1d4dcc16ca26de837b1c1 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Mon, 7 Jun 2021 15:14:47 +1000 Subject: [PATCH 3/3] remove debug logging --- cli/lsp/code_lens.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index eade345e53bfd8..3ad644af355339 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -369,7 +369,6 @@ pub(crate) async fn collect( specifier: &ModuleSpecifier, language_server: &mut language_server::Inner, ) -> Result, AnyError> { - log::info!("collect: {}", specifier); let mut code_lenses = collect_test(specifier, language_server)?; code_lenses.extend(collect_tsc(specifier, language_server).await?);