summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/capabilities.rs5
-rw-r--r--cli/lsp/language_server.rs157
-rw-r--r--cli/lsp/tsc.rs35
-rw-r--r--cli/tests/lsp/selection_range_did_open_notification.json12
-rw-r--r--cli/tests/lsp/selection_range_request.json16
-rw-r--r--cli/tsc/99_main_compiler.js9
-rw-r--r--cli/tsc/compiler.d.ts7
7 files changed, 240 insertions, 1 deletions
diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs
index 82bb910bb..dbfb42b59 100644
--- a/cli/lsp/capabilities.rs
+++ b/cli/lsp/capabilities.rs
@@ -15,6 +15,7 @@ use lspower::lsp::HoverProviderCapability;
use lspower::lsp::ImplementationProviderCapability;
use lspower::lsp::OneOf;
use lspower::lsp::SaveOptions;
+use lspower::lsp::SelectionRangeProviderCapability;
use lspower::lsp::ServerCapabilities;
use lspower::lsp::SignatureHelpOptions;
use lspower::lsp::TextDocumentSyncCapability;
@@ -104,7 +105,9 @@ pub fn server_capabilities(
document_formatting_provider: Some(OneOf::Left(true)),
document_range_formatting_provider: None,
document_on_type_formatting_provider: None,
- selection_range_provider: None,
+ selection_range_provider: Some(SelectionRangeProviderCapability::Simple(
+ true,
+ )),
folding_range_provider: None,
rename_provider: Some(OneOf::Left(true)),
document_link_provider: None,
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 3c3d82b3b..5489a0b9b 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -1590,6 +1590,48 @@ impl Inner {
}
}
+ async fn selection_range(
+ &self,
+ params: SelectionRangeParams,
+ ) -> LspResult<Option<Vec<SelectionRange>>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let mark = self.performance.mark("selection_range");
+ let specifier = self.url_map.normalize_url(&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 mut selection_ranges = Vec::<SelectionRange>::new();
+ for position in params.positions {
+ let req = tsc::RequestMethod::GetSmartSelectionRange((
+ specifier.clone(),
+ line_index.offset_tsc(position)?,
+ ));
+
+ let selection_range: tsc::SelectionRange = self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })?;
+
+ selection_ranges.push(selection_range.to_selection_range(&line_index));
+ }
+ self.performance.measure(mark);
+ Ok(Some(selection_ranges))
+ }
+
async fn signature_help(
&self,
params: SignatureHelpParams,
@@ -1794,6 +1836,13 @@ impl lspower::LanguageServer for LanguageServer {
self.0.lock().await.request_else(method, params).await
}
+ async fn selection_range(
+ &self,
+ params: SelectionRangeParams,
+ ) -> LspResult<Option<Vec<SelectionRange>>> {
+ self.0.lock().await.selection_range(params).await
+ }
+
async fn signature_help(
&self,
params: SignatureHelpParams,
@@ -2412,6 +2461,114 @@ mod tests {
}
#[tokio::test]
+ async fn test_selection_range() {
+ let mut harness = LspTestHarness::new(vec![
+ ("initialize_request.json", LspResponse::RequestAny),
+ ("initialized_notification.json", LspResponse::None),
+ (
+ "selection_range_did_open_notification.json",
+ LspResponse::None,
+ ),
+ (
+ "selection_range_request.json",
+ LspResponse::Request(
+ 2,
+ json!([{
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 8
+ },
+ "end": {
+ "line": 2,
+ "character": 9
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 8
+ },
+ "end": {
+ "line": 2,
+ "character": 15
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 4
+ },
+ "end": {
+ "line": 4,
+ "character": 5
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 13
+ },
+ "end": {
+ "line": 6,
+ "character": 2
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 1,
+ "character": 2
+ },
+ "end": {
+ "line": 6,
+ "character": 3
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 11
+ },
+ "end": {
+ "line": 7,
+ "character": 0
+ }
+ },
+ "parent": {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }]),
+ ),
+ ),
+ (
+ "shutdown_request.json",
+ LspResponse::Request(3, json!(null)),
+ ),
+ ("exit_notification.json", LspResponse::None),
+ ]);
+ harness.run().await;
+ }
+
+ #[tokio::test]
async fn test_code_lens_request() {
let mut harness = LspTestHarness::new(vec![
("initialize_request.json", LspResponse::RequestAny),
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index a60f15eb8..c2418d157 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -1312,6 +1312,31 @@ impl SignatureHelpParameter {
}
#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SelectionRange {
+ text_span: TextSpan,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ parent: Option<Box<SelectionRange>>,
+}
+
+impl SelectionRange {
+ pub fn to_selection_range(
+ &self,
+ line_index: &LineIndex,
+ ) -> lsp::SelectionRange {
+ lsp::SelectionRange {
+ range: self.text_span.to_range(line_index),
+ parent: match &self.parent {
+ Some(parent_selection) => {
+ Some(Box::new(parent_selection.to_selection_range(line_index)))
+ }
+ None => None,
+ },
+ }
+ }
+}
+
+#[derive(Debug, Clone, Deserialize)]
struct Response {
id: usize,
data: Value,
@@ -1856,6 +1881,8 @@ pub enum RequestMethod {
GetReferences((ModuleSpecifier, u32)),
/// Get signature help items for a specific position.
GetSignatureHelpItems((ModuleSpecifier, u32, SignatureHelpItemsOptions)),
+ /// Get a selection range for a specific position.
+ GetSmartSelectionRange((ModuleSpecifier, u32)),
/// Get the diagnostic codes that support some form of code fix.
GetSupportedCodeFixes,
}
@@ -1977,6 +2004,14 @@ impl RequestMethod {
"options": options,
})
}
+ RequestMethod::GetSmartSelectionRange((specifier, position)) => {
+ json!({
+ "id": id,
+ "method": "getSmartSelectionRange",
+ "specifier": specifier,
+ "position": position
+ })
+ }
RequestMethod::GetSupportedCodeFixes => json!({
"id": id,
"method": "getSupportedCodeFixes",
diff --git a/cli/tests/lsp/selection_range_did_open_notification.json b/cli/tests/lsp/selection_range_did_open_notification.json
new file mode 100644
index 000000000..a6b3d9d39
--- /dev/null
+++ b/cli/tests/lsp/selection_range_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": "class Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}"
+ }
+ }
+}
diff --git a/cli/tests/lsp/selection_range_request.json b/cli/tests/lsp/selection_range_request.json
new file mode 100644
index 000000000..5125fa6a0
--- /dev/null
+++ b/cli/tests/lsp/selection_range_request.json
@@ -0,0 +1,16 @@
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "textDocument/selectionRange",
+ "params": {
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "positions": [
+ {
+ "line": 2,
+ "character": 8
+ }
+ ]
+ }
+}
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index c84c2365c..8aba3dca9 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -703,6 +703,15 @@ delete Object.prototype.__proto__;
),
);
}
+ case "getSmartSelectionRange": {
+ return respond(
+ id,
+ languageService.getSmartSelectionRange(
+ request.specifier,
+ request.position,
+ ),
+ );
+ }
case "getSupportedCodeFixes": {
return respond(
id,
diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts
index a3200469c..bdbc89a87 100644
--- a/cli/tsc/compiler.d.ts
+++ b/cli/tsc/compiler.d.ts
@@ -61,6 +61,7 @@ declare global {
| GetQuickInfoRequest
| GetReferencesRequest
| GetSignatureHelpItemsRequest
+ | GetSmartSelectionRange
| GetSupportedCodeFixes;
interface BaseLanguageServerRequest {
@@ -169,6 +170,12 @@ declare global {
options: ts.SignatureHelpItemsOptions;
}
+ interface GetSmartSelectionRange extends BaseLanguageServerRequest {
+ method: "getSmartSelectionRange";
+ specifier: string;
+ position: number;
+ }
+
interface GetSupportedCodeFixes extends BaseLanguageServerRequest {
method: "getSupportedCodeFixes";
}