Skip to content

Instantly share code, notes, and snippets.

@kjeremy
Created September 21, 2018 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kjeremy/d8a551ec1eb156ae8407577a38378e0c to your computer and use it in GitHub Desktop.
Save kjeremy/d8a551ec1eb156ae8407577a38378e0c to your computer and use it in GitHub Desktop.
// LSP entry point
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 ranges = folds(&file);
if ranges.is_empty() {
info!("Oh no! No ranges!");
return Ok(None);
}
// Now that we have our ranges we need to put them into the response
let mut res = vec![];
info!("Ranges: ");
for range in ranges {
let range = range.conv_with(&line_index);
let folding_range = FoldingRange {
start_line : range.start.line,
start_character : None,
end_line : range.end.line,
end_character : None,
kind : Some(FoldingRangeKind::Comment)
};
info!(" {:?}", folding_range);
res.push(folding_range);
}
Ok(Some(res))
}
// Folds comments only
fn folds(file: &File) -> Vec<TextRange> {
use ra_syntax::SyntaxNodeRef;
use ra_syntax::SyntaxKind;
use ra_syntax::TextRange;
use ra_syntax::algo::{siblings, Direction};
use ra_syntax::algo::walk;
fn comment_range(node: SyntaxNodeRef) -> Option<TextRange> {
let left = node;
let mut right = node;
for node in siblings(node, Direction::Forward) {
match node.kind() {
SyntaxKind::COMMENT => right = node,
SyntaxKind::WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (),
_ => break
}
}
if left != right {
Some(TextRange::from_to(left.range().start(), right.range().end()))
} else {
None
}
}
let syntax = file.syntax();
let mut ranges = vec![];
for node in walk::preorder(syntax) {
if node.kind() == SyntaxKind::COMMENT {
// Check for overlapping ranges
// This is terribly innefficient
if ranges.iter().any(|range : &TextRange| {
node.range().start() >= range.start()
&& node.range().end() <= range.end()
}) {
continue;
}
if let Some(range) = comment_range(node) {
ranges.push(range);
}
}
}
ranges
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment