From 47c501bcfbd24356009d1db5b4dbe2e27a148d9a Mon Sep 17 00:00:00 2001 From: Sergey Parilin Date: Fri, 22 Mar 2019 13:29:58 +0300 Subject: Move folding_ranges to ra_ide_api --- crates/ra_ide_api_light/src/folding_ranges.rs | 326 -------------------------- crates/ra_ide_api_light/src/lib.rs | 2 - 2 files changed, 328 deletions(-) delete mode 100644 crates/ra_ide_api_light/src/folding_ranges.rs (limited to 'crates/ra_ide_api_light') diff --git a/crates/ra_ide_api_light/src/folding_ranges.rs b/crates/ra_ide_api_light/src/folding_ranges.rs deleted file mode 100644 index 357a7dee1..000000000 --- a/crates/ra_ide_api_light/src/folding_ranges.rs +++ /dev/null @@ -1,326 +0,0 @@ -use rustc_hash::FxHashSet; - -use ra_syntax::{ - AstNode, Direction, SourceFile, SyntaxNode, TextRange, - SyntaxKind::{self, *}, - ast::{self, VisibilityOwner}, -}; - -#[derive(Debug, PartialEq, Eq)] -pub enum FoldKind { - Comment, - Imports, - Mods, - Block, -} - -#[derive(Debug)] -pub struct Fold { - pub range: TextRange, - pub kind: FoldKind, -} - -pub fn folding_ranges(file: &SourceFile) -> Vec { - let mut res = vec![]; - let mut visited_comments = FxHashSet::default(); - let mut visited_imports = FxHashSet::default(); - let mut visited_mods = FxHashSet::default(); - - for node in file.syntax().descendants() { - // Fold items that span multiple lines - if let Some(kind) = fold_kind(node.kind()) { - if node.text().contains('\n') { - res.push(Fold { range: node.range(), kind }); - } - } - - // Fold groups of comments - if node.kind() == COMMENT && !visited_comments.contains(&node) { - if let Some(range) = contiguous_range_for_comment(node, &mut visited_comments) { - res.push(Fold { range, kind: FoldKind::Comment }) - } - } - - // Fold groups of imports - if node.kind() == USE_ITEM && !visited_imports.contains(&node) { - if let Some(range) = contiguous_range_for_group(node, &mut visited_imports) { - res.push(Fold { range, kind: FoldKind::Imports }) - } - } - - // Fold groups of mods - if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node) { - if let Some(range) = - contiguous_range_for_group_unless(node, has_visibility, &mut visited_mods) - { - res.push(Fold { range, kind: FoldKind::Mods }) - } - } - } - - res -} - -fn fold_kind(kind: SyntaxKind) -> Option { - match kind { - COMMENT => Some(FoldKind::Comment), - USE_ITEM => Some(FoldKind::Imports), - NAMED_FIELD_DEF_LIST | FIELD_PAT_LIST | ITEM_LIST | EXTERN_ITEM_LIST | USE_TREE_LIST - | BLOCK | ENUM_VARIANT_LIST | TOKEN_TREE => Some(FoldKind::Block), - _ => None, - } -} - -fn has_visibility(node: &SyntaxNode) -> bool { - ast::Module::cast(node).and_then(|m| m.visibility()).is_some() -} - -fn contiguous_range_for_group<'a>( - first: &'a SyntaxNode, - visited: &mut FxHashSet<&'a SyntaxNode>, -) -> Option { - contiguous_range_for_group_unless(first, |_| false, visited) -} - -fn contiguous_range_for_group_unless<'a>( - first: &'a SyntaxNode, - unless: impl Fn(&'a SyntaxNode) -> bool, - visited: &mut FxHashSet<&'a SyntaxNode>, -) -> Option { - visited.insert(first); - - let mut last = first; - for node in first.siblings(Direction::Next) { - if let Some(ws) = ast::Whitespace::cast(node) { - // There is a blank line, which means that the group ends here - if ws.count_newlines_lazy().take(2).count() == 2 { - break; - } - - // Ignore whitespace without blank lines - continue; - } - - // Stop if we find a node that doesn't belong to the group - if node.kind() != first.kind() || unless(node) { - break; - } - - visited.insert(node); - last = node; - } - - if first != last { - Some(TextRange::from_to(first.range().start(), last.range().end())) - } else { - // The group consists of only one element, therefore it cannot be folded - None - } -} - -fn contiguous_range_for_comment<'a>( - first: &'a SyntaxNode, - visited: &mut FxHashSet<&'a SyntaxNode>, -) -> Option { - visited.insert(first); - - // Only fold comments of the same flavor - let group_flavor = ast::Comment::cast(first)?.flavor(); - - let mut last = first; - for node in first.siblings(Direction::Next) { - if let Some(ws) = ast::Whitespace::cast(node) { - // There is a blank line, which means the group ends here - if ws.count_newlines_lazy().take(2).count() == 2 { - break; - } - - // Ignore whitespace without blank lines - continue; - } - - match ast::Comment::cast(node) { - Some(next_comment) if next_comment.flavor() == group_flavor => { - visited.insert(node); - last = node; - } - // The comment group ends because either: - // * An element of a different kind was reached - // * A comment of a different flavor was reached - _ => break, - } - } - - if first != last { - Some(TextRange::from_to(first.range().start(), last.range().end())) - } else { - // The group consists of only one element, therefore it cannot be folded - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - use test_utils::extract_ranges; - - fn do_check(text: &str, fold_kinds: &[FoldKind]) { - let (ranges, text) = extract_ranges(text, "fold"); - let file = SourceFile::parse(&text); - let folds = folding_ranges(&file); - - assert_eq!( - folds.len(), - ranges.len(), - "The amount of folds is different than the expected amount" - ); - assert_eq!( - folds.len(), - fold_kinds.len(), - "The amount of fold kinds is different than the expected amount" - ); - for ((fold, range), fold_kind) in - folds.into_iter().zip(ranges.into_iter()).zip(fold_kinds.into_iter()) - { - assert_eq!(fold.range.start(), range.start()); - assert_eq!(fold.range.end(), range.end()); - assert_eq!(&fold.kind, fold_kind); - } - } - - #[test] - fn test_fold_comments() { - let text = r#" -// Hello -// this is a multiline -// comment -// - -// But this is not - -fn main() { - // We should - // also - // fold - // this one. - //! But this one is different - //! because it has another flavor - /* As does this - multiline comment */ -}"#; - - let fold_kinds = &[ - FoldKind::Comment, - FoldKind::Block, - FoldKind::Comment, - FoldKind::Comment, - FoldKind::Comment, - ]; - do_check(text, fold_kinds); - } - - #[test] - fn test_fold_imports() { - let text = r#" -use std::{ - str, - vec, - io as iop -}; - -fn main() { -}"#; - - let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block]; - do_check(text, folds); - } - - #[test] - fn test_fold_mods() { - let text = r#" - -pub mod foo; -mod after_pub; -mod after_pub_next; - -mod before_pub; -mod before_pub_next; -pub mod bar; - -mod not_folding_single; -pub mod foobar; -pub not_folding_single_next; - -#[cfg(test)] -mod with_attribute; -mod with_attribute_next; - -fn main() { -}"#; - - let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block]; - do_check(text, folds); - } - - #[test] - fn test_fold_import_groups() { - let text = r#" -use std::str; -use std::vec; -use std::io as iop; - -use std::mem; -use std::f64; - -use std::collections::HashMap; -// Some random comment -use std::collections::VecDeque; - -fn main() { -}"#; - - let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block]; - do_check(text, folds); - } - - #[test] - fn test_fold_import_and_groups() { - let text = r#" -use std::str; -use std::vec; -use std::io as iop; - -use std::mem; -use std::f64; - -use std::collections::{ - HashMap, - VecDeque, -}; -// Some random comment - -fn main() { -}"#; - - let folds = &[ - FoldKind::Imports, - FoldKind::Imports, - FoldKind::Imports, - FoldKind::Block, - FoldKind::Block, - ]; - do_check(text, folds); - } - - #[test] - fn test_folds_macros() { - let text = r#" -macro_rules! foo { - ($($tt:tt)*) => { $($tt)* } -} -"#; - - let folds = &[FoldKind::Block]; - do_check(text, folds); - } -} diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs index 556d44c25..4036a598e 100644 --- a/crates/ra_ide_api_light/src/lib.rs +++ b/crates/ra_ide_api_light/src/lib.rs @@ -3,7 +3,6 @@ //! This usually means functions which take syntax tree as an input and produce //! an edit or some auxiliary info. -mod folding_ranges; mod structure; #[cfg(test)] mod test_utils; @@ -20,7 +19,6 @@ use ra_syntax::{ }; pub use crate::{ - folding_ranges::{folding_ranges, Fold, FoldKind}, structure::{file_structure, StructureNode}, join_lines::join_lines, typing::{on_enter, on_dot_typed, on_eq_typed}, -- cgit v1.2.3