summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs355
1 files changed, 355 insertions, 0 deletions
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),