aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor')
-rw-r--r--crates/ra_editor/src/folding_ranges.rs142
-rw-r--r--crates/ra_editor/src/lib.rs2
2 files changed, 144 insertions, 0 deletions
diff --git a/crates/ra_editor/src/folding_ranges.rs b/crates/ra_editor/src/folding_ranges.rs
new file mode 100644
index 000000000..817da28d1
--- /dev/null
+++ b/crates/ra_editor/src/folding_ranges.rs
@@ -0,0 +1,142 @@
1use std::collections::HashSet;
2
3use ra_syntax::{
4 File, TextRange, SyntaxNodeRef,
5 SyntaxKind,
6 algo::{walk, Direction, siblings},
7};
8
9#[derive(Debug, PartialEq, Eq)]
10pub enum FoldKind {
11 Comment,
12 Imports,
13}
14
15#[derive(Debug)]
16pub struct Fold {
17 pub range: TextRange,
18 pub kind: FoldKind,
19}
20
21pub fn folding_ranges(file: &File) -> Vec<Fold> {
22 let syntax = file.syntax();
23
24 let mut res = vec![];
25 let mut visited = HashSet::new();
26
27 for node in walk::preorder(syntax) {
28 if visited.contains(&node) {
29 continue;
30 }
31
32 let range_and_kind = match node.kind() {
33 SyntaxKind::COMMENT => (
34 contiguous_range_for(SyntaxKind::COMMENT, node, &mut visited),
35 Some(FoldKind::Comment),
36 ),
37 SyntaxKind::USE_ITEM => (
38 contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited),
39 Some(FoldKind::Imports),
40 ),
41 _ => (None, None),
42 };
43
44 match range_and_kind {
45 (Some(range), Some(kind)) => {
46 res.push(Fold {
47 range: range,
48 kind: kind
49 });
50 }
51 _ => {}
52 }
53 }
54
55 res
56}
57
58fn contiguous_range_for<'a>(
59 kind: SyntaxKind,
60 node: SyntaxNodeRef<'a>,
61 visited: &mut HashSet<SyntaxNodeRef<'a>>,
62) -> Option<TextRange> {
63 visited.insert(node);
64
65 let left = node;
66 let mut right = node;
67 for node in siblings(node, Direction::Forward) {
68 visited.insert(node);
69 match node.kind() {
70 SyntaxKind::WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (),
71 k => {
72 if k == kind {
73 right = node
74 } else {
75 break;
76 }
77 }
78 }
79 }
80 if left != right {
81 Some(TextRange::from_to(
82 left.range().start(),
83 right.range().end(),
84 ))
85 } else {
86 None
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_fold_comments() {
96 let text = r#"
97// Hello
98// this is a multiline
99// comment
100//
101
102// But this is not
103
104fn main() {
105 // We should
106 // also
107 // fold
108 // this one.
109}"#;
110
111 let file = File::parse(&text);
112 let folds = folding_ranges(&file);
113 assert_eq!(folds.len(), 2);
114 assert_eq!(folds[0].range.start(), 1.into());
115 assert_eq!(folds[0].range.end(), 46.into());
116 assert_eq!(folds[0].kind, FoldKind::Comment);
117
118 assert_eq!(folds[1].range.start(), 84.into());
119 assert_eq!(folds[1].range.end(), 137.into());
120 assert_eq!(folds[1].kind, FoldKind::Comment);
121 }
122
123 #[test]
124 fn test_fold_imports() {
125 let text = r#"
126use std::str;
127use std::vec;
128use std::io as iop;
129
130fn main() {
131}"#;
132
133 let file = File::parse(&text);
134 let folds = folding_ranges(&file);
135 assert_eq!(folds.len(), 1);
136 assert_eq!(folds[0].range.start(), 1.into());
137 assert_eq!(folds[0].range.end(), 48.into());
138 assert_eq!(folds[0].kind, FoldKind::Imports);
139 }
140
141
142} \ No newline at end of file
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index 78ed34c7c..de929d73a 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -10,6 +10,7 @@ mod extend_selection;
10mod symbols; 10mod symbols;
11mod line_index; 11mod line_index;
12mod edit; 12mod edit;
13mod folding_ranges;
13mod code_actions; 14mod code_actions;
14mod typing; 15mod typing;
15mod completion; 16mod completion;
@@ -36,6 +37,7 @@ pub use self::{
36 }, 37 },
37 typing::{join_lines, on_eq_typed}, 38 typing::{join_lines, on_eq_typed},
38 completion::{scope_completion, CompletionItem}, 39 completion::{scope_completion, CompletionItem},
40 folding_ranges::{Fold, FoldKind, folding_ranges}
39}; 41};
40 42
41#[derive(Debug)] 43#[derive(Debug)]