diff options
-rw-r--r-- | cli/lsp/capabilities.rs | 5 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 64 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 107 | ||||
-rw-r--r-- | cli/tsc/99_main_compiler.js | 9 | ||||
-rw-r--r-- | cli/tsc/compiler.d.ts | 7 |
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; |