diff options
Diffstat (limited to 'crates/libeditor/src')
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 49 | ||||
-rw-r--r-- | crates/libeditor/src/completion.rs | 52 | ||||
-rw-r--r-- | crates/libeditor/src/extend_selection.rs | 55 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 70 | ||||
-rw-r--r-- | crates/libeditor/src/symbols.rs | 42 | ||||
-rw-r--r-- | crates/libeditor/src/test_utils.rs | 20 | ||||
-rw-r--r-- | crates/libeditor/src/typing.rs | 129 |
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)] | ||
133 | mod 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)] | ||
42 | mod 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)] | ||
69 | mod 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#" | ||
97 | impl 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#" | ||
110 | fn 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 @@ | |||
1 | extern crate libsyntax2; | 1 | extern crate libsyntax2; |
2 | extern crate superslice; | 2 | extern crate superslice; |
3 | extern crate itertools; | 3 | extern crate itertools; |
4 | #[cfg(test)] | ||
5 | #[macro_use] | ||
6 | extern crate test_utils as _test_utils; | ||
4 | 7 | ||
5 | mod extend_selection; | 8 | mod extend_selection; |
6 | mod symbols; | 9 | mod symbols; |
@@ -10,6 +13,8 @@ mod code_actions; | |||
10 | mod typing; | 13 | mod typing; |
11 | mod completion; | 14 | mod completion; |
12 | mod scope; | 15 | mod scope; |
16 | #[cfg(test)] | ||
17 | mod test_utils; | ||
13 | 18 | ||
14 | use libsyntax2::{ | 19 | use 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)] | ||
164 | mod 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 | ||
172 | fn 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#" | ||
190 | fn main() {} | ||
191 | |||
192 | #[test] | ||
193 | fn test_foo() {} | ||
194 | |||
195 | #[test] | ||
196 | #[ignore] | ||
197 | fn 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)] | ||
128 | mod 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#" | ||
135 | struct Foo { | ||
136 | x: i32 | ||
137 | } | ||
138 | |||
139 | mod m { | ||
140 | fn bar() {} | ||
141 | } | ||
142 | |||
143 | enum E { X, Y(i32) } | ||
144 | type T = (); | ||
145 | static S: i32 = 92; | ||
146 | const C: i32 = 92; | ||
147 | |||
148 | impl E {} | ||
149 | |||
150 | impl 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 @@ | |||
1 | use libsyntax2::{File, TextUnit}; | ||
2 | pub use _test_utils::*; | ||
3 | use ActionResult; | ||
4 | |||
5 | pub 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)] | ||
168 | mod 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" | ||
183 | fn foo() { | ||
184 | <|>foo(1, | ||
185 | ) | ||
186 | } | ||
187 | ", r" | ||
188 | fn foo() { | ||
189 | <|>foo(1) | ||
190 | } | ||
191 | "); | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_join_lines_lambda_block() { | ||
196 | check_join_lines(r" | ||
197 | pub fn reparse(&self, edit: &AtomEdit) -> File { | ||
198 | <|>self.incremental_reparse(edit).unwrap_or_else(|| { | ||
199 | self.full_reparse(edit) | ||
200 | }) | ||
201 | } | ||
202 | ", r" | ||
203 | pub 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" | ||
212 | fn foo() { | ||
213 | foo(<|>{ | ||
214 | 92 | ||
215 | }) | ||
216 | }", r" | ||
217 | fn 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" | ||
233 | fn foo() { | ||
234 | <|>foo(1, | ||
235 | 2, | ||
236 | 3, | ||
237 | <|>) | ||
238 | } | ||
239 | ", r" | ||
240 | fn foo() { | ||
241 | foo(1, 2, 3) | ||
242 | } | ||
243 | "); | ||
244 | |||
245 | do_check(r" | ||
246 | struct Foo <|>{ | ||
247 | f: u32, | ||
248 | }<|> | ||
249 | ", r" | ||
250 | struct 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" | ||
274 | fn foo() { | ||
275 | let foo =<|> 1 + 1 | ||
276 | } | ||
277 | ", r" | ||
278 | fn 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 | } | ||