aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/folding_ranges.rs
diff options
context:
space:
mode:
authorAdolfo OchagavĂ­a <[email protected]>2018-10-12 18:20:58 +0100
committerAdolfo OchagavĂ­a <[email protected]>2018-10-12 18:20:58 +0100
commit4b3737510b97faa7d2fad3c98aa16eed46334703 (patch)
tree4d052d23ac53bf7444bb2f6235dc7ad4358152b9 /crates/ra_editor/src/folding_ranges.rs
parentee0a6bf0535a5a6c7e536d2cffa11959c3ee2ae3 (diff)
Cleanup fold code and split logic to fold single elements
Diffstat (limited to 'crates/ra_editor/src/folding_ranges.rs')
-rw-r--r--crates/ra_editor/src/folding_ranges.rs107
1 files changed, 69 insertions, 38 deletions
diff --git a/crates/ra_editor/src/folding_ranges.rs b/crates/ra_editor/src/folding_ranges.rs
index 1d89ac853..d45210ec7 100644
--- a/crates/ra_editor/src/folding_ranges.rs
+++ b/crates/ra_editor/src/folding_ranges.rs
@@ -22,65 +22,96 @@ pub struct Fold {
22 22
23pub fn folding_ranges(file: &File) -> Vec<Fold> { 23pub fn folding_ranges(file: &File) -> Vec<Fold> {
24 let mut res = vec![]; 24 let mut res = vec![];
25 let mut visited = FxHashSet::default(); 25 let mut group_members = FxHashSet::default();
26 26
27 for node in file.syntax().descendants() { 27 for node in file.syntax().descendants() {
28 if visited.contains(&node) { 28 // Fold items that span multiple lines
29 continue; 29 if let Some(kind) = fold_kind(node.kind()) {
30 if has_newline(node) {
31 res.push(Fold { range: node.range(), kind });
32 }
30 } 33 }
31 34
32 if let Some(comment) = ast::Comment::cast(node) { 35 // Also fold item *groups* that span multiple lines
33 // Multiline comments (`/* ... */`) can only be folded if they span multiple lines 36
34 let range = if let ast::CommentFlavor::Multiline = comment.flavor() { 37 // Note: we need to skip elements of the group that we have already visited,
35 if comment.text().contains('\n') { 38 // otherwise there will be folds for the whole group and for its sub groups
36 Some(comment.syntax().range()) 39 if group_members.contains(&node) {
37 } else { 40 continue;
38 None
39 }
40 } else {
41 contiguous_range_for(SyntaxKind::COMMENT, node, &mut visited)
42 };
43
44 range.map(|range| res.push(Fold { range, kind: FoldKind::Comment }));
45 } 41 }
46 42
47 if let SyntaxKind::USE_ITEM = node.kind() { 43 if let Some(kind) = fold_kind(node.kind()) {
48 contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited) 44 contiguous_range_for_group(node.kind(), node, &mut group_members)
49 .map(|range| res.push(Fold { range, kind: FoldKind::Imports})); 45 .map(|range| res.push(Fold { range, kind }));
50 }; 46 }
51 } 47 }
52 48
53 res 49 res
54} 50}
55 51
56fn contiguous_range_for<'a>( 52fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
57 kind: SyntaxKind, 53 match kind {
58 node: SyntaxNodeRef<'a>, 54 SyntaxKind::COMMENT => Some(FoldKind::Comment),
55 SyntaxKind::USE_ITEM => Some(FoldKind::Imports),
56 _ => None
57 }
58}
59
60fn has_newline(
61 node: SyntaxNodeRef,
62) -> bool {
63 for descendant in node.descendants() {
64 if let Some(ws) = ast::Whitespace::cast(descendant) {
65 if ws.has_newlines() {
66 return true;
67 }
68 } else if let Some(comment) = ast::Comment::cast(descendant) {
69 if comment.has_newlines() {
70 return true;
71 }
72 }
73 }
74
75 false
76}
77
78fn contiguous_range_for_group<'a>(
79 group_kind: SyntaxKind,
80 first: SyntaxNodeRef<'a>,
59 visited: &mut FxHashSet<SyntaxNodeRef<'a>>, 81 visited: &mut FxHashSet<SyntaxNodeRef<'a>>,
60) -> Option<TextRange> { 82) -> Option<TextRange> {
61 visited.insert(node); 83 visited.insert(first);
84
85 let mut last = first;
62 86
63 let left = node; 87 for node in first.siblings(Direction::Next) {
64 let mut right = node;
65 for node in node.siblings(Direction::Next) {
66 visited.insert(node); 88 visited.insert(node);
67 match node.kind() { 89 if let Some(ws) = ast::Whitespace::cast(node) {
68 SyntaxKind::WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), 90 // There is a blank line, which means the group ends here
69 k => { 91 if ws.count_newlines_lazy().take(2).count() == 2 {
70 if k == kind { 92 break;
71 right = node
72 } else {
73 break;
74 }
75 } 93 }
94
95 // Ignore whitespace without blank lines
96 continue;
97 }
98
99 // The group ends when an element of a different kind is reached
100 if node.kind() != group_kind {
101 break;
76 } 102 }
103
104 // Keep track of the last node in the group
105 last = node;
77 } 106 }
78 if left != right { 107
108 if first != last {
79 Some(TextRange::from_to( 109 Some(TextRange::from_to(
80 left.range().start(), 110 first.range().start(),
81 right.range().end(), 111 last.range().end(),
82 )) 112 ))
83 } else { 113 } else {
114 // The group consists of only one element, therefore it cannot be folded
84 None 115 None
85 } 116 }
86} 117}