mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
feat(lsp): add type definition provider (#12789)
This commit is contained in:
parent
bf5657cd59
commit
210300d049
6 changed files with 150 additions and 2 deletions
|
@ -27,6 +27,7 @@ use lspower::lsp::SignatureHelpOptions;
|
||||||
use lspower::lsp::TextDocumentSyncCapability;
|
use lspower::lsp::TextDocumentSyncCapability;
|
||||||
use lspower::lsp::TextDocumentSyncKind;
|
use lspower::lsp::TextDocumentSyncKind;
|
||||||
use lspower::lsp::TextDocumentSyncOptions;
|
use lspower::lsp::TextDocumentSyncOptions;
|
||||||
|
use lspower::lsp::TypeDefinitionProviderCapability;
|
||||||
use lspower::lsp::WorkDoneProgressOptions;
|
use lspower::lsp::WorkDoneProgressOptions;
|
||||||
use lspower::lsp::WorkspaceFoldersServerCapabilities;
|
use lspower::lsp::WorkspaceFoldersServerCapabilities;
|
||||||
use lspower::lsp::WorkspaceServerCapabilities;
|
use lspower::lsp::WorkspaceServerCapabilities;
|
||||||
|
@ -109,7 +110,9 @@ pub fn server_capabilities(
|
||||||
}),
|
}),
|
||||||
declaration_provider: None,
|
declaration_provider: None,
|
||||||
definition_provider: Some(OneOf::Left(true)),
|
definition_provider: Some(OneOf::Left(true)),
|
||||||
type_definition_provider: None,
|
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(
|
||||||
|
true,
|
||||||
|
)),
|
||||||
implementation_provider: Some(ImplementationProviderCapability::Simple(
|
implementation_provider: Some(ImplementationProviderCapability::Simple(
|
||||||
true,
|
true,
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -1622,6 +1622,54 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn goto_type_definition(
|
||||||
|
&mut self,
|
||||||
|
params: GotoTypeDefinitionParams,
|
||||||
|
) -> LspResult<Option<GotoTypeDefinitionResponse>> {
|
||||||
|
let specifier = self
|
||||||
|
.url_map
|
||||||
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
|
if !self.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mark = self.performance.mark("goto_definition", Some(¶ms));
|
||||||
|
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||||
|
let line_index = asset_or_doc.line_index();
|
||||||
|
let req = tsc::RequestMethod::GetTypeDefinition {
|
||||||
|
specifier,
|
||||||
|
position: line_index
|
||||||
|
.offset_tsc(params.text_document_position_params.position)?,
|
||||||
|
};
|
||||||
|
let maybe_definition_info: Option<Vec<tsc::DefinitionInfo>> = self
|
||||||
|
.ts_server
|
||||||
|
.request(self.snapshot()?, req)
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
error!("Unable to get type definition from TypeScript: {}", err);
|
||||||
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let response = if let Some(definition_info) = maybe_definition_info {
|
||||||
|
let mut location_links = Vec::new();
|
||||||
|
for info in definition_info {
|
||||||
|
if let Some(link) =
|
||||||
|
info.document_span.to_link(line_index.clone(), self).await
|
||||||
|
{
|
||||||
|
location_links.push(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(GotoTypeDefinitionResponse::Link(location_links))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.performance.measure(mark);
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
async fn completion(
|
async fn completion(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: CompletionParams,
|
params: CompletionParams,
|
||||||
|
@ -2428,6 +2476,13 @@ impl lspower::LanguageServer for LanguageServer {
|
||||||
self.0.lock().await.goto_definition(params).await
|
self.0.lock().await.goto_definition(params).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn goto_type_definition(
|
||||||
|
&self,
|
||||||
|
params: GotoTypeDefinitionParams,
|
||||||
|
) -> LspResult<Option<GotoTypeDefinitionResponse>> {
|
||||||
|
self.0.lock().await.goto_type_definition(params).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn completion(
|
async fn completion(
|
||||||
&self,
|
&self,
|
||||||
params: CompletionParams,
|
params: CompletionParams,
|
||||||
|
|
|
@ -2769,6 +2769,11 @@ pub enum RequestMethod {
|
||||||
GetSmartSelectionRange((ModuleSpecifier, u32)),
|
GetSmartSelectionRange((ModuleSpecifier, u32)),
|
||||||
/// Get the diagnostic codes that support some form of code fix.
|
/// Get the diagnostic codes that support some form of code fix.
|
||||||
GetSupportedCodeFixes,
|
GetSupportedCodeFixes,
|
||||||
|
/// Get the type definition information for a specific position.
|
||||||
|
GetTypeDefinition {
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
position: u32,
|
||||||
|
},
|
||||||
/// Resolve a call hierarchy item for a specific position.
|
/// Resolve a call hierarchy item for a specific position.
|
||||||
PrepareCallHierarchy((ModuleSpecifier, u32)),
|
PrepareCallHierarchy((ModuleSpecifier, u32)),
|
||||||
/// Resolve incoming call hierarchy items for a specific position.
|
/// Resolve incoming call hierarchy items for a specific position.
|
||||||
|
@ -2811,7 +2816,7 @@ impl RequestMethod {
|
||||||
"id": id,
|
"id": id,
|
||||||
"method": "getApplicableRefactors",
|
"method": "getApplicableRefactors",
|
||||||
"specifier": state.denormalize_specifier(specifier),
|
"specifier": state.denormalize_specifier(specifier),
|
||||||
"range": { "pos": span.start, "end": span.start + span.length},
|
"range": { "pos": span.start, "end": span.start + span.length },
|
||||||
"kind": kind,
|
"kind": kind,
|
||||||
}),
|
}),
|
||||||
RequestMethod::GetEditsForRefactor((
|
RequestMethod::GetEditsForRefactor((
|
||||||
|
@ -2950,6 +2955,15 @@ impl RequestMethod {
|
||||||
"id": id,
|
"id": id,
|
||||||
"method": "getSupportedCodeFixes",
|
"method": "getSupportedCodeFixes",
|
||||||
}),
|
}),
|
||||||
|
RequestMethod::GetTypeDefinition {
|
||||||
|
specifier,
|
||||||
|
position,
|
||||||
|
} => json!({
|
||||||
|
"id": id,
|
||||||
|
"method": "getTypeDefinition",
|
||||||
|
"specifier": state.denormalize_specifier(specifier),
|
||||||
|
"position": position
|
||||||
|
}),
|
||||||
RequestMethod::PrepareCallHierarchy((specifier, position)) => {
|
RequestMethod::PrepareCallHierarchy((specifier, position)) => {
|
||||||
json!({
|
json!({
|
||||||
"id": id,
|
"id": id,
|
||||||
|
|
|
@ -1266,6 +1266,66 @@ fn lsp_hover_typescript_types() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_goto_type_definition() {
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
did_open(
|
||||||
|
&mut client,
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "interface A {\n a: string;\n}\n\nexport class B implements A {\n a = \"a\";\n log() {\n console.log(this.a);\n }\n}\n\nconst b = new B();\nb;\n",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let (maybe_res, maybe_error) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"textDocument/typeDefinition",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"line": 12,
|
||||||
|
"character": 1
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_error.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
maybe_res,
|
||||||
|
Some(json!([
|
||||||
|
{
|
||||||
|
"targetUri": "file:///a/file.ts",
|
||||||
|
"targetRange": {
|
||||||
|
"start": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 9,
|
||||||
|
"character": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"targetSelectionRange": {
|
||||||
|
"start": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 13
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]))
|
||||||
|
);
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lsp_call_hierarchy() {
|
fn lsp_call_hierarchy() {
|
||||||
let mut client = init("initialize_params.json");
|
let mut client = init("initialize_params.json");
|
||||||
|
|
|
@ -810,6 +810,15 @@ delete Object.prototype.__proto__;
|
||||||
ts.getSupportedCodeFixes(),
|
ts.getSupportedCodeFixes(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case "getTypeDefinition": {
|
||||||
|
return respond(
|
||||||
|
id,
|
||||||
|
languageService.getTypeDefinitionAtPosition(
|
||||||
|
request.specifier,
|
||||||
|
request.position,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
case "prepareCallHierarchy": {
|
case "prepareCallHierarchy": {
|
||||||
return respond(
|
return respond(
|
||||||
id,
|
id,
|
||||||
|
|
7
cli/tsc/compiler.d.ts
vendored
7
cli/tsc/compiler.d.ts
vendored
|
@ -66,6 +66,7 @@ declare global {
|
||||||
| GetSignatureHelpItemsRequest
|
| GetSignatureHelpItemsRequest
|
||||||
| GetSmartSelectionRange
|
| GetSmartSelectionRange
|
||||||
| GetSupportedCodeFixes
|
| GetSupportedCodeFixes
|
||||||
|
| GetTypeDefinitionRequest
|
||||||
| PrepareCallHierarchy
|
| PrepareCallHierarchy
|
||||||
| ProvideCallHierarchyIncomingCalls
|
| ProvideCallHierarchyIncomingCalls
|
||||||
| ProvideCallHierarchyOutgoingCalls;
|
| ProvideCallHierarchyOutgoingCalls;
|
||||||
|
@ -220,6 +221,12 @@ declare global {
|
||||||
method: "getSupportedCodeFixes";
|
method: "getSupportedCodeFixes";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetTypeDefinitionRequest extends BaseLanguageServerRequest {
|
||||||
|
method: "getTypeDefinition";
|
||||||
|
specifier: string;
|
||||||
|
position: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface PrepareCallHierarchy extends BaseLanguageServerRequest {
|
interface PrepareCallHierarchy extends BaseLanguageServerRequest {
|
||||||
method: "prepareCallHierarchy";
|
method: "prepareCallHierarchy";
|
||||||
specifier: string;
|
specifier: string;
|
||||||
|
|
Loading…
Reference in a new issue