1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

feat(lsp): implement textDocument/selectionRange (#9845)

Ref: #8643
This commit is contained in:
Jean Pierre 2021-03-23 18:33:25 -05:00 committed by Kitson Kelly
parent 97dc2918fd
commit 424c086a2b
No known key found for this signature in database
GPG key ID: 2D87CFF11B51960A
7 changed files with 240 additions and 1 deletions

View file

@ -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,

View file

@ -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,
@ -2411,6 +2460,114 @@ mod tests {
harness.run().await;
}
#[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![

View file

@ -1311,6 +1311,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,
@ -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",

View file

@ -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}"
}
}
}

View file

@ -0,0 +1,16 @@
{
"jsonrpc": "2.0",
"id": 2,
"method": "textDocument/selectionRange",
"params": {
"textDocument": {
"uri": "file:///a/file.ts"
},
"positions": [
{
"line": 2,
"character": 8
}
]
}
}

View file

@ -703,6 +703,15 @@ delete Object.prototype.__proto__;
),
);
}
case "getSmartSelectionRange": {
return respond(
id,
languageService.getSmartSelectionRange(
request.specifier,
request.position,
),
);
}
case "getSupportedCodeFixes": {
return respond(
id,

View file

@ -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";
}