Split folding ranges into editor and lsp parts

This commit is contained in:
Jeremy A. Kolb 2018-09-24 09:52:33 -04:00
parent bd2b2f1b48
commit ff0a706a30
4 changed files with 114 additions and 77 deletions

View file

@ -34,6 +34,7 @@ use imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp};
pub use ra_editor::{ pub use ra_editor::{
StructureNode, LineIndex, FileSymbol, StructureNode, LineIndex, FileSymbol,
Runnable, RunnableKind, HighlightedRange, CompletionItem, Runnable, RunnableKind, HighlightedRange, CompletionItem,
Fold, FoldKind
}; };
pub use job::{JobToken, JobHandle}; pub use job::{JobToken, JobHandle};
@ -224,6 +225,10 @@ impl Analysis {
pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
self.imp.diagnostics(file_id) self.imp.diagnostics(file_id)
} }
pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
let file = self.imp.file_syntax(file_id);
ra_editor::folding_ranges(&file)
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -0,0 +1,86 @@
use std::collections::HashSet;
use ra_syntax::{
File, TextRange, SyntaxNodeRef,
SyntaxKind,
algo::{walk, Direction, siblings},
};
pub enum FoldKind {
Comment,
Imports,
}
pub struct Fold {
pub range: TextRange,
pub kind: FoldKind,
}
pub fn folding_ranges(file: &File) -> Vec<Fold> {
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(FoldKind::Comment),
),
SyntaxKind::USE_ITEM => (
contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited),
Some(FoldKind::Imports),
),
_ => (None, None),
};
match range_and_kind {
(Some(range), Some(kind)) => {
res.push(Fold {
range: range,
kind: kind
});
}
_ => {}
}
}
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
}
}

View file

@ -10,6 +10,7 @@ mod extend_selection;
mod symbols; mod symbols;
mod line_index; mod line_index;
mod edit; mod edit;
mod folding_ranges;
mod code_actions; mod code_actions;
mod typing; mod typing;
mod completion; mod completion;
@ -36,6 +37,7 @@ pub use self::{
}, },
typing::{join_lines, on_eq_typed}, typing::{join_lines, on_eq_typed},
completion::{scope_completion, CompletionItem}, completion::{scope_completion, CompletionItem},
folding_ranges::{Fold, FoldKind, folding_ranges}
}; };
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap};
use languageserver_types::{ use languageserver_types::{
Diagnostic, DiagnosticSeverity, DocumentSymbol, Diagnostic, DiagnosticSeverity, DocumentSymbol,
@ -8,11 +8,9 @@ use languageserver_types::{
FoldingRange, FoldingRangeParams, FoldingRangeKind 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, FoldKind};
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 ::{
@ -375,82 +373,28 @@ pub fn handle_folding_range(
_token: JobToken, _token: JobToken,
) -> Result<Option<Vec<FoldingRange>>> { ) -> Result<Option<Vec<FoldingRange>>> {
let file_id = params.text_document.try_conv_with(&world)?; 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 line_index = world.analysis().file_line_index(file_id);
let syntax = file.syntax();
let mut res = vec![]; let res = Some(world.analysis()
let mut visited = HashSet::new(); .folding_ranges(file_id)
.into_iter()
for node in walk::preorder(syntax) { .map(|fold| {
if visited.contains(&node) { let kind = match fold.kind {
continue; FoldKind::Comment => FoldingRangeKind::Comment,
} FoldKind::Imports => FoldingRangeKind::Imports
};
let range_and_kind = match node.kind() { let range = fold.range.conv_with(&line_index);
SyntaxKind::COMMENT => ( FoldingRange {
contiguous_range_for(SyntaxKind::COMMENT, node, &mut visited), start_line: range.start.line,
Some(FoldingRangeKind::Comment), start_character: Some(range.start.character),
), end_line: range.end.line,
SyntaxKind::USE_ITEM => ( end_character: Some(range.start.character),
contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited), kind: Some(kind)
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),
});
} }
_ => {} })
} .collect());
}
if res.is_empty() { Ok(res)
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(