use super::*;
pub fn compute_folding_ranges(text: &str) -> Vec<FoldingRange> {
let root = parse(text).cst;
let line_index = LineIndex::new(text);
let line_of = |offset: TextSize| line_index.byte_to_position(u32::from(offset) as usize).line;
let mut ranges = Vec::new();
for node in root.descendants() {
let Some((open, close)) = delimiter_pair(node.kind()) else {
continue;
};
let tokens = || node.children_with_tokens().filter_map(|el| el.into_token());
let Some(open_tok) = tokens().find(|t| t.kind() == open) else {
continue;
};
let Some(close_tok) = tokens().filter(|t| t.kind() == close).last() else {
continue;
};
let start_line = line_of(open_tok.text_range().start());
let end_line = line_of(close_tok.text_range().end());
if end_line > start_line {
ranges.push(FoldingRange {
start_line,
end_line,
..Default::default()
});
}
}
let mut standalone: Vec<u32> = Vec::new();
let mut code_on_line = false;
for token in root
.descendants_with_tokens()
.filter_map(|el| el.into_token())
{
match token.kind() {
SyntaxKind::NEWLINE => code_on_line = false,
SyntaxKind::WHITESPACE => {}
SyntaxKind::COMMENT => {
if !code_on_line {
standalone.push(line_of(token.text_range().start()));
}
}
_ => code_on_line = true,
}
}
let mut i = 0;
while i < standalone.len() {
let mut j = i + 1;
while j < standalone.len() && standalone[j] == standalone[j - 1] + 1 {
j += 1;
}
if j - i >= 2 {
ranges.push(FoldingRange {
start_line: standalone[i],
end_line: standalone[j - 1],
kind: Some(FoldingRangeKind::Comment),
..Default::default()
});
}
i = j;
}
ranges
}
pub(crate) fn delimiter_pair(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind)> {
Some(match kind {
SyntaxKind::BLOCK_EXPR => (SyntaxKind::LBRACE, SyntaxKind::RBRACE),
SyntaxKind::PAREN_EXPR | SyntaxKind::FUNCTION_EXPR | SyntaxKind::CALL_EXPR => {
(SyntaxKind::LPAREN, SyntaxKind::RPAREN)
}
SyntaxKind::SUBSET_EXPR => (SyntaxKind::LBRACK, SyntaxKind::RBRACK),
SyntaxKind::SUBSET2_EXPR => (SyntaxKind::LBRACK2, SyntaxKind::RBRACK2),
_ => return None,
})
}