summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHirochika Matsumoto <matsujika@gmail.com>2021-01-13 06:53:27 +0900
committerGitHub <noreply@github.com>2021-01-13 08:53:27 +1100
commit8d5af6ca5264201be1cb04b0bb1a0b88ce5166da (patch)
treeb466958b75a1eb4f7f71626ea59e272dac8ecd0a
parent46a072c79267ee937b6fd14ad22b2aff2ff3d739 (diff)
feat(lsp): Add textDocument/implementation (#9071)
Ref #8643
-rw-r--r--cli/lsp/capabilities.rs5
-rw-r--r--cli/lsp/language_server.rs64
-rw-r--r--cli/lsp/tsc.rs107
-rw-r--r--cli/tsc/99_main_compiler.js9
-rw-r--r--cli/tsc/compiler.d.ts7
5 files changed, 162 insertions, 30 deletions
diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs
index 9c4cd317f..68835eba7 100644
--- a/cli/lsp/capabilities.rs
+++ b/cli/lsp/capabilities.rs
@@ -8,6 +8,7 @@
use lspower::lsp_types::ClientCapabilities;
use lspower::lsp_types::CompletionOptions;
use lspower::lsp_types::HoverProviderCapability;
+use lspower::lsp_types::ImplementationProviderCapability;
use lspower::lsp_types::OneOf;
use lspower::lsp_types::SaveOptions;
use lspower::lsp_types::ServerCapabilities;
@@ -50,7 +51,9 @@ pub fn server_capabilities(
declaration_provider: None,
definition_provider: Some(OneOf::Left(true)),
type_definition_provider: None,
- implementation_provider: None,
+ implementation_provider: Some(ImplementationProviderCapability::Simple(
+ true,
+ )),
references_provider: Some(OneOf::Left(true)),
document_highlight_provider: Some(OneOf::Left(true)),
document_symbol_provider: None,
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 70036df3e..1b22cea2c 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -11,6 +11,7 @@ use deno_core::ModuleSpecifier;
use dprint_plugin_typescript as dprint;
use lspower::jsonrpc::Error as LspError;
use lspower::jsonrpc::Result as LspResult;
+use lspower::lsp_types::request::*;
use lspower::lsp_types::*;
use lspower::Client;
use std::collections::HashMap;
@@ -893,6 +894,69 @@ impl lspower::LanguageServer for LanguageServer {
}
}
+ async fn goto_implementation(
+ &self,
+ params: GotoImplementationParams,
+ ) -> LspResult<Option<GotoImplementationResponse>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let specifier = utils::normalize_url(
+ params.text_document_position_params.text_document.uri,
+ );
+ let line_index =
+ self
+ .get_line_index(specifier.clone())
+ .await
+ .map_err(|err| {
+ error!("Failed to get line_index {:#?}", err);
+ LspError::internal_error()
+ })?;
+
+ let req = tsc::RequestMethod::GetImplementation((
+ specifier,
+ text::to_char_pos(
+ &line_index,
+ params.text_document_position_params.position,
+ ),
+ ));
+ let res =
+ self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {:#?}", err);
+ LspError::invalid_request()
+ })?;
+
+ let maybe_implementations = serde_json::from_value::<Option<Vec<tsc::ImplementationLocation>>>(res)
+ .map_err(|err| {
+ error!("Failed to deserialized tsserver response to Vec<ImplementationLocation> {:#?}", err);
+ LspError::internal_error()
+ })?;
+
+ if let Some(implementations) = maybe_implementations {
+ let mut results = Vec::new();
+ for impl_ in implementations {
+ let document_span = impl_.document_span;
+ let impl_specifier =
+ ModuleSpecifier::resolve_url(&document_span.file_name).unwrap();
+ let impl_line_index =
+ &self.get_line_index(impl_specifier).await.unwrap();
+ if let Some(link) = document_span
+ .to_link(impl_line_index, |s| self.get_line_index(s))
+ .await
+ {
+ results.push(link);
+ }
+ }
+ Ok(Some(GotoDefinitionResponse::Link(results)))
+ } else {
+ Ok(None)
+ }
+ }
+
async fn rename(
&self,
params: RenameParams,
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 67d6afcc5..b545a796b 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -413,6 +413,66 @@ impl QuickInfo {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
+pub struct DocumentSpan {
+ text_span: TextSpan,
+ pub file_name: String,
+ original_text_span: Option<TextSpan>,
+ original_file_name: Option<String>,
+ context_span: Option<TextSpan>,
+ original_context_span: Option<TextSpan>,
+}
+
+impl DocumentSpan {
+ pub async fn to_link<F, Fut>(
+ &self,
+ line_index: &[u32],
+ index_provider: F,
+ ) -> Option<lsp_types::LocationLink>
+ where
+ F: Fn(ModuleSpecifier) -> Fut,
+ Fut: Future<Output = Result<Vec<u32>, AnyError>>,
+ {
+ let target_specifier =
+ ModuleSpecifier::resolve_url(&self.file_name).unwrap();
+ if let Ok(target_line_index) = index_provider(target_specifier).await {
+ let target_uri = utils::normalize_file_name(&self.file_name).unwrap();
+ let (target_range, target_selection_range) =
+ if let Some(context_span) = &self.context_span {
+ (
+ context_span.to_range(&target_line_index),
+ self.text_span.to_range(&target_line_index),
+ )
+ } else {
+ (
+ self.text_span.to_range(&target_line_index),
+ self.text_span.to_range(&target_line_index),
+ )
+ };
+ let link = lsp_types::LocationLink {
+ origin_selection_range: Some(self.text_span.to_range(line_index)),
+ target_uri,
+ target_range,
+ target_selection_range,
+ };
+ Some(link)
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ImplementationLocation {
+ #[serde(flatten)]
+ pub document_span: DocumentSpan,
+ // ImplementationLocation props
+ kind: ScriptElementKind,
+ display_parts: Vec<SymbolDisplayPart>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct RenameLocation {
// inherit from DocumentSpan
text_span: TextSpan,
@@ -520,12 +580,9 @@ pub struct DefinitionInfo {
name: String,
container_kind: Option<ScriptElementKind>,
container_name: Option<String>,
- text_span: TextSpan,
- pub file_name: String,
- original_text_span: Option<TextSpan>,
- original_file_name: Option<String>,
- context_span: Option<TextSpan>,
- original_context_span: Option<TextSpan>,
+
+ #[serde(flatten)]
+ pub document_span: DocumentSpan,
}
#[derive(Debug, Deserialize)]
@@ -542,34 +599,18 @@ impl DefinitionInfoAndBoundSpan {
index_provider: F,
) -> Option<lsp_types::GotoDefinitionResponse>
where
- F: Fn(ModuleSpecifier) -> Fut,
+ F: Fn(ModuleSpecifier) -> Fut + Clone,
Fut: Future<Output = Result<Vec<u32>, AnyError>>,
{
if let Some(definitions) = &self.definitions {
let mut location_links = Vec::<lsp_types::LocationLink>::new();
for di in definitions {
- let target_specifier =
- ModuleSpecifier::resolve_url(&di.file_name).unwrap();
- if let Ok(target_line_index) = index_provider(target_specifier).await {
- let target_uri = utils::normalize_file_name(&di.file_name).unwrap();
- let (target_range, target_selection_range) =
- if let Some(context_span) = &di.context_span {
- (
- context_span.to_range(&target_line_index),
- di.text_span.to_range(&target_line_index),
- )
- } else {
- (
- di.text_span.to_range(&target_line_index),
- di.text_span.to_range(&target_line_index),
- )
- };
- location_links.push(lsp_types::LocationLink {
- origin_selection_range: Some(self.text_span.to_range(line_index)),
- target_uri,
- target_range,
- target_selection_range,
- });
+ if let Some(link) = di
+ .document_span
+ .to_link(line_index, index_provider.clone())
+ .await
+ {
+ location_links.push(link);
}
}
Some(lsp_types::GotoDefinitionResponse::Link(location_links))
@@ -1135,6 +1176,8 @@ pub enum RequestMethod {
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)),
}
@@ -1195,6 +1238,12 @@ impl RequestMethod {
"preferences": preferences,
})
}
+ RequestMethod::GetImplementation((specifier, position)) => json!({
+ "id": id,
+ "method": "getImplementation",
+ "specifier": specifier,
+ "position": position,
+ }),
RequestMethod::FindRenameLocations((
specifier,
position,
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index 8cbe8558f..14fcfa2bc 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -569,6 +569,15 @@ delete Object.prototype.__proto__;
),
);
}
+ case "getImplementation": {
+ return respond(
+ id,
+ languageService.getImplementationAtPosition(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
case "findRenameLocations": {
return respond(
id,
diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts
index a2e004d8c..7fd4ce37d 100644
--- a/cli/tsc/compiler.d.ts
+++ b/cli/tsc/compiler.d.ts
@@ -49,6 +49,7 @@ declare global {
| GetReferencesRequest
| GetDefinitionRequest
| GetCompletionsRequest
+ | GetImplementationRequest
| FindRenameLocationsRequest;
interface BaseLanguageServerRequest {
@@ -104,6 +105,12 @@ declare global {
preferences: ts.UserPreferences;
}
+ interface GetImplementationRequest extends BaseLanguageServerRequest {
+ method: "getImplementation";
+ specifier: string;
+ position: number;
+ }
+
interface FindRenameLocationsRequest extends BaseLanguageServerRequest {
method: "findRenameLocations";
specifier: string;