1
0
Fork 0
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:
Kitson Kelly 2021-11-23 11:09:19 +11:00 committed by GitHub
parent bf5657cd59
commit 210300d049
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 2 deletions

View file

@ -27,6 +27,7 @@ use lspower::lsp::SignatureHelpOptions;
use lspower::lsp::TextDocumentSyncCapability;
use lspower::lsp::TextDocumentSyncKind;
use lspower::lsp::TextDocumentSyncOptions;
use lspower::lsp::TypeDefinitionProviderCapability;
use lspower::lsp::WorkDoneProgressOptions;
use lspower::lsp::WorkspaceFoldersServerCapabilities;
use lspower::lsp::WorkspaceServerCapabilities;
@ -109,7 +110,9 @@ pub fn server_capabilities(
}),
declaration_provider: None,
definition_provider: Some(OneOf::Left(true)),
type_definition_provider: None,
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(
true,
)),
implementation_provider: Some(ImplementationProviderCapability::Simple(
true,
)),

View file

@ -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(&params.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(&params));
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(
&mut self,
params: CompletionParams,
@ -2428,6 +2476,13 @@ impl lspower::LanguageServer for LanguageServer {
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(
&self,
params: CompletionParams,

View file

@ -2769,6 +2769,11 @@ pub enum RequestMethod {
GetSmartSelectionRange((ModuleSpecifier, u32)),
/// Get the diagnostic codes that support some form of code fix.
GetSupportedCodeFixes,
/// Get the type definition information for a specific position.
GetTypeDefinition {
specifier: ModuleSpecifier,
position: u32,
},
/// Resolve a call hierarchy item for a specific position.
PrepareCallHierarchy((ModuleSpecifier, u32)),
/// Resolve incoming call hierarchy items for a specific position.
@ -2811,7 +2816,7 @@ impl RequestMethod {
"id": id,
"method": "getApplicableRefactors",
"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,
}),
RequestMethod::GetEditsForRefactor((
@ -2950,6 +2955,15 @@ impl RequestMethod {
"id": id,
"method": "getSupportedCodeFixes",
}),
RequestMethod::GetTypeDefinition {
specifier,
position,
} => json!({
"id": id,
"method": "getTypeDefinition",
"specifier": state.denormalize_specifier(specifier),
"position": position
}),
RequestMethod::PrepareCallHierarchy((specifier, position)) => {
json!({
"id": id,

View file

@ -1266,6 +1266,66 @@ fn lsp_hover_typescript_types() {
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]
fn lsp_call_hierarchy() {
let mut client = init("initialize_params.json");

View file

@ -810,6 +810,15 @@ delete Object.prototype.__proto__;
ts.getSupportedCodeFixes(),
);
}
case "getTypeDefinition": {
return respond(
id,
languageService.getTypeDefinitionAtPosition(
request.specifier,
request.position,
),
);
}
case "prepareCallHierarchy": {
return respond(
id,

View file

@ -66,6 +66,7 @@ declare global {
| GetSignatureHelpItemsRequest
| GetSmartSelectionRange
| GetSupportedCodeFixes
| GetTypeDefinitionRequest
| PrepareCallHierarchy
| ProvideCallHierarchyIncomingCalls
| ProvideCallHierarchyOutgoingCalls;
@ -220,6 +221,12 @@ declare global {
method: "getSupportedCodeFixes";
}
interface GetTypeDefinitionRequest extends BaseLanguageServerRequest {
method: "getTypeDefinition";
specifier: string;
position: number;
}
interface PrepareCallHierarchy extends BaseLanguageServerRequest {
method: "prepareCallHierarchy";
specifier: string;