Implement folding ranges

This commit is contained in:
Jeremy A. Kolb 2018-09-23 11:13:27 -04:00
parent e293a16d6b
commit bd2b2f1b48
2 changed files with 90 additions and 1 deletions

View file

@ -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<Option<Vec<FoldingRange>>> {
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<SyntaxNodeRef<'a>>,
) -> Option<TextRange> {
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,

View file

@ -253,6 +253,7 @@ fn on_request(
.on::<req::DecorationsRequest>(handlers::handle_decorations)?
.on::<req::Completion>(handlers::handle_completion)?
.on::<req::CodeActionRequest>(handlers::handle_code_action)?
.on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
.finish();
match req {
Ok((id, handle)) => {