summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/capabilities.rs3
-rw-r--r--cli/lsp/language_server.rs355
-rw-r--r--cli/lsp/tsc.rs248
-rw-r--r--cli/tests/lsp/incoming_calls_request.json33
-rw-r--r--cli/tests/lsp/outgoing_calls_request.json33
-rw-r--r--cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json12
-rw-r--r--cli/tests/lsp/prepare_call_hierarchy_request.json14
-rw-r--r--cli/tsc/99_main_compiler.js27
-rw-r--r--cli/tsc/compiler.d.ts25
9 files changed, 747 insertions, 3 deletions
diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs
index e17f030d3..cce349a5d 100644
--- a/cli/lsp/capabilities.rs
+++ b/cli/lsp/capabilities.rs
@@ -5,6 +5,7 @@
///! language server, which helps determine what messages are sent from the
///! client.
///!
+use lspower::lsp::CallHierarchyServerCapability;
use lspower::lsp::ClientCapabilities;
use lspower::lsp::CodeActionKind;
use lspower::lsp::CodeActionOptions;
@@ -114,7 +115,7 @@ pub fn server_capabilities(
document_link_provider: None,
color_provider: None,
execute_command_provider: None,
- call_hierarchy_provider: None,
+ call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
semantic_tokens_provider: None,
workspace: None,
experimental: None,
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 70682c41b..d5de93593 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -1643,6 +1643,192 @@ impl Inner {
Ok(response)
}
+ async fn incoming_calls(
+ &mut self,
+ params: CallHierarchyIncomingCallsParams,
+ ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let mark = self.performance.mark("incoming_calls");
+ let specifier = self.url_map.normalize_url(&params.item.uri);
+
+ let line_index =
+ if let Some(line_index) = self.get_line_index_sync(&specifier) {
+ line_index
+ } else {
+ return Err(LspError::invalid_params(format!(
+ "An unexpected specifier ({}) was provided.",
+ specifier
+ )));
+ };
+
+ let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls((
+ specifier.clone(),
+ line_index.offset_tsc(params.item.selection_range.start)?,
+ ));
+ let incoming_calls: Vec<tsc::CallHierarchyIncomingCall> = self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })?;
+
+ let maybe_root_path_owned = self
+ .config
+ .root_uri
+ .as_ref()
+ .and_then(|uri| uri.to_file_path().ok());
+ let mut resolved_items = Vec::<CallHierarchyIncomingCall>::new();
+ for item in incoming_calls.iter() {
+ if let Some(resolved) = item
+ .try_resolve_call_hierarchy_incoming_call(
+ self,
+ maybe_root_path_owned.as_deref(),
+ )
+ .await
+ {
+ resolved_items.push(resolved);
+ }
+ }
+ self.performance.measure(mark);
+ Ok(Some(resolved_items))
+ }
+
+ async fn outgoing_calls(
+ &mut self,
+ params: CallHierarchyOutgoingCallsParams,
+ ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let mark = self.performance.mark("outgoing_calls");
+ let specifier = self.url_map.normalize_url(&params.item.uri);
+
+ let line_index =
+ if let Some(line_index) = self.get_line_index_sync(&specifier) {
+ line_index
+ } else {
+ return Err(LspError::invalid_params(format!(
+ "An unexpected specifier ({}) was provided.",
+ specifier
+ )));
+ };
+
+ let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls((
+ specifier.clone(),
+ line_index.offset_tsc(params.item.selection_range.start)?,
+ ));
+ let outgoing_calls: Vec<tsc::CallHierarchyOutgoingCall> = self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })?;
+
+ let maybe_root_path_owned = self
+ .config
+ .root_uri
+ .as_ref()
+ .and_then(|uri| uri.to_file_path().ok());
+ let mut resolved_items = Vec::<CallHierarchyOutgoingCall>::new();
+ for item in outgoing_calls.iter() {
+ if let Some(resolved) = item
+ .try_resolve_call_hierarchy_outgoing_call(
+ &line_index,
+ self,
+ maybe_root_path_owned.as_deref(),
+ )
+ .await
+ {
+ resolved_items.push(resolved);
+ }
+ }
+ self.performance.measure(mark);
+ Ok(Some(resolved_items))
+ }
+
+ async fn prepare_call_hierarchy(
+ &mut self,
+ params: CallHierarchyPrepareParams,
+ ) -> LspResult<Option<Vec<CallHierarchyItem>>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let mark = self.performance.mark("prepare_call_hierarchy");
+ let specifier = self
+ .url_map
+ .normalize_url(&params.text_document_position_params.text_document.uri);
+
+ let line_index =
+ if let Some(line_index) = self.get_line_index_sync(&specifier) {
+ line_index
+ } else {
+ return Err(LspError::invalid_params(format!(
+ "An unexpected specifier ({}) was provided.",
+ specifier
+ )));
+ };
+
+ let req = tsc::RequestMethod::PrepareCallHierarchy((
+ specifier.clone(),
+ line_index.offset_tsc(params.text_document_position_params.position)?,
+ ));
+ let maybe_one_or_many: Option<tsc::OneOrMany<tsc::CallHierarchyItem>> =
+ self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })?;
+
+ let response = if let Some(one_or_many) = maybe_one_or_many {
+ let maybe_root_path_owned = self
+ .config
+ .root_uri
+ .as_ref()
+ .and_then(|uri| uri.to_file_path().ok());
+ let mut resolved_items = Vec::<CallHierarchyItem>::new();
+ match one_or_many {
+ tsc::OneOrMany::One(item) => {
+ if let Some(resolved) = item
+ .try_resolve_call_hierarchy_item(
+ self,
+ maybe_root_path_owned.as_deref(),
+ )
+ .await
+ {
+ resolved_items.push(resolved)
+ }
+ }
+ tsc::OneOrMany::Many(items) => {
+ for item in items.iter() {
+ if let Some(resolved) = item
+ .try_resolve_call_hierarchy_item(
+ self,
+ maybe_root_path_owned.as_deref(),
+ )
+ .await
+ {
+ resolved_items.push(resolved);
+ }
+ }
+ }
+ }
+ Some(resolved_items)
+ } else {
+ None
+ };
+ self.performance.measure(mark);
+ Ok(response)
+ }
+
async fn rename(
&mut self,
params: RenameParams,
@@ -1971,6 +2157,27 @@ impl lspower::LanguageServer for LanguageServer {
self.0.lock().await.folding_range(params).await
}
+ async fn incoming_calls(
+ &self,
+ params: CallHierarchyIncomingCallsParams,
+ ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
+ self.0.lock().await.incoming_calls(params).await
+ }
+
+ async fn outgoing_calls(
+ &self,
+ params: CallHierarchyOutgoingCallsParams,
+ ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
+ self.0.lock().await.outgoing_calls(params).await
+ }
+
+ async fn prepare_call_hierarchy(
+ &self,
+ params: CallHierarchyPrepareParams,
+ ) -> LspResult<Option<Vec<CallHierarchyItem>>> {
+ self.0.lock().await.prepare_call_hierarchy(params).await
+ }
+
async fn rename(
&self,
params: RenameParams,
@@ -2472,6 +2679,154 @@ mod tests {
}
#[tokio::test]
+ async fn test_call_hierarchy() {
+ let mut harness = LspTestHarness::new(vec![
+ ("initialize_request.json", LspResponse::RequestAny),
+ ("initialized_notification.json", LspResponse::None),
+ (
+ "prepare_call_hierarchy_did_open_notification.json",
+ LspResponse::None,
+ ),
+ (
+ "prepare_call_hierarchy_request.json",
+ LspResponse::Request(
+ 2,
+ json!([
+ {
+ "name": "baz",
+ "kind": 6,
+ "detail": "Bar",
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 7,
+ "character": 3
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 5,
+ "character": 5
+ }
+ }
+ }
+ ]),
+ ),
+ ),
+ (
+ "incoming_calls_request.json",
+ LspResponse::Request(
+ 4,
+ json!([
+ {
+ "from": {
+ "name": "main",
+ "kind": 12,
+ "detail": "",
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 10,
+ "character": 0
+ },
+ "end": {
+ "line": 13,
+ "character": 1
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 10,
+ "character": 9
+ },
+ "end": {
+ "line": 10,
+ "character": 13
+ }
+ }
+ },
+ "fromRanges": [
+ {
+ "start": {
+ "line": 12,
+ "character": 6
+ },
+ "end": {
+ "line": 12,
+ "character": 9
+ }
+ }
+ ]
+ }
+ ]),
+ ),
+ ),
+ (
+ "outgoing_calls_request.json",
+ LspResponse::Request(
+ 5,
+ json!([
+ {
+ "to": {
+ "name": "foo",
+ "kind": 12,
+ "detail": "",
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 2,
+ "character": 1
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 0,
+ "character": 9
+ },
+ "end": {
+ "line": 0,
+ "character": 12
+ }
+ }
+ },
+ "fromRanges": [
+ {
+ "start": {
+ "line": 6,
+ "character": 11
+ },
+ "end": {
+ "line": 6,
+ "character": 14
+ }
+ }
+ ]
+ }
+ ]),
+ ),
+ ),
+ (
+ "shutdown_request.json",
+ LspResponse::Request(3, json!(null)),
+ ),
+ ("exit_notification.json", LspResponse::None),
+ ]);
+ harness.run().await;
+ }
+
+ #[tokio::test]
async fn test_format_mbc() {
let mut harness = LspTestHarness::new(vec![
("initialize_request.json", LspResponse::RequestAny),
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index aaea82421..128a2ba00 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -35,10 +35,10 @@ use log::warn;
use lspower::lsp;
use regex::Captures;
use regex::Regex;
-use std::collections::HashMap;
use std::collections::HashSet;
use std::thread;
use std::{borrow::Cow, cmp};
+use std::{collections::HashMap, path::Path};
use text_size::TextSize;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
@@ -283,6 +283,13 @@ fn parse_kind_modifier(kind_modifiers: &str) -> HashSet<&str> {
re.split(kind_modifiers).collect()
}
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+pub enum OneOrMany<T> {
+ One(T),
+ Many(Vec<T>),
+}
+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub enum ScriptElementKind {
#[serde(rename = "")]
@@ -411,6 +418,33 @@ impl From<ScriptElementKind> for lsp::CompletionItemKind {
}
}
+impl From<ScriptElementKind> for lsp::SymbolKind {
+ fn from(kind: ScriptElementKind) -> Self {
+ match kind {
+ ScriptElementKind::ModuleElement => lsp::SymbolKind::Module,
+ ScriptElementKind::ClassElement => lsp::SymbolKind::Class,
+ ScriptElementKind::EnumElement => lsp::SymbolKind::Enum,
+ ScriptElementKind::InterfaceElement => lsp::SymbolKind::Interface,
+ ScriptElementKind::MemberFunctionElement => lsp::SymbolKind::Method,
+ ScriptElementKind::MemberVariableElement => lsp::SymbolKind::Property,
+ ScriptElementKind::MemberGetAccessorElement => lsp::SymbolKind::Property,
+ ScriptElementKind::MemberSetAccessorElement => lsp::SymbolKind::Property,
+ ScriptElementKind::VariableElement => lsp::SymbolKind::Variable,
+ ScriptElementKind::ConstElement => lsp::SymbolKind::Variable,
+ ScriptElementKind::LocalVariableElement => lsp::SymbolKind::Variable,
+ ScriptElementKind::FunctionElement => lsp::SymbolKind::Function,
+ ScriptElementKind::LocalFunctionElement => lsp::SymbolKind::Function,
+ ScriptElementKind::ConstructSignatureElement => {
+ lsp::SymbolKind::Constructor
+ }
+ ScriptElementKind::ConstructorImplementationElement => {
+ lsp::SymbolKind::Constructor
+ }
+ _ => lsp::SymbolKind::Variable,
+ }
+ }
+}
+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TextSpan {
@@ -919,6 +953,182 @@ impl ReferenceEntry {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
+pub struct CallHierarchyItem {
+ name: String,
+ kind: ScriptElementKind,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ kind_modifiers: Option<String>,
+ file: String,
+ span: TextSpan,
+ selection_span: TextSpan,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ container_name: Option<String>,
+}
+
+impl CallHierarchyItem {
+ pub(crate) async fn try_resolve_call_hierarchy_item(
+ &self,
+ language_server: &mut language_server::Inner,
+ maybe_root_path: Option<&Path>,
+ ) -> Option<lsp::CallHierarchyItem> {
+ let target_specifier = resolve_url(&self.file).unwrap();
+ let target_line_index = language_server
+ .get_line_index(target_specifier)
+ .await
+ .ok()?;
+
+ Some(self.to_call_hierarchy_item(
+ &target_line_index,
+ language_server,
+ maybe_root_path,
+ ))
+ }
+
+ pub(crate) fn to_call_hierarchy_item(
+ &self,
+ line_index: &LineIndex,
+ language_server: &mut language_server::Inner,
+ maybe_root_path: Option<&Path>,
+ ) -> lsp::CallHierarchyItem {
+ let target_specifier = resolve_url(&self.file).unwrap();
+ let uri = language_server
+ .url_map
+ .normalize_specifier(&target_specifier)
+ .unwrap();
+
+ let use_file_name = self.is_source_file_item();
+ let maybe_file_path = if uri.scheme() == "file" {
+ uri.to_file_path().ok()
+ } else {
+ None
+ };
+ let name = if use_file_name {
+ if let Some(file_path) = maybe_file_path.as_ref() {
+ file_path.file_name().unwrap().to_string_lossy().to_string()
+ } else {
+ uri.to_string()
+ }
+ } else {
+ self.name.clone()
+ };
+ let detail = if use_file_name {
+ if let Some(file_path) = maybe_file_path.as_ref() {
+ // TODO: update this to work with multi root workspaces
+ let parent_dir = file_path.parent().unwrap();
+ if let Some(root_path) = maybe_root_path {
+ parent_dir
+ .strip_prefix(root_path)
+ .unwrap_or(parent_dir)
+ .to_string_lossy()
+ .to_string()
+ } else {
+ parent_dir.to_string_lossy().to_string()
+ }
+ } else {
+ String::new()
+ }
+ } else {
+ self.container_name.as_ref().cloned().unwrap_or_default()
+ };
+
+ let mut tags: Option<Vec<lsp::SymbolTag>> = None;
+ if let Some(modifiers) = self.kind_modifiers.as_ref() {
+ let kind_modifiers = parse_kind_modifier(modifiers);
+ if kind_modifiers.contains("deprecated") {
+ tags = Some(vec![lsp::SymbolTag::Deprecated]);
+ }
+ }
+
+ lsp::CallHierarchyItem {
+ name,
+ tags,
+ uri,
+ detail: Some(detail),
+ kind: self.kind.clone().into(),
+ range: self.span.to_range(line_index),
+ selection_range: self.selection_span.to_range(line_index),
+ data: None,
+ }
+ }
+
+ fn is_source_file_item(&self) -> bool {
+ self.kind == ScriptElementKind::ScriptElement
+ || self.kind == ScriptElementKind::ModuleElement
+ && self.selection_span.start == 0
+ }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CallHierarchyIncomingCall {
+ from: CallHierarchyItem,
+ from_spans: Vec<TextSpan>,
+}
+
+impl CallHierarchyIncomingCall {
+ pub(crate) async fn try_resolve_call_hierarchy_incoming_call(
+ &self,
+ language_server: &mut language_server::Inner,
+ maybe_root_path: Option<&Path>,
+ ) -> Option<lsp::CallHierarchyIncomingCall> {
+ let target_specifier = resolve_url(&self.from.file).unwrap();
+ let target_line_index = language_server
+ .get_line_index(target_specifier)
+ .await
+ .ok()?;
+
+ Some(lsp::CallHierarchyIncomingCall {
+ from: self.from.to_call_hierarchy_item(
+ &target_line_index,
+ language_server,
+ maybe_root_path,
+ ),
+ from_ranges: self
+ .from_spans
+ .iter()
+ .map(|span| span.to_range(&target_line_index))
+ .collect(),
+ })
+ }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CallHierarchyOutgoingCall {
+ to: CallHierarchyItem,
+ from_spans: Vec<TextSpan>,
+}
+
+impl CallHierarchyOutgoingCall {
+ pub(crate) async fn try_resolve_call_hierarchy_outgoing_call(
+ &self,
+ line_index: &LineIndex,
+ language_server: &mut language_server::Inner,
+ maybe_root_path: Option<&Path>,
+ ) -> Option<lsp::CallHierarchyOutgoingCall> {
+ let target_specifier = resolve_url(&self.to.file).unwrap();
+ let target_line_index = language_server
+ .get_line_index(target_specifier)
+ .await
+ .ok()?;
+
+ Some(lsp::CallHierarchyOutgoingCall {
+ to: self.to.to_call_hierarchy_item(
+ &target_line_index,
+ language_server,
+ maybe_root_path,
+ ),
+ from_ranges: self
+ .from_spans
+ .iter()
+ .map(|span| span.to_range(&line_index))
+ .collect(),
+ })
+ }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct CompletionEntryDetails {
name: String,
kind: ScriptElementKind,
@@ -1956,6 +2166,12 @@ pub enum RequestMethod {
GetSmartSelectionRange((ModuleSpecifier, u32)),
/// Get the diagnostic codes that support some form of code fix.
GetSupportedCodeFixes,
+ /// Resolve a call hierarchy item for a specific position.
+ PrepareCallHierarchy((ModuleSpecifier, u32)),
+ /// Resolve incoming call hierarchy items for a specific position.
+ ProvideCallHierarchyIncomingCalls((ModuleSpecifier, u32)),
+ /// Resolve outgoing call hierarchy items for a specific position.
+ ProvideCallHierarchyOutgoingCalls((ModuleSpecifier, u32)),
}
impl RequestMethod {
@@ -2092,6 +2308,36 @@ impl RequestMethod {
"id": id,
"method": "getSupportedCodeFixes",
}),
+ RequestMethod::PrepareCallHierarchy((specifier, position)) => {
+ json!({
+ "id": id,
+ "method": "prepareCallHierarchy",
+ "specifier": specifier,
+ "position": position
+ })
+ }
+ RequestMethod::ProvideCallHierarchyIncomingCalls((
+ specifier,
+ position,
+ )) => {
+ json!({
+ "id": id,
+ "method": "provideCallHierarchyIncomingCalls",
+ "specifier": specifier,
+ "position": position
+ })
+ }
+ RequestMethod::ProvideCallHierarchyOutgoingCalls((
+ specifier,
+ position,
+ )) => {
+ json!({
+ "id": id,
+ "method": "provideCallHierarchyOutgoingCalls",
+ "specifier": specifier,
+ "position": position
+ })
+ }
}
}
}
diff --git a/cli/tests/lsp/incoming_calls_request.json b/cli/tests/lsp/incoming_calls_request.json
new file mode 100644
index 000000000..47af92c1b
--- /dev/null
+++ b/cli/tests/lsp/incoming_calls_request.json
@@ -0,0 +1,33 @@
+{
+ "jsonrpc": "2.0",
+ "id": 4,
+ "method": "callHierarchy/incomingCalls",
+ "params": {
+ "item": {
+ "name": "baz",
+ "kind": 6,
+ "detail": "Bar",
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 7,
+ "character": 3
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 5,
+ "character": 5
+ }
+ }
+ }
+ }
+}
diff --git a/cli/tests/lsp/outgoing_calls_request.json b/cli/tests/lsp/outgoing_calls_request.json
new file mode 100644
index 000000000..a8d224ae8
--- /dev/null
+++ b/cli/tests/lsp/outgoing_calls_request.json
@@ -0,0 +1,33 @@
+{
+ "jsonrpc": "2.0",
+ "id": 5,
+ "method": "callHierarchy/outgoingCalls",
+ "params": {
+ "item": {
+ "name": "baz",
+ "kind": 6,
+ "detail": "Bar",
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 7,
+ "character": 3
+ }
+ },
+ "selectionRange": {
+ "start": {
+ "line": 5,
+ "character": 2
+ },
+ "end": {
+ "line": 5,
+ "character": 5
+ }
+ }
+ }
+ }
+}
diff --git a/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json b/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json
new file mode 100644
index 000000000..a75bd3a53
--- /dev/null
+++ b/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json
@@ -0,0 +1,12 @@
+{
+ "jsonrpc": "2.0",
+ "method": "textDocument/didOpen",
+ "params": {
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "function foo() {\n return false;\n}\n\nclass Bar {\n baz() {\n return foo();\n }\n}\n\nfunction main() {\n const bar = new Bar();\n bar.baz();\n}\n\nmain();"
+ }
+ }
+}
diff --git a/cli/tests/lsp/prepare_call_hierarchy_request.json b/cli/tests/lsp/prepare_call_hierarchy_request.json
new file mode 100644
index 000000000..1f469ee8b
--- /dev/null
+++ b/cli/tests/lsp/prepare_call_hierarchy_request.json
@@ -0,0 +1,14 @@
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "textDocument/prepareCallHierarchy",
+ "params": {
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 5,
+ "character": 3
+ }
+ }
+}
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index dc2b59533..ad661e087 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -726,6 +726,33 @@ delete Object.prototype.__proto__;
ts.getSupportedCodeFixes(),
);
}
+ case "prepareCallHierarchy": {
+ return respond(
+ id,
+ languageService.prepareCallHierarchy(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "provideCallHierarchyIncomingCalls": {
+ return respond(
+ id,
+ languageService.provideCallHierarchyIncomingCalls(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
+ case "provideCallHierarchyOutgoingCalls": {
+ return respond(
+ id,
+ languageService.provideCallHierarchyOutgoingCalls(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
default:
throw new TypeError(
// @ts-ignore exhausted case statement sets type to never
diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts
index 0b1f5e4de..1488f1b02 100644
--- a/cli/tsc/compiler.d.ts
+++ b/cli/tsc/compiler.d.ts
@@ -63,7 +63,10 @@ declare global {
| GetReferencesRequest
| GetSignatureHelpItemsRequest
| GetSmartSelectionRange
- | GetSupportedCodeFixes;
+ | GetSupportedCodeFixes
+ | PrepareCallHierarchy
+ | ProvideCallHierarchyIncomingCalls
+ | ProvideCallHierarchyOutgoingCalls;
interface BaseLanguageServerRequest {
id: number;
@@ -185,4 +188,24 @@ declare global {
interface GetSupportedCodeFixes extends BaseLanguageServerRequest {
method: "getSupportedCodeFixes";
}
+
+ interface PrepareCallHierarchy extends BaseLanguageServerRequest {
+ method: "prepareCallHierarchy";
+ specifier: string;
+ position: number;
+ }
+
+ interface ProvideCallHierarchyIncomingCalls
+ extends BaseLanguageServerRequest {
+ method: "provideCallHierarchyIncomingCalls";
+ specifier: string;
+ position: number;
+ }
+
+ interface ProvideCallHierarchyOutgoingCalls
+ extends BaseLanguageServerRequest {
+ method: "provideCallHierarchyOutgoingCalls";
+ specifier: string;
+ position: number;
+ }
}