summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/lsp/analysis.rs15
-rw-r--r--cli/lsp/capabilities.rs5
-rw-r--r--cli/lsp/config.rs46
-rw-r--r--cli/lsp/documents.rs27
-rw-r--r--cli/lsp/language_server.rs360
-rw-r--r--cli/lsp/tsc.rs169
-rw-r--r--cli/tests/lsp/code_lens_request.json10
-rw-r--r--cli/tests/lsp/code_lens_resolve_request.json21
-rw-r--r--cli/tests/lsp/did_open_notification_cl_references.json12
-rw-r--r--cli/tests/lsp/initialize_request.json3
-rw-r--r--cli/tsc/99_main_compiler.js72
-rw-r--r--cli/tsc/compiler.d.ts58
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;
}
}