Skip to content

Commit

Permalink
feat(lsp): Add textDocument/implementation (denoland#9071)
Browse files Browse the repository at this point in the history
  • Loading branch information
hkmatsumoto committed Jan 12, 2021
1 parent 46a072c commit 8d5af6c
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 30 deletions.
5 changes: 4 additions & 1 deletion cli/lsp/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
64 changes: 64 additions & 0 deletions cli/lsp/language_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
107 changes: 78 additions & 29 deletions cli/lsp/tsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,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 {
Expand Down Expand Up @@ -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)]
Expand All @@ -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))
Expand Down Expand Up @@ -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)),
}
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions cli/tsc/99_main_compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,15 @@ delete Object.prototype.__proto__;
),
);
}
case "getImplementation": {
return respond(
id,
languageService.getImplementationAtPosition(
request.specifier,
request.position,
),
);
}
case "findRenameLocations": {
return respond(
id,
Expand Down
7 changes: 7 additions & 0 deletions cli/tsc/compiler.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ declare global {
| GetReferencesRequest
| GetDefinitionRequest
| GetCompletionsRequest
| GetImplementationRequest
| FindRenameLocationsRequest;

interface BaseLanguageServerRequest {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 8d5af6c

Please sign in to comment.