diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/lsp/analysis.rs | 15 | ||||
-rw-r--r-- | cli/lsp/capabilities.rs | 5 | ||||
-rw-r--r-- | cli/lsp/config.rs | 46 | ||||
-rw-r--r-- | cli/lsp/documents.rs | 27 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 360 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 169 | ||||
-rw-r--r-- | cli/tests/lsp/code_lens_request.json | 10 | ||||
-rw-r--r-- | cli/tests/lsp/code_lens_resolve_request.json | 21 | ||||
-rw-r--r-- | cli/tests/lsp/did_open_notification_cl_references.json | 12 | ||||
-rw-r--r-- | cli/tests/lsp/initialize_request.json | 3 | ||||
-rw-r--r-- | cli/tsc/99_main_compiler.js | 72 | ||||
-rw-r--r-- | cli/tsc/compiler.d.ts | 58 |
12 files changed, 680 insertions, 118 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 12f738382..95c0e95ff 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -9,6 +9,8 @@ use crate::module_graph::TypeScriptReference; use crate::tools::lint::create_linter; use deno_core::error::AnyError; +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; use deno_core::ModuleSpecifier; use deno_lint::rules; use lspower::lsp; @@ -249,6 +251,19 @@ pub fn analyze_dependencies( } } +#[derive(Debug, Deserialize, Serialize)] +pub enum CodeLensSource { + #[serde(rename = "references")] + References, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CodeLensData { + pub source: CodeLensSource, + pub specifier: ModuleSpecifier, +} + #[cfg(test)] mod tests { use super::*; diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs index c83b369ce..6e8082ee8 100644 --- a/cli/lsp/capabilities.rs +++ b/cli/lsp/capabilities.rs @@ -6,6 +6,7 @@ ///! client. ///! use lspower::lsp::ClientCapabilities; +use lspower::lsp::CodeLensOptions; use lspower::lsp::CompletionOptions; use lspower::lsp::HoverProviderCapability; use lspower::lsp::ImplementationProviderCapability; @@ -59,7 +60,9 @@ pub fn server_capabilities( document_symbol_provider: None, workspace_symbol_provider: None, code_action_provider: None, - code_lens_provider: None, + code_lens_provider: Some(CodeLensOptions { + resolve_provider: Some(true), + }), document_formatting_provider: Some(OneOf::Left(true)), document_range_formatting_provider: None, document_on_type_formatting_provider: None, diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index c7cbaa992..47285a01e 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -15,12 +15,25 @@ pub struct ClientCapabilities { pub workspace_did_change_watched_files: bool, } -#[derive(Debug, Clone, Default, Deserialize)] +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CodeLensSettings { + /// Flag for providing reference code lens. + #[serde(default)] + pub references: bool, + /// Flag for providing reference code lens on all functions. For this to have + /// an impact, the `references` flag needs to be `true`. + #[serde(default)] + pub references_all_functions: bool, +} + +#[derive(Debug, Default, Deserialize)] #[serde(rename_all = "camelCase")] pub struct WorkspaceSettings { pub enable: bool, pub config: Option<String>, pub import_map: Option<String>, + pub code_lens: Option<CodeLensSettings>, #[serde(default)] pub lint: bool, @@ -28,7 +41,36 @@ pub struct WorkspaceSettings { pub unstable: bool, } -#[derive(Debug, Clone, Default)] +impl WorkspaceSettings { + /// Determine if any code lenses are enabled at all. This allows short + /// circuiting when there are no code lenses enabled. + pub fn enabled_code_lens(&self) -> bool { + if let Some(code_lens) = &self.code_lens { + // This should contain all the "top level" code lens references + code_lens.references + } else { + false + } + } + + pub fn enabled_code_lens_references(&self) -> bool { + if let Some(code_lens) = &self.code_lens { + code_lens.references + } else { + false + } + } + + pub fn enabled_code_lens_references_all_functions(&self) -> bool { + if let Some(code_lens) = &self.code_lens { + code_lens.references_all_functions + } else { + false + } + } +} + +#[derive(Debug, Default)] pub struct Config { pub client_capabilities: ClientCapabilities, pub root_uri: Option<Url>, diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 955ca1c78..4cda1f048 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -2,6 +2,7 @@ use super::analysis; use super::text::LineIndex; +use super::tsc::NavigationTree; use crate::import_map::ImportMap; use crate::media_type::MediaType; @@ -33,6 +34,7 @@ impl IndexValid { pub struct DocumentData { bytes: Option<Vec<u8>>, line_index: Option<LineIndex>, + navigation_tree: Option<NavigationTree>, dependencies: Option<HashMap<String, analysis::Dependency>>, version: Option<i32>, } @@ -72,6 +74,7 @@ impl DocumentData { } else { Some(LineIndex::new(&content)) }; + self.navigation_tree = None; Ok(()) } @@ -187,6 +190,14 @@ impl DocumentCache { doc.line_index.clone() } + pub fn navigation_tree( + &self, + specifier: &ModuleSpecifier, + ) -> Option<NavigationTree> { + let doc = self.docs.get(specifier)?; + doc.navigation_tree.clone() + } + pub fn open( &mut self, specifier: ModuleSpecifier, @@ -218,6 +229,22 @@ impl DocumentCache { .collect() } + pub fn set_navigation_tree( + &mut self, + specifier: &ModuleSpecifier, + navigation_tree: NavigationTree, + ) -> Result<(), AnyError> { + if let Some(mut doc) = self.docs.get_mut(specifier) { + doc.navigation_tree = Some(navigation_tree); + Ok(()) + } else { + Err(custom_error( + "NotFound", + "The document \"{}\" was unexpectedly missing.", + )) + } + } + pub fn version(&self, specifier: &ModuleSpecifier) -> Option<i32> { self.docs.get(specifier).and_then(|doc| doc.version) } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 087b11436..52022632c 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1,6 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use deno_core::error::anyhow; +use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; @@ -14,9 +15,12 @@ use lspower::jsonrpc::Result as LspResult; use lspower::lsp::request::*; use lspower::lsp::*; use lspower::Client; +use regex::Regex; +use std::cell::RefCell; use std::collections::HashMap; use std::env; use std::path::PathBuf; +use std::rc::Rc; use std::sync::Arc; use tokio::fs; @@ -25,6 +29,8 @@ use crate::import_map::ImportMap; use crate::tsc_config::parse_config; use crate::tsc_config::TsConfig; +use super::analysis::CodeLensData; +use super::analysis::CodeLensSource; use super::capabilities; use super::config::Config; use super::diagnostics; @@ -41,6 +47,10 @@ use super::tsc::AssetDocument; use super::tsc::TsServer; use super::utils; +lazy_static! { + static ref EXPORT_MODIFIER: Regex = Regex::new(r"\bexport\b").unwrap(); +} + #[derive(Debug, Clone)] pub struct LanguageServer(Arc<tokio::sync::Mutex<Inner>>); @@ -162,6 +172,37 @@ impl Inner { maybe_line_index } + async fn get_navigation_tree( + &mut self, + specifier: &ModuleSpecifier, + ) -> Result<tsc::NavigationTree, AnyError> { + if self.documents.contains(specifier) { + if let Some(navigation_tree) = self.documents.navigation_tree(specifier) { + Ok(navigation_tree) + } else { + let res = self + .ts_server + .request( + self.snapshot(), + tsc::RequestMethod::GetNavigationTree(specifier.clone()), + ) + .await + .unwrap(); + let navigation_tree: tsc::NavigationTree = + serde_json::from_value(res).unwrap(); + self + .documents + .set_navigation_tree(specifier, navigation_tree.clone())?; + Ok(navigation_tree) + } + } else { + Err(custom_error( + "NotFound", + format!("The document \"{}\" was unexpectedly not found.", specifier), + )) + } + } + async fn prepare_diagnostics(&mut self) -> Result<(), AnyError> { let (enabled, lint_enabled) = { let config = &self.config; @@ -271,14 +312,13 @@ impl Inner { (maybe_changes, diagnostics_collection.clone()) }; if let Some(diagnostic_changes) = maybe_changes { - let settings = self.config.settings.clone(); for specifier in diagnostic_changes { // TODO(@kitsonk) not totally happy with the way we collect and store // different types of diagnostics and offer them up to the client, we // do need to send "empty" vectors though when a particular feature is // disabled, otherwise the client will not clear down previous // diagnostics - let mut diagnostics: Vec<Diagnostic> = if settings.lint { + let mut diagnostics: Vec<Diagnostic> = if self.config.settings.lint { diagnostics_collection .diagnostics_for(&specifier, &DiagnosticSource::Lint) .cloned() @@ -778,6 +818,209 @@ impl Inner { } } + async fn code_lens( + &mut self, + params: CodeLensParams, + ) -> LspResult<Option<Vec<CodeLens>>> { + if !self.enabled() || !self.config.settings.enabled_code_lens() { + return Ok(None); + } + + let mark = self.performance.mark("code_lens"); + let specifier = utils::normalize_url(params.text_document.uri); + let line_index = self.get_line_index_sync(&specifier).unwrap(); + let navigation_tree = + self.get_navigation_tree(&specifier).await.map_err(|err| { + error!("Failed to retrieve nav tree: {:#?}", err); + LspError::invalid_request() + })?; + + // because we have to use this as a mutable in a closure, the compiler + // can't be sure when the vector will be mutated, and so a RefCell is + // required to "protect" the vector. + let cl = Rc::new(RefCell::new(Vec::new())); + navigation_tree.walk(&|i, mp| { + let mut code_lenses = cl.borrow_mut(); + + // TSC References Code Lens + if self.config.settings.enabled_code_lens_references() { + let source = CodeLensSource::References; + if let Some(parent) = &mp { + if parent.kind == tsc::ScriptElementKind::EnumElement { + code_lenses.push(i.to_code_lens(&line_index, &specifier, &source)); + } + } + match i.kind { + tsc::ScriptElementKind::FunctionElement => { + if self + .config + .settings + .enabled_code_lens_references_all_functions() + { + code_lenses.push(i.to_code_lens( + &line_index, + &specifier, + &source, + )); + } + } + tsc::ScriptElementKind::ConstElement + | tsc::ScriptElementKind::LetElement + | tsc::ScriptElementKind::VariableElement => { + if EXPORT_MODIFIER.is_match(&i.kind_modifiers) { + code_lenses.push(i.to_code_lens( + &line_index, + &specifier, + &source, + )); + } + } + tsc::ScriptElementKind::ClassElement => { + if i.text != "<class>" { + code_lenses.push(i.to_code_lens( + &line_index, + &specifier, + &source, + )); + } + } + tsc::ScriptElementKind::InterfaceElement + | tsc::ScriptElementKind::TypeElement + | tsc::ScriptElementKind::EnumElement => { + code_lenses.push(i.to_code_lens(&line_index, &specifier, &source)); + } + tsc::ScriptElementKind::LocalFunctionElement + | tsc::ScriptElementKind::MemberGetAccessorElement + | tsc::ScriptElementKind::MemberSetAccessorElement + | tsc::ScriptElementKind::ConstructorImplementationElement + | tsc::ScriptElementKind::MemberVariableElement => { + if let Some(parent) = &mp { + if parent.spans[0].start != i.spans[0].start { + match parent.kind { + tsc::ScriptElementKind::ClassElement + | tsc::ScriptElementKind::InterfaceElement + | tsc::ScriptElementKind::TypeElement => { + code_lenses.push(i.to_code_lens( + &line_index, + &specifier, + &source, + )); + } + _ => (), + } + } + } + } + _ => (), + } + } + }); + + self.performance.measure(mark); + Ok(Some(Rc::try_unwrap(cl).unwrap().into_inner())) + } + + async fn code_lens_resolve( + &mut self, + params: CodeLens, + ) -> LspResult<CodeLens> { + let mark = self.performance.mark("code_lens_resolve"); + if let Some(data) = params.data.clone() { + let code_lens_data: CodeLensData = serde_json::from_value(data) + .map_err(|err| LspError::invalid_params(err.to_string()))?; + let code_lens = match code_lens_data.source { + CodeLensSource::References => { + let line_index = + self.get_line_index_sync(&code_lens_data.specifier).unwrap(); + let req = tsc::RequestMethod::GetReferences(( + code_lens_data.specifier.clone(), + line_index.offset_tsc(params.range.start)?, + )); + let res = + self.ts_server.request(self.snapshot(), req).await.map_err( + |err| { + error!("Error processing TypeScript request: {}", err); + LspError::internal_error() + }, + )?; + let maybe_references: Option<Vec<tsc::ReferenceEntry>> = + serde_json::from_value(res).map_err(|err| { + error!("Error deserializing response: {}", err); + LspError::internal_error() + })?; + if let Some(references) = maybe_references { + let mut locations = Vec::new(); + for reference in references { + if reference.is_definition { + continue; + } + let reference_specifier = ModuleSpecifier::resolve_url( + &reference.document_span.file_name, + ) + .map_err(|err| { + error!("Invalid specifier returned from TypeScript: {}", err); + LspError::internal_error() + })?; + let line_index = self + .get_line_index(reference_specifier) + .await + .map_err(|err| { + error!("Unable to get line index: {}", err); + LspError::internal_error() + })?; + locations.push(reference.to_location(&line_index)); + } + let command = if !locations.is_empty() { + let title = if locations.len() > 1 { + format!("{} references", locations.len()) + } else { + "1 reference".to_string() + }; + Command { + title, + command: "deno.showReferences".to_string(), + arguments: Some(vec![ + serde_json::to_value(code_lens_data.specifier).unwrap(), + serde_json::to_value(params.range.start).unwrap(), + serde_json::to_value(locations).unwrap(), + ]), + } + } else { + Command { + title: "0 references".to_string(), + command: "".to_string(), + arguments: None, + } + }; + CodeLens { + range: params.range, + command: Some(command), + data: None, + } + } else { + let command = Command { + title: "0 references".to_string(), + command: "".to_string(), + arguments: None, + }; + CodeLens { + range: params.range, + command: Some(command), + data: None, + } + } + } + }; + self.performance.measure(mark); + Ok(code_lens) + } else { + self.performance.measure(mark); + Err(LspError::invalid_params( + "Code lens is missing the \"data\" property.", + )) + } + } + async fn document_highlight( &self, params: DocumentHighlightParams, @@ -1195,6 +1438,17 @@ impl lspower::LanguageServer for LanguageServer { self.0.lock().await.hover(params).await } + async fn code_lens( + &self, + params: CodeLensParams, + ) -> LspResult<Option<Vec<CodeLens>>> { + self.0.lock().await.code_lens(params).await + } + + async fn code_lens_resolve(&self, params: CodeLens) -> LspResult<CodeLens> { + self.0.lock().await.code_lens_resolve(params).await + } + async fn document_highlight( &self, params: DocumentHighlightParams, @@ -1765,6 +2019,108 @@ mod tests { harness.run().await; } + #[tokio::test] + async fn test_code_lens_request() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ( + "did_open_notification_cl_references.json", + LspResponse::None, + ), + ( + "code_lens_request.json", + LspResponse::Request( + 2, + json!([ + { + "range": { + "start": { + "line": 0, + "character": 6, + }, + "end": { + "line": 0, + "character": 7, + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references", + }, + }, + { + "range": { + "start": { + "line": 1, + "character": 2, + }, + "end": { + "line": 1, + "character": 3, + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references", + } + } + ]), + ), + ), + ( + "code_lens_resolve_request.json", + LspResponse::Request( + 4, + json!({ + "range": { + "start": { + "line": 0, + "character": 6, + }, + "end": { + "line": 0, + "character": 7, + } + }, + "command": { + "title": "1 reference", + "command": "deno.showReferences", + "arguments": [ + "file:///a/file.ts", + { + "line": 0, + "character": 6, + }, + [ + { + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 12, + "character": 14, + }, + "end": { + "line": 12, + "character": 15, + } + } + } + ], + ] + } + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + #[derive(Deserialize)] struct PerformanceAverages { averages: Vec<PerformanceAverage>, diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index ce9f31e68..3fee900c6 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1,5 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use super::analysis::CodeLensSource; use super::analysis::ResolvedDependency; use super::language_server::StateSnapshot; use super::text; @@ -241,7 +242,7 @@ fn replace_links(text: &str) -> String { .to_string() } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] pub enum ScriptElementKind { #[serde(rename = "")] Unknown, @@ -356,8 +357,8 @@ impl From<ScriptElementKind> for lsp::CompletionItemKind { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TextSpan { - start: u32, - length: u32, + pub start: u32, + pub length: u32, } impl TextSpan { @@ -480,6 +481,59 @@ impl DocumentSpan { } } +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NavigationTree { + pub text: String, + pub kind: ScriptElementKind, + pub kind_modifiers: String, + pub spans: Vec<TextSpan>, + pub name_span: Option<TextSpan>, + pub child_items: Option<Vec<NavigationTree>>, +} + +impl NavigationTree { + pub fn to_code_lens( + &self, + line_index: &LineIndex, + specifier: &ModuleSpecifier, + source: &CodeLensSource, + ) -> lsp::CodeLens { + lsp::CodeLens { + range: self.name_span.clone().unwrap().to_range(line_index), + command: None, + data: Some(json!({ + "specifier": specifier, + "source": source + })), + } + } + + pub fn walk<F>(&self, callback: &F) + where + F: Fn(&NavigationTree, Option<&NavigationTree>), + { + callback(self, None); + if let Some(child_items) = &self.child_items { + for child in child_items { + child.walk_child(callback, self); + } + } + } + + fn walk_child<F>(&self, callback: &F, parent: &NavigationTree) + where + F: Fn(&NavigationTree, Option<&NavigationTree>), + { + callback(self, Some(parent)); + if let Some(child_items) = &self.child_items { + for child in child_items { + child.walk_child(callback, self); + } + } + } +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ImplementationLocation { @@ -1157,24 +1211,26 @@ pub struct UserPreferences { pub enum RequestMethod { /// Configure the compilation settings for the server. Configure(TsConfig), + /// Get rename locations at a given position. + FindRenameLocations((ModuleSpecifier, u32, bool, bool, bool)), /// Retrieve the text of an assets that exists in memory in the isolate. GetAsset(ModuleSpecifier), + /// Get completion information at a given position (IntelliSense). + GetCompletions((ModuleSpecifier, u32, UserPreferences)), + /// Get declaration information for a specific position. + GetDefinition((ModuleSpecifier, u32)), /// Return diagnostics for given file. GetDiagnostics(Vec<ModuleSpecifier>), - /// Return quick info at position (hover information). - GetQuickInfo((ModuleSpecifier, u32)), /// Return document highlights at position. GetDocumentHighlights((ModuleSpecifier, u32, Vec<ModuleSpecifier>)), - /// Get document references for a specific position. - GetReferences((ModuleSpecifier, u32)), - /// Get declaration information for a specific position. - GetDefinition((ModuleSpecifier, u32)), - /// Get completion information at a given position (IntelliSense). - GetCompletions((ModuleSpecifier, u32, UserPreferences)), /// Get implementation information for a specific position. GetImplementation((ModuleSpecifier, u32)), - /// Get rename locations at a given position. - FindRenameLocations((ModuleSpecifier, u32, bool, bool, bool)), + /// Get a "navigation tree" for a specifier. + GetNavigationTree(ModuleSpecifier), + /// Return quick info at position (hover information). + GetQuickInfo((ModuleSpecifier, u32)), + /// Get document references for a specific position. + GetReferences((ModuleSpecifier, u32)), } impl RequestMethod { @@ -1185,22 +1241,48 @@ impl RequestMethod { "method": "configure", "compilerOptions": config, }), + RequestMethod::FindRenameLocations(( + specifier, + position, + find_in_strings, + find_in_comments, + provide_prefix_and_suffix_text_for_rename, + )) => { + json!({ + "id": id, + "method": "findRenameLocations", + "specifier": specifier, + "position": position, + "findInStrings": find_in_strings, + "findInComments": find_in_comments, + "providePrefixAndSuffixTextForRename": provide_prefix_and_suffix_text_for_rename + }) + } RequestMethod::GetAsset(specifier) => json!({ "id": id, "method": "getAsset", "specifier": specifier, }), + RequestMethod::GetCompletions((specifier, position, preferences)) => { + json!({ + "id": id, + "method": "getCompletions", + "specifier": specifier, + "position": position, + "preferences": preferences, + }) + } + RequestMethod::GetDefinition((specifier, position)) => json!({ + "id": id, + "method": "getDefinition", + "specifier": specifier, + "position": position, + }), RequestMethod::GetDiagnostics(specifiers) => json!({ "id": id, "method": "getDiagnostics", "specifiers": specifiers, }), - RequestMethod::GetQuickInfo((specifier, position)) => json!({ - "id": id, - "method": "getQuickInfo", - "specifier": specifier, - "position": position, - }), RequestMethod::GetDocumentHighlights(( specifier, position, @@ -1212,50 +1294,29 @@ impl RequestMethod { "position": position, "filesToSearch": files_to_search, }), - RequestMethod::GetReferences((specifier, position)) => json!({ + RequestMethod::GetImplementation((specifier, position)) => json!({ "id": id, - "method": "getReferences", + "method": "getImplementation", "specifier": specifier, "position": position, }), - RequestMethod::GetDefinition((specifier, position)) => json!({ + RequestMethod::GetNavigationTree(specifier) => json!({ "id": id, - "method": "getDefinition", + "method": "getNavigationTree", + "specifier": specifier, + }), + RequestMethod::GetQuickInfo((specifier, position)) => json!({ + "id": id, + "method": "getQuickInfo", "specifier": specifier, "position": position, }), - RequestMethod::GetCompletions((specifier, position, preferences)) => { - json!({ - "id": id, - "method": "getCompletions", - "specifier": specifier, - "position": position, - "preferences": preferences, - }) - } - RequestMethod::GetImplementation((specifier, position)) => json!({ - "id": id, - "method": "getImplementation", - "specifier": specifier, - "position": position, + RequestMethod::GetReferences((specifier, position)) => json!({ + "id": id, + "method": "getReferences", + "specifier": specifier, + "position": position, }), - RequestMethod::FindRenameLocations(( - specifier, - position, - find_in_strings, - find_in_comments, - provide_prefix_and_suffix_text_for_rename, - )) => { - json!({ - "id": id, - "method": "findRenameLocations", - "specifier": specifier, - "position": position, - "findInStrings": find_in_strings, - "findInComments": find_in_comments, - "providePrefixAndSuffixTextForRename": provide_prefix_and_suffix_text_for_rename - }) - } } } } diff --git a/cli/tests/lsp/code_lens_request.json b/cli/tests/lsp/code_lens_request.json new file mode 100644 index 000000000..a876153bf --- /dev/null +++ b/cli/tests/lsp/code_lens_request.json @@ -0,0 +1,10 @@ +{ + "jsonrpc": "2.0", + "id": 2, + "method": "textDocument/codeLens", + "params": { + "textDocument": { + "uri": "file:///a/file.ts" + } + } +} diff --git a/cli/tests/lsp/code_lens_resolve_request.json b/cli/tests/lsp/code_lens_resolve_request.json new file mode 100644 index 000000000..150603cd4 --- /dev/null +++ b/cli/tests/lsp/code_lens_resolve_request.json @@ -0,0 +1,21 @@ +{ + "jsonrpc": "2.0", + "id": 4, + "method": "codeLens/resolve", + "params": { + "range": { + "start": { + "line": 0, + "character": 6 + }, + "end": { + "line": 0, + "character": 7 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + } +} diff --git a/cli/tests/lsp/did_open_notification_cl_references.json b/cli/tests/lsp/did_open_notification_cl_references.json new file mode 100644 index 000000000..546ba5674 --- /dev/null +++ b/cli/tests/lsp/did_open_notification_cl_references.json @@ -0,0 +1,12 @@ +{ + "jsonrpc": "2.0", + "method": "textDocument/didOpen", + "params": { + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "class A {\n a = \"a\";\n\n b() {\n console.log(this.a);\n }\n\n c() {\n this.a = \"c\";\n }\n}\n\nconst a = new A();\na.b();\n" + } + } +} diff --git a/cli/tests/lsp/initialize_request.json b/cli/tests/lsp/initialize_request.json index 722a3c783..46f96a2c5 100644 --- a/cli/tests/lsp/initialize_request.json +++ b/cli/tests/lsp/initialize_request.json @@ -11,6 +11,9 @@ "rootUri": null, "initializationOptions": { "enable": true, + "codeLens": { + "references": true + }, "lint": true, "importMap": null, "unstable": false diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index b50e32c76..1dc00873d 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -502,6 +502,18 @@ delete Object.prototype.__proto__; compilationSettings = options; return respond(id, true); } + case "findRenameLocations": { + return respond( + id, + languageService.findRenameLocations( + request.specifier, + request.position, + request.findInStrings, + request.findInComments, + request.providePrefixAndSuffixTextForRename, + ), + ); + } case "getAsset": { const sourceFile = host.getSourceFile( request.specifier, @@ -509,6 +521,25 @@ delete Object.prototype.__proto__; ); return respond(id, sourceFile && sourceFile.text); } + case "getCompletions": { + return respond( + id, + languageService.getCompletionsAtPosition( + request.specifier, + request.position, + request.preferences, + ), + ); + } + case "getDefinition": { + return respond( + id, + languageService.getDefinitionAndBoundSpan( + request.specifier, + request.position, + ), + ); + } case "getDiagnostics": { try { /** @type {Record<string, any[]>} */ @@ -530,25 +561,6 @@ delete Object.prototype.__proto__; return respond(id, {}); } } - case "getQuickInfo": { - return respond( - id, - languageService.getQuickInfoAtPosition( - request.specifier, - request.position, - ), - ); - } - case "getCompletions": { - return respond( - id, - languageService.getCompletionsAtPosition( - request.specifier, - request.position, - request.preferences, - ), - ); - } case "getDocumentHighlights": { return respond( id, @@ -559,42 +571,36 @@ delete Object.prototype.__proto__; ), ); } - case "getReferences": { + case "getImplementation": { return respond( id, - languageService.getReferencesAtPosition( + languageService.getImplementationAtPosition( request.specifier, request.position, ), ); } - case "getDefinition": { + case "getNavigationTree": { return respond( id, - languageService.getDefinitionAndBoundSpan( - request.specifier, - request.position, - ), + languageService.getNavigationTree(request.specifier), ); } - case "getImplementation": { + case "getQuickInfo": { return respond( id, - languageService.getImplementationAtPosition( + languageService.getQuickInfoAtPosition( request.specifier, request.position, ), ); } - case "findRenameLocations": { + case "getReferences": { return respond( id, - languageService.findRenameLocations( + languageService.getReferencesAtPosition( request.specifier, request.position, - request.findInStrings, - request.findInComments, - request.providePrefixAndSuffixTextForRename, ), ); } diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index fc0a2bf83..17d6ddb38 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -42,15 +42,16 @@ declare global { type LanguageServerRequest = | ConfigureRequest + | FindRenameLocationsRequest | GetAsset + | GetCompletionsRequest + | GetDefinitionRequest | GetDiagnosticsRequest - | GetQuickInfoRequest | GetDocumentHighlightsRequest - | GetReferencesRequest - | GetDefinitionRequest - | GetCompletionsRequest | GetImplementationRequest - | FindRenameLocationsRequest; + | GetNavigationTree + | GetQuickInfoRequest + | GetReferencesRequest; interface BaseLanguageServerRequest { id: number; @@ -63,18 +64,34 @@ declare global { compilerOptions: Record<string, any>; } + interface FindRenameLocationsRequest extends BaseLanguageServerRequest { + method: "findRenameLocations"; + specifier: string; + position: number; + findInStrings: boolean; + findInComments: boolean; + providePrefixAndSuffixTextForRename: boolean; + } + interface GetAsset extends BaseLanguageServerRequest { method: "getAsset"; specifier: string; } + interface GetCompletionsRequest extends BaseLanguageServerRequest { + method: "getCompletions"; + specifier: string; + position: number; + preferences: ts.UserPreferences; + } + interface GetDiagnosticsRequest extends BaseLanguageServerRequest { method: "getDiagnostics"; specifiers: string[]; } - interface GetQuickInfoRequest extends BaseLanguageServerRequest { - method: "getQuickInfo"; + interface GetDefinitionRequest extends BaseLanguageServerRequest { + method: "getDefinition"; specifier: string; position: number; } @@ -86,37 +103,26 @@ declare global { filesToSearch: string[]; } - interface GetReferencesRequest extends BaseLanguageServerRequest { - method: "getReferences"; + interface GetImplementationRequest extends BaseLanguageServerRequest { + method: "getImplementation"; specifier: string; position: number; } - interface GetDefinitionRequest extends BaseLanguageServerRequest { - method: "getDefinition"; + interface GetNavigationTree extends BaseLanguageServerRequest { + method: "getNavigationTree"; specifier: string; - position: number; } - interface GetCompletionsRequest extends BaseLanguageServerRequest { - method: "getCompletions"; - specifier: string; - position: number; - preferences: ts.UserPreferences; - } - - interface GetImplementationRequest extends BaseLanguageServerRequest { - method: "getImplementation"; + interface GetQuickInfoRequest extends BaseLanguageServerRequest { + method: "getQuickInfo"; specifier: string; position: number; } - interface FindRenameLocationsRequest extends BaseLanguageServerRequest { - method: "findRenameLocations"; + interface GetReferencesRequest extends BaseLanguageServerRequest { + method: "getReferences"; specifier: string; position: number; - findInStrings: boolean; - findInComments: boolean; - providePrefixAndSuffixTextForRename: boolean; } } |