mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Implement folding ranges
This commit is contained in:
parent
e293a16d6b
commit
bd2b2f1b48
2 changed files with 90 additions and 1 deletions
|
@ -1,15 +1,18 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use languageserver_types::{
|
use languageserver_types::{
|
||||||
Diagnostic, DiagnosticSeverity, DocumentSymbol,
|
Diagnostic, DiagnosticSeverity, DocumentSymbol,
|
||||||
CodeActionResponse, Command, TextDocumentIdentifier,
|
CodeActionResponse, Command, TextDocumentIdentifier,
|
||||||
SymbolInformation, Position, Location, TextEdit,
|
SymbolInformation, Position, Location, TextEdit,
|
||||||
CompletionItem, InsertTextFormat, CompletionItemKind,
|
CompletionItem, InsertTextFormat, CompletionItemKind,
|
||||||
|
FoldingRange, FoldingRangeParams, FoldingRangeKind
|
||||||
};
|
};
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
use ra_analysis::{Query, FileId, RunnableKind, JobToken};
|
use ra_analysis::{Query, FileId, RunnableKind, JobToken};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
algo::{siblings, walk, Direction},
|
||||||
text_utils::contains_offset_nonstrict,
|
text_utils::contains_offset_nonstrict,
|
||||||
|
SyntaxKind, SyntaxNodeRef, TextRange
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
|
@ -177,6 +180,7 @@ pub fn handle_workspace_symbol(
|
||||||
world, &line_index
|
world, &line_index
|
||||||
)?,
|
)?,
|
||||||
container_name: None,
|
container_name: None,
|
||||||
|
deprecated: None,
|
||||||
};
|
};
|
||||||
res.push(info);
|
res.push(info);
|
||||||
};
|
};
|
||||||
|
@ -365,6 +369,90 @@ pub fn handle_completion(
|
||||||
Ok(Some(req::CompletionResponse::Array(items)))
|
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(
|
pub fn handle_code_action(
|
||||||
world: ServerWorld,
|
world: ServerWorld,
|
||||||
params: req::CodeActionParams,
|
params: req::CodeActionParams,
|
||||||
|
|
|
@ -253,6 +253,7 @@ fn on_request(
|
||||||
.on::<req::DecorationsRequest>(handlers::handle_decorations)?
|
.on::<req::DecorationsRequest>(handlers::handle_decorations)?
|
||||||
.on::<req::Completion>(handlers::handle_completion)?
|
.on::<req::Completion>(handlers::handle_completion)?
|
||||||
.on::<req::CodeActionRequest>(handlers::handle_code_action)?
|
.on::<req::CodeActionRequest>(handlers::handle_code_action)?
|
||||||
|
.on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
|
||||||
.finish();
|
.finish();
|
||||||
match req {
|
match req {
|
||||||
Ok((id, handle)) => {
|
Ok((id, handle)) => {
|
||||||
|
|
Loading…
Reference in a new issue