aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor/src')
-rw-r--r--crates/libeditor/src/code_actions.rs49
-rw-r--r--crates/libeditor/src/completion.rs52
-rw-r--r--crates/libeditor/src/extend_selection.rs55
-rw-r--r--crates/libeditor/src/lib.rs70
-rw-r--r--crates/libeditor/src/symbols.rs42
-rw-r--r--crates/libeditor/src/test_utils.rs20
-rw-r--r--crates/libeditor/src/typing.rs129
7 files changed, 417 insertions, 0 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs
index cd5146d87..ca159c658 100644
--- a/crates/libeditor/src/code_actions.rs
+++ b/crates/libeditor/src/code_actions.rs
@@ -128,3 +128,52 @@ impl PushDisplay for String {
128 write!(self, "{}", item).unwrap() 128 write!(self, "{}", item).unwrap()
129 } 129 }
130} 130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use test_utils::check_action;
136
137 #[test]
138 fn test_swap_comma() {
139 check_action(
140 "fn foo(x: i32,<|> y: Result<(), ()>) {}",
141 "fn foo(y: Result<(), ()>,<|> x: i32) {}",
142 |file, off| flip_comma(file, off).map(|f| f()),
143 )
144 }
145
146 #[test]
147 fn test_add_derive() {
148 check_action(
149 "struct Foo { a: i32, <|>}",
150 "#[derive(<|>)]\nstruct Foo { a: i32, }",
151 |file, off| add_derive(file, off).map(|f| f()),
152 );
153 check_action(
154 "struct Foo { <|> a: i32, }",
155 "#[derive(<|>)]\nstruct Foo { a: i32, }",
156 |file, off| add_derive(file, off).map(|f| f()),
157 );
158 check_action(
159 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
160 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
161 |file, off| add_derive(file, off).map(|f| f()),
162 );
163 }
164
165 #[test]
166 fn test_add_impl() {
167 check_action(
168 "struct Foo {<|>}\n",
169 "struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
170 |file, off| add_impl(file, off).map(|f| f()),
171 );
172 check_action(
173 "struct Foo<T: Clone> {<|>}",
174 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
175 |file, off| add_impl(file, off).map(|f| f()),
176 );
177 }
178
179}
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs
index cea2d14d1..c6ce62661 100644
--- a/crates/libeditor/src/completion.rs
+++ b/crates/libeditor/src/completion.rs
@@ -37,3 +37,55 @@ fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> {
37 }) 37 })
38 .collect() 38 .collect()
39} 39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use test_utils::{assert_eq_dbg, extract_offset};
45
46 fn do_check(code: &str, expected_completions: &str) {
47 let (off, code) = extract_offset(&code);
48 let file = File::parse(&code);
49 let completions = scope_completion(&file, off).unwrap();
50 assert_eq_dbg(expected_completions, &completions);
51 }
52
53 #[test]
54 fn test_completion_let_scope() {
55 do_check(r"
56 fn quux(x: i32) {
57 let y = 92;
58 1 + <|>;
59 let z = ();
60 }
61 ", r#"[CompletionItem { name: "y" },
62 CompletionItem { name: "x" }]"#);
63 }
64
65 #[test]
66 fn test_completion_if_let_scope() {
67 do_check(r"
68 fn quux() {
69 if let Some(x) = foo() {
70 let y = 92;
71 };
72 if let Some(a) = bar() {
73 let b = 62;
74 1 + <|>
75 }
76 }
77 ", r#"[CompletionItem { name: "b" },
78 CompletionItem { name: "a" }]"#);
79 }
80
81 #[test]
82 fn test_completion_for_scope() {
83 do_check(r"
84 fn quux() {
85 for x in &[1, 2, 3] {
86 <|>
87 }
88 }
89 ", r#"[CompletionItem { name: "x" }]"#);
90 }
91}
diff --git a/crates/libeditor/src/extend_selection.rs b/crates/libeditor/src/extend_selection.rs
index 154f89671..30cff6558 100644
--- a/crates/libeditor/src/extend_selection.rs
+++ b/crates/libeditor/src/extend_selection.rs
@@ -64,3 +64,58 @@ fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
64 } 64 }
65 res 65 res
66} 66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use test_utils::extract_offset;
72
73 fn do_check(before: &str, afters: &[&str]) {
74 let (cursor, before) = extract_offset(before);
75 let file = File::parse(&before);
76 let mut range = TextRange::offset_len(cursor, 0.into());
77 for &after in afters {
78 range = extend_selection(&file, range)
79 .unwrap();
80 let actual = &before[range];
81 assert_eq!(after, actual);
82 }
83 }
84
85 #[test]
86 fn test_extend_selection_arith() {
87 do_check(
88 r#"fn foo() { <|>1 + 1 }"#,
89 &["1", "1 + 1", "{ 1 + 1 }"],
90 );
91 }
92
93 #[test]
94 fn test_extend_selection_start_of_the_lind() {
95 do_check(
96 r#"
97impl S {
98<|> fn foo() {
99
100 }
101}"#,
102 &["fn foo() {\n\n }"]
103 );
104 }
105
106 #[test]
107 fn test_extend_selection_comments() {
108 do_check(
109 r#"
110fn bar(){}
111
112// fn foo() {
113// 1 + <|>1
114// }
115
116// fn foo(){}
117 "#,
118 &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"]
119 );
120 }
121}
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index 34056b3c0..b2e2c4782 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -1,6 +1,9 @@
1extern crate libsyntax2; 1extern crate libsyntax2;
2extern crate superslice; 2extern crate superslice;
3extern crate itertools; 3extern crate itertools;
4#[cfg(test)]
5#[macro_use]
6extern crate test_utils as _test_utils;
4 7
5mod extend_selection; 8mod extend_selection;
6mod symbols; 9mod symbols;
@@ -10,6 +13,8 @@ mod code_actions;
10mod typing; 13mod typing;
11mod completion; 14mod completion;
12mod scope; 15mod scope;
16#[cfg(test)]
17mod test_utils;
13 18
14use libsyntax2::{ 19use libsyntax2::{
15 File, TextUnit, TextRange, SyntaxNodeRef, 20 File, TextUnit, TextRange, SyntaxNodeRef,
@@ -154,3 +159,68 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>(
154 .filter_map(N::cast) 159 .filter_map(N::cast)
155 .next() 160 .next()
156} 161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use test_utils::{assert_eq_dbg, extract_offset, add_cursor};
167
168 #[test]
169 fn test_highlighting() {
170 let file = File::parse(r#"
171// comment
172fn main() {}
173 println!("Hello, {}!", 92);
174"#);
175 let hls = highlight(&file);
176 assert_eq_dbg(
177 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
178 HighlightedRange { range: [12; 14), tag: "keyword" },
179 HighlightedRange { range: [15; 19), tag: "function" },
180 HighlightedRange { range: [29; 36), tag: "text" },
181 HighlightedRange { range: [38; 50), tag: "string" },
182 HighlightedRange { range: [52; 54), tag: "literal" }]"#,
183 &hls,
184 );
185 }
186
187 #[test]
188 fn test_runnables() {
189 let file = File::parse(r#"
190fn main() {}
191
192#[test]
193fn test_foo() {}
194
195#[test]
196#[ignore]
197fn test_foo() {}
198"#);
199 let runnables = runnables(&file);
200 assert_eq_dbg(
201 r#"[Runnable { range: [1; 13), kind: Bin },
202 Runnable { range: [15; 39), kind: Test { name: "test_foo" } },
203 Runnable { range: [41; 75), kind: Test { name: "test_foo" } }]"#,
204 &runnables,
205 )
206 }
207
208 #[test]
209 fn test_matching_brace() {
210 fn do_check(before: &str, after: &str) {
211 let (pos, before) = extract_offset(before);
212 let file = File::parse(&before);
213 let new_pos = match matching_brace(&file, pos) {
214 None => pos,
215 Some(pos) => pos,
216 };
217 let actual = add_cursor(&before, new_pos);
218 assert_eq_text!(after, &actual);
219 }
220
221 do_check(
222 "struct Foo { a: i32, }<|>",
223 "struct Foo <|>{ a: i32, }",
224 );
225 }
226}
diff --git a/crates/libeditor/src/symbols.rs b/crates/libeditor/src/symbols.rs
index 98a35dcdf..28b86c004 100644
--- a/crates/libeditor/src/symbols.rs
+++ b/crates/libeditor/src/symbols.rs
@@ -123,3 +123,45 @@ fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
123 }) 123 })
124 .accept(node)? 124 .accept(node)?
125} 125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use test_utils::assert_eq_dbg;
131
132 #[test]
133 fn test_file_structure() {
134 let file = File::parse(r#"
135struct Foo {
136 x: i32
137}
138
139mod m {
140 fn bar() {}
141}
142
143enum E { X, Y(i32) }
144type T = ();
145static S: i32 = 92;
146const C: i32 = 92;
147
148impl E {}
149
150impl fmt::Debug for E {}
151"#);
152 let symbols = file_structure(&file);
153 assert_eq_dbg(
154 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
155 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
156 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
157 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
158 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
159 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
160 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
161 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
162 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
163 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
164 &symbols,
165 )
166 }
167}
diff --git a/crates/libeditor/src/test_utils.rs b/crates/libeditor/src/test_utils.rs
new file mode 100644
index 000000000..475f560fa
--- /dev/null
+++ b/crates/libeditor/src/test_utils.rs
@@ -0,0 +1,20 @@
1use libsyntax2::{File, TextUnit};
2pub use _test_utils::*;
3use ActionResult;
4
5pub fn check_action<F: Fn(&File, TextUnit) -> Option<ActionResult>> (
6 before: &str,
7 after: &str,
8 f: F,
9) {
10 let (before_cursor_pos, before) = extract_offset(before);
11 let file = File::parse(&before);
12 let result = f(&file, before_cursor_pos).expect("code action is not applicable");
13 let actual = result.edit.apply(&before);
14 let actual_cursor_pos = match result.cursor_position {
15 None => result.edit.apply_to_offset(before_cursor_pos).unwrap(),
16 Some(off) => off,
17 };
18 let actual = add_cursor(&actual, actual_cursor_pos);
19 assert_eq_text!(after, &actual);
20}
diff --git a/crates/libeditor/src/typing.rs b/crates/libeditor/src/typing.rs
index daa57983f..952caf7f6 100644
--- a/crates/libeditor/src/typing.rs
+++ b/crates/libeditor/src/typing.rs
@@ -163,3 +163,132 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str {
163 } 163 }
164 " " 164 " "
165} 165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use test_utils::{check_action, extract_range, extract_offset};
171
172 fn check_join_lines(before: &str, after: &str) {
173 check_action(before, after, |file, offset| {
174 let range = TextRange::offset_len(offset, 0.into());
175 let res = join_lines(file, range);
176 Some(res)
177 })
178 }
179
180 #[test]
181 fn test_join_lines_comma() {
182 check_join_lines(r"
183fn foo() {
184 <|>foo(1,
185 )
186}
187", r"
188fn foo() {
189 <|>foo(1)
190}
191");
192 }
193
194 #[test]
195 fn test_join_lines_lambda_block() {
196 check_join_lines(r"
197pub fn reparse(&self, edit: &AtomEdit) -> File {
198 <|>self.incremental_reparse(edit).unwrap_or_else(|| {
199 self.full_reparse(edit)
200 })
201}
202", r"
203pub fn reparse(&self, edit: &AtomEdit) -> File {
204 <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
205}
206");
207 }
208
209 #[test]
210 fn test_join_lines_block() {
211 check_join_lines(r"
212fn foo() {
213 foo(<|>{
214 92
215 })
216}", r"
217fn foo() {
218 foo(<|>92)
219}");
220 }
221
222 #[test]
223 fn test_join_lines_selection() {
224 fn do_check(before: &str, after: &str) {
225 let (sel, before) = extract_range(before);
226 let file = File::parse(&before);
227 let result = join_lines(&file, sel);
228 let actual = result.edit.apply(&before);
229 assert_eq_text!(after, &actual);
230 }
231
232 do_check(r"
233fn foo() {
234 <|>foo(1,
235 2,
236 3,
237 <|>)
238}
239 ", r"
240fn foo() {
241 foo(1, 2, 3)
242}
243 ");
244
245 do_check(r"
246struct Foo <|>{
247 f: u32,
248}<|>
249 ", r"
250struct Foo { f: u32 }
251 ");
252 }
253
254 #[test]
255 fn test_on_eq_typed() {
256 fn do_check(before: &str, after: &str) {
257 let (offset, before) = extract_offset(before);
258 let file = File::parse(&before);
259 let result = on_eq_typed(&file, offset).unwrap();
260 let actual = result.edit.apply(&before);
261 assert_eq_text!(after, &actual);
262 }
263
264 // do_check(r"
265 // fn foo() {
266 // let foo =<|>
267 // }
268 // ", r"
269 // fn foo() {
270 // let foo =;
271 // }
272 // ");
273 do_check(r"
274fn foo() {
275 let foo =<|> 1 + 1
276}
277", r"
278fn foo() {
279 let foo = 1 + 1;
280}
281");
282 // do_check(r"
283 // fn foo() {
284 // let foo =<|>
285 // let bar = 1;
286 // }
287 // ", r"
288 // fn foo() {
289 // let foo =;
290 // let bar = 1;
291 // }
292 // ");
293 }
294}