From bd2b2f1b48f86d59c3b746b72a14192f75419a84 Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Sun, 23 Sep 2018 11:13:27 -0400 Subject: [PATCH] Implement folding ranges --- .../ra_lsp_server/src/main_loop/handlers.rs | 90 ++++++++++++++++++- crates/ra_lsp_server/src/main_loop/mod.rs | 1 + 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 2d9391859e..36cdeda380 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -1,15 +1,18 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use languageserver_types::{ Diagnostic, DiagnosticSeverity, DocumentSymbol, CodeActionResponse, Command, TextDocumentIdentifier, SymbolInformation, Position, Location, TextEdit, CompletionItem, InsertTextFormat, CompletionItemKind, + FoldingRange, FoldingRangeParams, FoldingRangeKind }; use serde_json::to_value; use ra_analysis::{Query, FileId, RunnableKind, JobToken}; use ra_syntax::{ + algo::{siblings, walk, Direction}, text_utils::contains_offset_nonstrict, + SyntaxKind, SyntaxNodeRef, TextRange }; use ::{ @@ -177,6 +180,7 @@ pub fn handle_workspace_symbol( world, &line_index )?, container_name: None, + deprecated: None, }; res.push(info); }; @@ -365,6 +369,90 @@ pub fn handle_completion( Ok(Some(req::CompletionResponse::Array(items))) } +pub fn handle_folding_range( + world: ServerWorld, + params: FoldingRangeParams, + _token: JobToken, +) -> Result>> { + let file_id = params.text_document.try_conv_with(&world)?; + let file = world.analysis().file_syntax(file_id); + let line_index = world.analysis().file_line_index(file_id); + let syntax = file.syntax(); + + let mut res = vec![]; + let mut visited = HashSet::new(); + + for node in walk::preorder(syntax) { + if visited.contains(&node) { + continue; + } + + let range_and_kind = match node.kind() { + SyntaxKind::COMMENT => ( + contiguous_range_for(SyntaxKind::COMMENT, node, &mut visited), + Some(FoldingRangeKind::Comment), + ), + SyntaxKind::USE_ITEM => ( + contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited), + Some(FoldingRangeKind::Imports), + ), + _ => (None, None), + }; + + match range_and_kind { + (Some(range), Some(kind)) => { + let range = range.conv_with(&line_index); + res.push(FoldingRange { + start_line: range.start.line, + start_character: Some(range.start.character), + end_line: range.end.line, + end_character: Some(range.start.character), + kind: Some(kind), + }); + } + _ => {} + } + } + + if res.is_empty() { + Ok(None) + } else { + Ok(Some(res)) + } +} + +fn contiguous_range_for<'a>( + kind: SyntaxKind, + node: SyntaxNodeRef<'a>, + visited: &mut HashSet>, +) -> Option { + visited.insert(node); + + let left = node; + let mut right = node; + for node in siblings(node, Direction::Forward) { + visited.insert(node); + match node.kind() { + SyntaxKind::WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), + k => { + if k == kind { + right = node + } else { + break; + } + } + } + } + if left != right { + Some(TextRange::from_to( + left.range().start(), + right.range().end(), + )) + } else { + None + } +} + pub fn handle_code_action( world: ServerWorld, params: req::CodeActionParams, diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs index 2b2279e977..abc58b70ec 100644 --- a/crates/ra_lsp_server/src/main_loop/mod.rs +++ b/crates/ra_lsp_server/src/main_loop/mod.rs @@ -253,6 +253,7 @@ fn on_request( .on::(handlers::handle_decorations)? .on::(handlers::handle_completion)? .on::(handlers::handle_code_action)? + .on::(handlers::handle_folding_range)? .finish(); match req { Ok((id, handle)) => {