summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
authorJean Pierre <jeanp413@hotmail.com>2021-04-02 01:21:07 -0500
committerGitHub <noreply@github.com>2021-04-02 17:21:07 +1100
commit035f7b0ca037aaff37428d339b5f4abc796320fc (patch)
tree835ba72b435a64838cbc36e1343904566de67789 /cli/lsp/language_server.rs
parentf50385b2a5c9bed99709522edc9ab759ce5f07ba (diff)
feat(lsp): implement textDocument/foldingRange (#9900)
Co-authored-by: Kitson Kelly <me@kitsonkelly.com>
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index b55c38189..7b738cc00 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -228,6 +228,25 @@ impl Inner {
maybe_line_index
}
+ // TODO(@kitsonk) we really should find a better way to just return the
+ // content as a `&str`, or be able to get the byte at a particular offset
+ // which is all that this API that is consuming it is trying to do at the
+ // moment
+ /// Searches already cached assets and documents and returns its text
+ /// content. If not found, `None` is returned.
+ fn get_text_content(&self, specifier: &ModuleSpecifier) -> Option<String> {
+ if specifier.scheme() == "asset" {
+ self
+ .assets
+ .get(specifier)
+ .map(|o| o.clone().map(|a| a.text))?
+ } else if self.documents.contains_key(specifier) {
+ self.documents.content(specifier).unwrap()
+ } else {
+ self.sources.get_source(specifier)
+ }
+ }
+
async fn get_navigation_tree(
&mut self,
specifier: &ModuleSpecifier,
@@ -1515,6 +1534,63 @@ impl Inner {
Ok(result)
}
+ async fn folding_range(
+ &self,
+ params: FoldingRangeParams,
+ ) -> LspResult<Option<Vec<FoldingRange>>> {
+ if !self.enabled() {
+ return Ok(None);
+ }
+ let mark = self.performance.mark("folding_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 req = tsc::RequestMethod::GetOutliningSpans(specifier.clone());
+ let outlining_spans: Vec<tsc::OutliningSpan> = self
+ .ts_server
+ .request(self.snapshot(), req)
+ .await
+ .map_err(|err| {
+ error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })?;
+
+ let response = if !outlining_spans.is_empty() {
+ let text_content =
+ self.get_text_content(&specifier).ok_or_else(|| {
+ LspError::invalid_params(format!(
+ "An unexpected specifier ({}) was provided.",
+ specifier
+ ))
+ })?;
+ Some(
+ outlining_spans
+ .iter()
+ .map(|span| {
+ span.to_folding_range(
+ &line_index,
+ text_content.as_str().as_bytes(),
+ self.config.client_capabilities.line_folding_only,
+ )
+ })
+ .collect::<Vec<FoldingRange>>(),
+ )
+ } else {
+ None
+ };
+ self.performance.measure(mark);
+ Ok(response)
+ }
+
async fn rename(
&mut self,
params: RenameParams,
@@ -1840,6 +1916,13 @@ impl lspower::LanguageServer for LanguageServer {
self.0.lock().await.goto_implementation(params).await
}
+ async fn folding_range(
+ &self,
+ params: FoldingRangeParams,
+ ) -> LspResult<Option<Vec<FoldingRange>>> {
+ self.0.lock().await.folding_range(params).await
+ }
+
async fn rename(
&self,
params: RenameParams,
@@ -2426,6 +2509,54 @@ mod tests {
}
#[tokio::test]
+ async fn test_folding_range() {
+ let mut harness = LspTestHarness::new(vec![
+ ("initialize_request.json", LspResponse::RequestAny),
+ ("initialized_notification.json", LspResponse::None),
+ (
+ "folding_range_did_open_notification.json",
+ LspResponse::None,
+ ),
+ (
+ "folding_range_request.json",
+ LspResponse::Request(
+ 2,
+ json!([
+ {
+ "startLine": 0,
+ "endLine": 12,
+ "kind": "region"
+ },
+ {
+ "startLine": 1,
+ "endLine": 3,
+ "kind": "comment"
+ },
+ {
+ "startLine": 4,
+ "endLine": 10
+ },
+ {
+ "startLine": 5,
+ "endLine": 9
+ },
+ {
+ "startLine": 6,
+ "endLine": 7
+ }
+ ]),
+ ),
+ ),
+ (
+ "shutdown_request.json",
+ LspResponse::Request(3, json!(null)),
+ ),
+ ("exit_notification.json", LspResponse::None),
+ ]);
+ harness.run().await;
+ }
+
+ #[tokio::test]
async fn test_rename() {
let mut harness = LspTestHarness::new(vec![
("initialize_request.json", LspResponse::RequestAny),