mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
parent
46a072c792
commit
8d5af6ca52
5 changed files with 162 additions and 30 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
107
cli/lsp/tsc.rs
107
cli/lsp/tsc.rs
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
|
|
@ -569,6 +569,15 @@ delete Object.prototype.__proto__;
|
|||
),
|
||||
);
|
||||
}
|
||||
case "getImplementation": {
|
||||
return respond(
|
||||
id,
|
||||
languageService.getImplementationAtPosition(
|
||||
request.specifier,
|
||||
request.position,
|
||||
),
|
||||
);
|
||||
}
|
||||
case "findRenameLocations": {
|
||||
return respond(
|
||||
id,
|
||||
|
|
7
cli/tsc/compiler.d.ts
vendored
7
cli/tsc/compiler.d.ts
vendored
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue