diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-16 14:44:24 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-16 14:44:24 +0100 |
commit | 1216878f7be20dd0e652fb8cdc395009fdcfae07 (patch) | |
tree | 6551967cc8c6e921b66071453ad7888a9121d326 /crates/ra_editor | |
parent | 39cb6c6d3f78b193f5873c3492e530bbd24d5dd2 (diff) | |
parent | 61f3a438d3a729a6be941bca1ff4c6a97a33f221 (diff) |
Merge #134
134: Cargo Format run r=kjeremy a=kjeremy
I'm not sure how appreciated this is but I figured I would run `cargo fmt` and see what came up.
I made sure that `cargo test` still passes.
Co-authored-by: Jeremy A. Kolb <[email protected]>
Diffstat (limited to 'crates/ra_editor')
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 49 | ||||
-rw-r--r-- | crates/ra_editor/src/completion.rs | 178 | ||||
-rw-r--r-- | crates/ra_editor/src/edit.rs | 13 | ||||
-rw-r--r-- | crates/ra_editor/src/extend_selection.rs | 58 | ||||
-rw-r--r-- | crates/ra_editor/src/folding_ranges.rs | 36 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 103 | ||||
-rw-r--r-- | crates/ra_editor/src/line_index.rs | 119 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/fn_scope.rs | 150 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/mod.rs | 3 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/mod_scope.rs | 47 | ||||
-rw-r--r-- | crates/ra_editor/src/symbols.rs | 34 | ||||
-rw-r--r-- | crates/ra_editor/src/test_utils.rs | 12 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 205 |
13 files changed, 612 insertions, 395 deletions
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs index 7b0a48c81..cadcd2720 100644 --- a/crates/ra_editor/src/code_actions.rs +++ b/crates/ra_editor/src/code_actions.rs | |||
@@ -1,17 +1,14 @@ | |||
1 | use join_to_string::join; | 1 | use join_to_string::join; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | File, TextUnit, TextRange, Direction, | 4 | algo::{find_covering_node, find_leaf_at_offset}, |
5 | ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, | 5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, |
6 | Direction, File, | ||
6 | SyntaxKind::{COMMA, WHITESPACE}, | 7 | SyntaxKind::{COMMA, WHITESPACE}, |
7 | SyntaxNodeRef, | 8 | SyntaxNodeRef, TextRange, TextUnit, |
8 | algo::{ | ||
9 | find_leaf_at_offset, | ||
10 | find_covering_node, | ||
11 | }, | ||
12 | }; | 9 | }; |
13 | 10 | ||
14 | use crate::{EditBuilder, Edit, find_node_at_offset}; | 11 | use crate::{find_node_at_offset, Edit, EditBuilder}; |
15 | 12 | ||
16 | #[derive(Debug)] | 13 | #[derive(Debug)] |
17 | pub struct LocalEdit { | 14 | pub struct LocalEdit { |
@@ -52,9 +49,7 @@ pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() | |||
52 | edit.insert(node_start, "#[derive()]\n".to_string()); | 49 | edit.insert(node_start, "#[derive()]\n".to_string()); |
53 | node_start + TextUnit::of_str("#[derive(") | 50 | node_start + TextUnit::of_str("#[derive(") |
54 | } | 51 | } |
55 | Some(tt) => { | 52 | Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), |
56 | tt.syntax().range().end() - TextUnit::of_char(')') | ||
57 | } | ||
58 | }; | 53 | }; |
59 | LocalEdit { | 54 | LocalEdit { |
60 | edit: edit.finish(), | 55 | edit: edit.finish(), |
@@ -74,14 +69,19 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> | |||
74 | let mut buf = String::new(); | 69 | let mut buf = String::new(); |
75 | buf.push_str("\n\nimpl"); | 70 | buf.push_str("\n\nimpl"); |
76 | if let Some(type_params) = type_params { | 71 | if let Some(type_params) = type_params { |
77 | type_params.syntax().text() | 72 | type_params.syntax().text().push_to(&mut buf); |
78 | .push_to(&mut buf); | ||
79 | } | 73 | } |
80 | buf.push_str(" "); | 74 | buf.push_str(" "); |
81 | buf.push_str(name.text().as_str()); | 75 | buf.push_str(name.text().as_str()); |
82 | if let Some(type_params) = type_params { | 76 | if let Some(type_params) = type_params { |
83 | let lifetime_params = type_params.lifetime_params().filter_map(|it| it.lifetime()).map(|it| it.text()); | 77 | let lifetime_params = type_params |
84 | let type_params = type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()); | 78 | .lifetime_params() |
79 | .filter_map(|it| it.lifetime()) | ||
80 | .map(|it| it.text()); | ||
81 | let type_params = type_params | ||
82 | .type_params() | ||
83 | .filter_map(|it| it.name()) | ||
84 | .map(|it| it.text()); | ||
85 | join(lifetime_params.chain(type_params)) | 85 | join(lifetime_params.chain(type_params)) |
86 | .surround_with("<", ">") | 86 | .surround_with("<", ">") |
87 | .to_buf(&mut buf); | 87 | .to_buf(&mut buf); |
@@ -97,10 +97,17 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> | |||
97 | }) | 97 | }) |
98 | } | 98 | } |
99 | 99 | ||
100 | pub fn introduce_variable<'a>(file: &'a File, range: TextRange) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 100 | pub fn introduce_variable<'a>( |
101 | file: &'a File, | ||
102 | range: TextRange, | ||
103 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
101 | let node = find_covering_node(file.syntax(), range); | 104 | let node = find_covering_node(file.syntax(), range); |
102 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 105 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; |
103 | let anchor_stmt = expr.syntax().ancestors().filter_map(ast::Stmt::cast).next()?; | 106 | let anchor_stmt = expr |
107 | .syntax() | ||
108 | .ancestors() | ||
109 | .filter_map(ast::Stmt::cast) | ||
110 | .next()?; | ||
104 | let indent = anchor_stmt.syntax().prev_sibling()?; | 111 | let indent = anchor_stmt.syntax().prev_sibling()?; |
105 | if indent.kind() != WHITESPACE { | 112 | if indent.kind() != WHITESPACE { |
106 | return None; | 113 | return None; |
@@ -191,7 +198,8 @@ mod tests { | |||
191 | " | 198 | " |
192 | fn foo() { | 199 | fn foo() { |
193 | foo(<|>1 + 1<|>); | 200 | foo(<|>1 + 1<|>); |
194 | }", " | 201 | }", |
202 | " | ||
195 | fn foo() { | 203 | fn foo() { |
196 | let <|>var_name = 1 + 1; | 204 | let <|>var_name = 1 + 1; |
197 | foo(var_name); | 205 | foo(var_name); |
@@ -201,11 +209,12 @@ fn foo() { | |||
201 | } | 209 | } |
202 | #[test] | 210 | #[test] |
203 | fn test_intrdoduce_var_expr_stmt() { | 211 | fn test_intrdoduce_var_expr_stmt() { |
204 | check_action_range( | 212 | check_action_range( |
205 | " | 213 | " |
206 | fn foo() { | 214 | fn foo() { |
207 | <|>1 + 1<|>; | 215 | <|>1 + 1<|>; |
208 | }", " | 216 | }", |
217 | " | ||
209 | fn foo() { | 218 | fn foo() { |
210 | let <|>var_name = 1 + 1; | 219 | let <|>var_name = 1 + 1; |
211 | }", | 220 | }", |
diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs index b6095dca9..86ef46ebd 100644 --- a/crates/ra_editor/src/completion.rs +++ b/crates/ra_editor/src/completion.rs | |||
@@ -1,17 +1,18 @@ | |||
1 | use rustc_hash::{FxHashMap, FxHashSet}; | 1 | use rustc_hash::{FxHashMap, FxHashSet}; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*, | 4 | algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx}, |
5 | ast::{self, LoopBodyOwner, ModuleItemOwner}, | 5 | ast::{self, LoopBodyOwner, ModuleItemOwner}, |
6 | algo::{ | ||
7 | visit::{visitor, Visitor, visitor_ctx, VisitorCtx}, | ||
8 | }, | ||
9 | text_utils::is_subrange, | 6 | text_utils::is_subrange, |
7 | AstNode, File, | ||
8 | SyntaxKind::*, | ||
9 | SyntaxNodeRef, TextUnit, | ||
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | AtomEdit, find_node_at_offset, | 13 | find_node_at_offset, |
14 | scope::{FnScopes, ModuleScope}, | 14 | scope::{FnScopes, ModuleScope}, |
15 | AtomEdit, | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | #[derive(Debug)] | 18 | #[derive(Debug)] |
@@ -21,7 +22,7 @@ pub struct CompletionItem { | |||
21 | /// What string is used for filtering, defaults to label | 22 | /// What string is used for filtering, defaults to label |
22 | pub lookup: Option<String>, | 23 | pub lookup: Option<String>, |
23 | /// What is inserted, defaults to label | 24 | /// What is inserted, defaults to label |
24 | pub snippet: Option<String> | 25 | pub snippet: Option<String>, |
25 | } | 26 | } |
26 | 27 | ||
27 | pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> { | 28 | pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> { |
@@ -40,7 +41,12 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI | |||
40 | param_completions(name_ref.syntax(), &mut res); | 41 | param_completions(name_ref.syntax(), &mut res); |
41 | } | 42 | } |
42 | let name_range = name_ref.syntax().range(); | 43 | let name_range = name_ref.syntax().range(); |
43 | let top_node = name_ref.syntax().ancestors().take_while(|it| it.range() == name_range).last().unwrap(); | 44 | let top_node = name_ref |
45 | .syntax() | ||
46 | .ancestors() | ||
47 | .take_while(|it| it.range() == name_range) | ||
48 | .last() | ||
49 | .unwrap(); | ||
44 | match top_node.parent().map(|it| it.kind()) { | 50 | match top_node.parent().map(|it| it.kind()) { |
45 | Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res), | 51 | Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res), |
46 | _ => (), | 52 | _ => (), |
@@ -68,21 +74,23 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<Completi | |||
68 | if let Some(items) = visitor() | 74 | if let Some(items) = visitor() |
69 | .visit::<ast::Root, _>(|it| Some(it.items())) | 75 | .visit::<ast::Root, _>(|it| Some(it.items())) |
70 | .visit::<ast::Module, _>(|it| Some(it.item_list()?.items())) | 76 | .visit::<ast::Module, _>(|it| Some(it.item_list()?.items())) |
71 | .accept(node) { | 77 | .accept(node) |
78 | { | ||
72 | if let Some(items) = items { | 79 | if let Some(items) = items { |
73 | let scope = ModuleScope::new(items); | 80 | let scope = ModuleScope::new(items); |
74 | acc.extend( | 81 | acc.extend( |
75 | scope.entries().iter() | 82 | scope |
83 | .entries() | ||
84 | .iter() | ||
76 | .filter(|entry| entry.syntax() != name_ref.syntax()) | 85 | .filter(|entry| entry.syntax() != name_ref.syntax()) |
77 | .map(|entry| CompletionItem { | 86 | .map(|entry| CompletionItem { |
78 | label: entry.name().to_string(), | 87 | label: entry.name().to_string(), |
79 | lookup: None, | 88 | lookup: None, |
80 | snippet: None, | 89 | snippet: None, |
81 | }) | 90 | }), |
82 | ); | 91 | ); |
83 | } | 92 | } |
84 | break; | 93 | break; |
85 | |||
86 | } else if !visited_fn { | 94 | } else if !visited_fn { |
87 | if let Some(fn_def) = ast::FnDef::cast(node) { | 95 | if let Some(fn_def) = ast::FnDef::cast(node) { |
88 | visited_fn = true; | 96 | visited_fn = true; |
@@ -103,26 +111,34 @@ fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) { | |||
103 | .visit::<ast::ItemList, _>(process) | 111 | .visit::<ast::ItemList, _>(process) |
104 | .accept(node); | 112 | .accept(node); |
105 | } | 113 | } |
106 | params.into_iter() | 114 | params |
115 | .into_iter() | ||
107 | .filter_map(|(label, (count, param))| { | 116 | .filter_map(|(label, (count, param))| { |
108 | let lookup = param.pat()?.syntax().text().to_string(); | 117 | let lookup = param.pat()?.syntax().text().to_string(); |
109 | if count < 2 { None } else { Some((label, lookup)) } | 118 | if count < 2 { |
119 | None | ||
120 | } else { | ||
121 | Some((label, lookup)) | ||
122 | } | ||
110 | }) | 123 | }) |
111 | .for_each(|(label, lookup)| { | 124 | .for_each(|(label, lookup)| { |
112 | acc.push(CompletionItem { | 125 | acc.push(CompletionItem { |
113 | label, lookup: Some(lookup), snippet: None | 126 | label, |
127 | lookup: Some(lookup), | ||
128 | snippet: None, | ||
114 | }) | 129 | }) |
115 | }); | 130 | }); |
116 | 131 | ||
117 | fn process<'a, N: ast::FnDefOwner<'a>>(node: N, params: &mut FxHashMap<String, (u32, ast::Param<'a>)>) { | 132 | fn process<'a, N: ast::FnDefOwner<'a>>( |
133 | node: N, | ||
134 | params: &mut FxHashMap<String, (u32, ast::Param<'a>)>, | ||
135 | ) { | ||
118 | node.functions() | 136 | node.functions() |
119 | .filter_map(|it| it.param_list()) | 137 | .filter_map(|it| it.param_list()) |
120 | .flat_map(|it| it.params()) | 138 | .flat_map(|it| it.params()) |
121 | .for_each(|param| { | 139 | .for_each(|param| { |
122 | let text = param.syntax().text().to_string(); | 140 | let text = param.syntax().text().to_string(); |
123 | params.entry(text) | 141 | params.entry(text).or_insert((0, param)).0 += 1; |
124 | .or_insert((0, param)) | ||
125 | .0 += 1; | ||
126 | }) | 142 | }) |
127 | } | 143 | } |
128 | } | 144 | } |
@@ -134,8 +150,12 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { | |||
134 | } | 150 | } |
135 | } | 151 | } |
136 | 152 | ||
137 | 153 | fn complete_expr_keywords( | |
138 | fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { | 154 | file: &File, |
155 | fn_def: ast::FnDef, | ||
156 | name_ref: ast::NameRef, | ||
157 | acc: &mut Vec<CompletionItem>, | ||
158 | ) { | ||
139 | acc.push(keyword("if", "if $0 {}")); | 159 | acc.push(keyword("if", "if $0 {}")); |
140 | acc.push(keyword("match", "match $0 {}")); | 160 | acc.push(keyword("match", "match $0 {}")); |
141 | acc.push(keyword("while", "while $0 {}")); | 161 | acc.push(keyword("while", "while $0 {}")); |
@@ -186,9 +206,14 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet | |||
186 | // return None; | 206 | // return None; |
187 | // } | 207 | // } |
188 | 208 | ||
189 | let is_stmt = match name_ref.syntax().ancestors().filter_map(ast::ExprStmt::cast).next() { | 209 | let is_stmt = match name_ref |
210 | .syntax() | ||
211 | .ancestors() | ||
212 | .filter_map(ast::ExprStmt::cast) | ||
213 | .next() | ||
214 | { | ||
190 | None => false, | 215 | None => false, |
191 | Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range() | 216 | Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), |
192 | }; | 217 | }; |
193 | let snip = match (is_stmt, fn_def.ret_type().is_some()) { | 218 | let snip = match (is_stmt, fn_def.ret_type().is_some()) { |
194 | (true, true) => "return $0;", | 219 | (true, true) => "return $0;", |
@@ -209,39 +234,37 @@ fn keyword(kw: &str, snip: &str) -> CompletionItem { | |||
209 | 234 | ||
210 | fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { | 235 | fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { |
211 | acc.push(CompletionItem { | 236 | acc.push(CompletionItem { |
212 | label: "pd".to_string(), | 237 | label: "pd".to_string(), |
213 | lookup: None, | 238 | lookup: None, |
214 | snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()), | 239 | snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()), |
215 | } | 240 | }); |
216 | ); | ||
217 | acc.push(CompletionItem { | 241 | acc.push(CompletionItem { |
218 | label: "ppd".to_string(), | 242 | label: "ppd".to_string(), |
219 | lookup: None, | 243 | lookup: None, |
220 | snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), | 244 | snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), |
221 | } | 245 | }); |
222 | ); | ||
223 | } | 246 | } |
224 | 247 | ||
225 | fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { | 248 | fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { |
226 | acc.push(CompletionItem { | 249 | acc.push(CompletionItem { |
227 | label: "tfn".to_string(), | 250 | label: "tfn".to_string(), |
228 | lookup: None, | 251 | lookup: None, |
229 | snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()), | 252 | snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()), |
230 | } | 253 | }); |
231 | ); | ||
232 | } | 254 | } |
233 | 255 | ||
234 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { | 256 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { |
235 | let mut shadowed = FxHashSet::default(); | 257 | let mut shadowed = FxHashSet::default(); |
236 | acc.extend( | 258 | acc.extend( |
237 | scopes.scope_chain(name_ref.syntax()) | 259 | scopes |
260 | .scope_chain(name_ref.syntax()) | ||
238 | .flat_map(|scope| scopes.entries(scope).iter()) | 261 | .flat_map(|scope| scopes.entries(scope).iter()) |
239 | .filter(|entry| shadowed.insert(entry.name())) | 262 | .filter(|entry| shadowed.insert(entry.name())) |
240 | .map(|entry| CompletionItem { | 263 | .map(|entry| CompletionItem { |
241 | label: entry.name().to_string(), | 264 | label: entry.name().to_string(), |
242 | lookup: None, | 265 | lookup: None, |
243 | snippet: None, | 266 | snippet: None, |
244 | }) | 267 | }), |
245 | ); | 268 | ); |
246 | if scopes.self_param.is_some() { | 269 | if scopes.self_param.is_some() { |
247 | acc.push(CompletionItem { | 270 | acc.push(CompletionItem { |
@@ -281,20 +304,24 @@ mod tests { | |||
281 | 304 | ||
282 | #[test] | 305 | #[test] |
283 | fn test_completion_let_scope() { | 306 | fn test_completion_let_scope() { |
284 | check_scope_completion(r" | 307 | check_scope_completion( |
308 | r" | ||
285 | fn quux(x: i32) { | 309 | fn quux(x: i32) { |
286 | let y = 92; | 310 | let y = 92; |
287 | 1 + <|>; | 311 | 1 + <|>; |
288 | let z = (); | 312 | let z = (); |
289 | } | 313 | } |
290 | ", r#"[CompletionItem { label: "y", lookup: None, snippet: None }, | 314 | ", |
315 | r#"[CompletionItem { label: "y", lookup: None, snippet: None }, | ||
291 | CompletionItem { label: "x", lookup: None, snippet: None }, | 316 | CompletionItem { label: "x", lookup: None, snippet: None }, |
292 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#); | 317 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, |
318 | ); | ||
293 | } | 319 | } |
294 | 320 | ||
295 | #[test] | 321 | #[test] |
296 | fn test_completion_if_let_scope() { | 322 | fn test_completion_if_let_scope() { |
297 | check_scope_completion(r" | 323 | check_scope_completion( |
324 | r" | ||
298 | fn quux() { | 325 | fn quux() { |
299 | if let Some(x) = foo() { | 326 | if let Some(x) = foo() { |
300 | let y = 92; | 327 | let y = 92; |
@@ -304,67 +331,85 @@ mod tests { | |||
304 | 1 + <|> | 331 | 1 + <|> |
305 | } | 332 | } |
306 | } | 333 | } |
307 | ", r#"[CompletionItem { label: "b", lookup: None, snippet: None }, | 334 | ", |
335 | r#"[CompletionItem { label: "b", lookup: None, snippet: None }, | ||
308 | CompletionItem { label: "a", lookup: None, snippet: None }, | 336 | CompletionItem { label: "a", lookup: None, snippet: None }, |
309 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#); | 337 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, |
338 | ); | ||
310 | } | 339 | } |
311 | 340 | ||
312 | #[test] | 341 | #[test] |
313 | fn test_completion_for_scope() { | 342 | fn test_completion_for_scope() { |
314 | check_scope_completion(r" | 343 | check_scope_completion( |
344 | r" | ||
315 | fn quux() { | 345 | fn quux() { |
316 | for x in &[1, 2, 3] { | 346 | for x in &[1, 2, 3] { |
317 | <|> | 347 | <|> |
318 | } | 348 | } |
319 | } | 349 | } |
320 | ", r#"[CompletionItem { label: "x", lookup: None, snippet: None }, | 350 | ", |
321 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#); | 351 | r#"[CompletionItem { label: "x", lookup: None, snippet: None }, |
352 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | ||
353 | ); | ||
322 | } | 354 | } |
323 | 355 | ||
324 | #[test] | 356 | #[test] |
325 | fn test_completion_mod_scope() { | 357 | fn test_completion_mod_scope() { |
326 | check_scope_completion(r" | 358 | check_scope_completion( |
359 | r" | ||
327 | struct Foo; | 360 | struct Foo; |
328 | enum Baz {} | 361 | enum Baz {} |
329 | fn quux() { | 362 | fn quux() { |
330 | <|> | 363 | <|> |
331 | } | 364 | } |
332 | ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, | 365 | ", |
366 | r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, | ||
333 | CompletionItem { label: "Baz", lookup: None, snippet: None }, | 367 | CompletionItem { label: "Baz", lookup: None, snippet: None }, |
334 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#); | 368 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, |
369 | ); | ||
335 | } | 370 | } |
336 | 371 | ||
337 | #[test] | 372 | #[test] |
338 | fn test_completion_mod_scope_no_self_use() { | 373 | fn test_completion_mod_scope_no_self_use() { |
339 | check_scope_completion(r" | 374 | check_scope_completion( |
375 | r" | ||
340 | use foo<|>; | 376 | use foo<|>; |
341 | ", r#"[]"#); | 377 | ", |
378 | r#"[]"#, | ||
379 | ); | ||
342 | } | 380 | } |
343 | 381 | ||
344 | #[test] | 382 | #[test] |
345 | fn test_completion_mod_scope_nested() { | 383 | fn test_completion_mod_scope_nested() { |
346 | check_scope_completion(r" | 384 | check_scope_completion( |
385 | r" | ||
347 | struct Foo; | 386 | struct Foo; |
348 | mod m { | 387 | mod m { |
349 | struct Bar; | 388 | struct Bar; |
350 | fn quux() { <|> } | 389 | fn quux() { <|> } |
351 | } | 390 | } |
352 | ", r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, | 391 | ", |
353 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#); | 392 | r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, |
393 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | ||
394 | ); | ||
354 | } | 395 | } |
355 | 396 | ||
356 | #[test] | 397 | #[test] |
357 | fn test_complete_type() { | 398 | fn test_complete_type() { |
358 | check_scope_completion(r" | 399 | check_scope_completion( |
400 | r" | ||
359 | struct Foo; | 401 | struct Foo; |
360 | fn x() -> <|> | 402 | fn x() -> <|> |
361 | ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, | 403 | ", |
362 | CompletionItem { label: "x", lookup: None, snippet: None }]"#) | 404 | r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, |
405 | CompletionItem { label: "x", lookup: None, snippet: None }]"#, | ||
406 | ) | ||
363 | } | 407 | } |
364 | 408 | ||
365 | #[test] | 409 | #[test] |
366 | fn test_complete_shadowing() { | 410 | fn test_complete_shadowing() { |
367 | check_scope_completion(r" | 411 | check_scope_completion( |
412 | r" | ||
368 | fn foo() -> { | 413 | fn foo() -> { |
369 | let bar = 92; | 414 | let bar = 92; |
370 | { | 415 | { |
@@ -372,15 +417,20 @@ mod tests { | |||
372 | <|> | 417 | <|> |
373 | } | 418 | } |
374 | } | 419 | } |
375 | ", r#"[CompletionItem { label: "bar", lookup: None, snippet: None }, | 420 | ", |
376 | CompletionItem { label: "foo", lookup: None, snippet: None }]"#) | 421 | r#"[CompletionItem { label: "bar", lookup: None, snippet: None }, |
422 | CompletionItem { label: "foo", lookup: None, snippet: None }]"#, | ||
423 | ) | ||
377 | } | 424 | } |
378 | 425 | ||
379 | #[test] | 426 | #[test] |
380 | fn test_complete_self() { | 427 | fn test_complete_self() { |
381 | check_scope_completion(r" | 428 | check_scope_completion( |
429 | r" | ||
382 | impl S { fn foo(&self) { <|> } } | 430 | impl S { fn foo(&self) { <|> } } |
383 | ", r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#) | 431 | ", |
432 | r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#, | ||
433 | ) | ||
384 | } | 434 | } |
385 | 435 | ||
386 | #[test] | 436 | #[test] |
diff --git a/crates/ra_editor/src/edit.rs b/crates/ra_editor/src/edit.rs index 46e687319..c3149ec54 100644 --- a/crates/ra_editor/src/edit.rs +++ b/crates/ra_editor/src/edit.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | use crate::{TextRange, TextUnit}; | 1 | use crate::{TextRange, TextUnit}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{text_utils::contains_offset_nonstrict, AtomEdit}; |
3 | AtomEdit, | ||
4 | text_utils::contains_offset_nonstrict, | ||
5 | }; | ||
6 | 3 | ||
7 | #[derive(Debug, Clone)] | 4 | #[derive(Debug, Clone)] |
8 | pub struct Edit { | 5 | pub struct Edit { |
@@ -11,7 +8,7 @@ pub struct Edit { | |||
11 | 8 | ||
12 | #[derive(Debug)] | 9 | #[derive(Debug)] |
13 | pub struct EditBuilder { | 10 | pub struct EditBuilder { |
14 | atoms: Vec<AtomEdit> | 11 | atoms: Vec<AtomEdit>, |
15 | } | 12 | } |
16 | 13 | ||
17 | impl EditBuilder { | 14 | impl EditBuilder { |
@@ -36,7 +33,9 @@ impl EditBuilder { | |||
36 | Edit { atoms } | 33 | Edit { atoms } |
37 | } | 34 | } |
38 | pub fn invalidates_offset(&self, offset: TextUnit) -> bool { | 35 | pub fn invalidates_offset(&self, offset: TextUnit) -> bool { |
39 | self.atoms.iter().any(|atom| contains_offset_nonstrict(atom.delete, offset)) | 36 | self.atoms |
37 | .iter() | ||
38 | .any(|atom| contains_offset_nonstrict(atom.delete, offset)) | ||
40 | } | 39 | } |
41 | } | 40 | } |
42 | 41 | ||
@@ -74,7 +73,7 @@ impl Edit { | |||
74 | break; | 73 | break; |
75 | } | 74 | } |
76 | if offset < atom.delete.end() { | 75 | if offset < atom.delete.end() { |
77 | return None | 76 | return None; |
78 | } | 77 | } |
79 | res += TextUnit::of_str(&atom.insert); | 78 | res += TextUnit::of_str(&atom.insert); |
80 | res -= atom.delete.len(); | 79 | res -= atom.delete.len(); |
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs index ab03a717e..9ee1df281 100644 --- a/crates/ra_editor/src/extend_selection.rs +++ b/crates/ra_editor/src/extend_selection.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | File, TextRange, SyntaxNodeRef, TextUnit, Direction, | 2 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
3 | Direction, File, | ||
3 | SyntaxKind::*, | 4 | SyntaxKind::*, |
4 | algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node}, | 5 | SyntaxNodeRef, TextRange, TextUnit, |
5 | }; | 6 | }; |
6 | 7 | ||
7 | pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { | 8 | pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { |
@@ -20,11 +21,11 @@ pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> | |||
20 | LeafAtOffset::None => return None, | 21 | LeafAtOffset::None => return None, |
21 | LeafAtOffset::Single(l) => { | 22 | LeafAtOffset::Single(l) => { |
22 | if l.kind() == COMMENT { | 23 | if l.kind() == COMMENT { |
23 | extend_single_word_in_comment(l, offset).unwrap_or_else(||l.range()) | 24 | extend_single_word_in_comment(l, offset).unwrap_or_else(|| l.range()) |
24 | } else { | 25 | } else { |
25 | l.range() | 26 | l.range() |
26 | } | 27 | } |
27 | }, | 28 | } |
28 | LeafAtOffset::Between(l, r) => pick_best(l, r).range(), | 29 | LeafAtOffset::Between(l, r) => pick_best(l, r).range(), |
29 | }; | 30 | }; |
30 | return Some(leaf_range); | 31 | return Some(leaf_range); |
@@ -66,7 +67,7 @@ fn extend_ws(root: SyntaxNodeRef, ws: SyntaxNodeRef, offset: TextUnit) -> TextRa | |||
66 | if let Some(node) = ws.next_sibling() { | 67 | if let Some(node) = ws.next_sibling() { |
67 | let start = match ws_prefix.rfind('\n') { | 68 | let start = match ws_prefix.rfind('\n') { |
68 | Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32), | 69 | Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32), |
69 | None => node.range().start() | 70 | None => node.range().start(), |
70 | }; | 71 | }; |
71 | let end = if root.text().char_at(node.range().end()) == Some('\n') { | 72 | let end = if root.text().char_at(node.range().end()) == Some('\n') { |
72 | node.range().end() + TextUnit::of_char('\n') | 73 | node.range().end() + TextUnit::of_char('\n') |
@@ -94,10 +95,7 @@ fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> { | |||
94 | let prev = adj_comments(node, Direction::Prev); | 95 | let prev = adj_comments(node, Direction::Prev); |
95 | let next = adj_comments(node, Direction::Next); | 96 | let next = adj_comments(node, Direction::Next); |
96 | if prev != next { | 97 | if prev != next { |
97 | Some(TextRange::from_to( | 98 | Some(TextRange::from_to(prev.range().start(), next.range().end())) |
98 | prev.range().start(), | ||
99 | next.range().end(), | ||
100 | )) | ||
101 | } else { | 99 | } else { |
102 | None | 100 | None |
103 | } | 101 | } |
@@ -109,7 +107,7 @@ fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef { | |||
109 | match node.kind() { | 107 | match node.kind() { |
110 | COMMENT => res = node, | 108 | COMMENT => res = node, |
111 | WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), | 109 | WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), |
112 | _ => break | 110 | _ => break, |
113 | } | 111 | } |
114 | } | 112 | } |
115 | res | 113 | res |
@@ -125,8 +123,7 @@ mod tests { | |||
125 | let file = File::parse(&before); | 123 | let file = File::parse(&before); |
126 | let mut range = TextRange::offset_len(cursor, 0.into()); | 124 | let mut range = TextRange::offset_len(cursor, 0.into()); |
127 | for &after in afters { | 125 | for &after in afters { |
128 | range = extend_selection(&file, range) | 126 | range = extend_selection(&file, range).unwrap(); |
129 | .unwrap(); | ||
130 | let actual = &before[range]; | 127 | let actual = &before[range]; |
131 | assert_eq!(after, actual); | 128 | assert_eq!(after, actual); |
132 | } | 129 | } |
@@ -134,10 +131,7 @@ mod tests { | |||
134 | 131 | ||
135 | #[test] | 132 | #[test] |
136 | fn test_extend_selection_arith() { | 133 | fn test_extend_selection_arith() { |
137 | do_check( | 134 | do_check(r#"fn foo() { <|>1 + 1 }"#, &["1", "1 + 1", "{ 1 + 1 }"]); |
138 | r#"fn foo() { <|>1 + 1 }"#, | ||
139 | &["1", "1 + 1", "{ 1 + 1 }"], | ||
140 | ); | ||
141 | } | 135 | } |
142 | 136 | ||
143 | #[test] | 137 | #[test] |
@@ -149,7 +143,7 @@ impl S { | |||
149 | 143 | ||
150 | } | 144 | } |
151 | }"#, | 145 | }"#, |
152 | &[" fn foo() {\n\n }\n"] | 146 | &[" fn foo() {\n\n }\n"], |
153 | ); | 147 | ); |
154 | } | 148 | } |
155 | 149 | ||
@@ -165,7 +159,11 @@ struct B { | |||
165 | <|> | 159 | <|> |
166 | } | 160 | } |
167 | "#, | 161 | "#, |
168 | &["\n \n", "{\n \n}", "/// bla\n/// bla\nstruct B {\n \n}"] | 162 | &[ |
163 | "\n \n", | ||
164 | "{\n \n}", | ||
165 | "/// bla\n/// bla\nstruct B {\n \n}", | ||
166 | ], | ||
169 | ) | 167 | ) |
170 | } | 168 | } |
171 | 169 | ||
@@ -181,7 +179,7 @@ fn bar(){} | |||
181 | 179 | ||
182 | // fn foo(){} | 180 | // fn foo(){} |
183 | "#, | 181 | "#, |
184 | &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"] | 182 | &["// 1 + 1", "// fn foo() {\n// 1 + 1\n// }"], |
185 | ); | 183 | ); |
186 | } | 184 | } |
187 | 185 | ||
@@ -191,42 +189,34 @@ fn bar(){} | |||
191 | r#" | 189 | r#" |
192 | fn main() { foo<|>+bar;} | 190 | fn main() { foo<|>+bar;} |
193 | "#, | 191 | "#, |
194 | &["foo", "foo+bar"] | 192 | &["foo", "foo+bar"], |
195 | ); | 193 | ); |
196 | do_check( | 194 | do_check( |
197 | r#" | 195 | r#" |
198 | fn main() { foo+<|>bar;} | 196 | fn main() { foo+<|>bar;} |
199 | "#, | 197 | "#, |
200 | &["bar", "foo+bar"] | 198 | &["bar", "foo+bar"], |
201 | ); | 199 | ); |
202 | } | 200 | } |
203 | 201 | ||
204 | #[test] | 202 | #[test] |
205 | fn test_extend_selection_prefer_lifetimes() { | 203 | fn test_extend_selection_prefer_lifetimes() { |
206 | do_check( | 204 | do_check(r#"fn foo<<|>'a>() {}"#, &["'a", "<'a>"]); |
207 | r#"fn foo<<|>'a>() {}"#, | 205 | do_check(r#"fn foo<'a<|>>() {}"#, &["'a", "<'a>"]); |
208 | &["'a", "<'a>"] | ||
209 | ); | ||
210 | do_check( | ||
211 | r#"fn foo<'a<|>>() {}"#, | ||
212 | &["'a", "<'a>"] | ||
213 | ); | ||
214 | } | 206 | } |
215 | 207 | ||
216 | #[test] | 208 | #[test] |
217 | fn test_extend_selection_select_first_word() { | 209 | fn test_extend_selection_select_first_word() { |
210 | do_check(r#"// foo bar b<|>az quxx"#, &["baz", "// foo bar baz quxx"]); | ||
218 | do_check( | 211 | do_check( |
219 | r#"// foo bar b<|>az quxx"#, | 212 | r#" |
220 | &["baz", "// foo bar baz quxx"] | ||
221 | ); | ||
222 | do_check(r#" | ||
223 | impl S { | 213 | impl S { |
224 | fn foo() { | 214 | fn foo() { |
225 | // hel<|>lo world | 215 | // hel<|>lo world |
226 | } | 216 | } |
227 | } | 217 | } |
228 | "#, | 218 | "#, |
229 | &["hello", "// hello world"] | 219 | &["hello", "// hello world"], |
230 | ); | 220 | ); |
231 | } | 221 | } |
232 | } | 222 | } |
diff --git a/crates/ra_editor/src/folding_ranges.rs b/crates/ra_editor/src/folding_ranges.rs index a1699d449..e5bc0c4ee 100644 --- a/crates/ra_editor/src/folding_ranges.rs +++ b/crates/ra_editor/src/folding_ranges.rs | |||
@@ -1,11 +1,9 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast, | 4 | ast, AstNode, Direction, File, |
5 | AstNode, | ||
6 | File, TextRange, SyntaxNodeRef, | ||
7 | SyntaxKind::{self, *}, | 5 | SyntaxKind::{self, *}, |
8 | Direction, | 6 | SyntaxNodeRef, TextRange, |
9 | }; | 7 | }; |
10 | 8 | ||
11 | #[derive(Debug, PartialEq, Eq)] | 9 | #[derive(Debug, PartialEq, Eq)] |
@@ -28,7 +26,10 @@ pub fn folding_ranges(file: &File) -> Vec<Fold> { | |||
28 | // Fold items that span multiple lines | 26 | // Fold items that span multiple lines |
29 | if let Some(kind) = fold_kind(node.kind()) { | 27 | if let Some(kind) = fold_kind(node.kind()) { |
30 | if has_newline(node) { | 28 | if has_newline(node) { |
31 | res.push(Fold { range: node.range(), kind }); | 29 | res.push(Fold { |
30 | range: node.range(), | ||
31 | kind, | ||
32 | }); | ||
32 | } | 33 | } |
33 | } | 34 | } |
34 | 35 | ||
@@ -37,8 +38,12 @@ pub fn folding_ranges(file: &File) -> Vec<Fold> { | |||
37 | continue; | 38 | continue; |
38 | } | 39 | } |
39 | if node.kind() == COMMENT { | 40 | if node.kind() == COMMENT { |
40 | contiguous_range_for_comment(node, &mut visited_comments) | 41 | contiguous_range_for_comment(node, &mut visited_comments).map(|range| { |
41 | .map(|range| res.push(Fold { range, kind: FoldKind::Comment })); | 42 | res.push(Fold { |
43 | range, | ||
44 | kind: FoldKind::Comment, | ||
45 | }) | ||
46 | }); | ||
42 | } | 47 | } |
43 | } | 48 | } |
44 | 49 | ||
@@ -49,13 +54,11 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
49 | match kind { | 54 | match kind { |
50 | COMMENT => Some(FoldKind::Comment), | 55 | COMMENT => Some(FoldKind::Comment), |
51 | USE_ITEM => Some(FoldKind::Imports), | 56 | USE_ITEM => Some(FoldKind::Imports), |
52 | _ => None | 57 | _ => None, |
53 | } | 58 | } |
54 | } | 59 | } |
55 | 60 | ||
56 | fn has_newline( | 61 | fn has_newline(node: SyntaxNodeRef) -> bool { |
57 | node: SyntaxNodeRef, | ||
58 | ) -> bool { | ||
59 | for descendant in node.descendants() { | 62 | for descendant in node.descendants() { |
60 | if let Some(ws) = ast::Whitespace::cast(descendant) { | 63 | if let Some(ws) = ast::Whitespace::cast(descendant) { |
61 | if ws.has_newlines() { | 64 | if ws.has_newlines() { |
@@ -100,9 +103,7 @@ fn contiguous_range_for_comment<'a>( | |||
100 | // The comment group ends because either: | 103 | // The comment group ends because either: |
101 | // * An element of a different kind was reached | 104 | // * An element of a different kind was reached |
102 | // * A comment of a different flavor was reached | 105 | // * A comment of a different flavor was reached |
103 | _ => { | 106 | _ => break, |
104 | break | ||
105 | } | ||
106 | } | 107 | } |
107 | } | 108 | } |
108 | 109 | ||
@@ -128,7 +129,11 @@ mod tests { | |||
128 | let folds = folding_ranges(&file); | 129 | let folds = folding_ranges(&file); |
129 | 130 | ||
130 | assert_eq!(folds.len(), ranges.len()); | 131 | assert_eq!(folds.len(), ranges.len()); |
131 | for ((fold, range), fold_kind) in folds.into_iter().zip(ranges.into_iter()).zip(fold_kinds.into_iter()) { | 132 | for ((fold, range), fold_kind) in folds |
133 | .into_iter() | ||
134 | .zip(ranges.into_iter()) | ||
135 | .zip(fold_kinds.into_iter()) | ||
136 | { | ||
132 | assert_eq!(fold.range.start(), range.start()); | 137 | assert_eq!(fold.range.start(), range.start()); |
133 | assert_eq!(fold.range.end(), range.end()); | 138 | assert_eq!(fold.range.end(), range.end()); |
134 | assert_eq!(&fold.kind, fold_kind); | 139 | assert_eq!(&fold.kind, fold_kind); |
@@ -181,5 +186,4 @@ fn main() { | |||
181 | do_check(text, folds); | 186 | do_check(text, folds); |
182 | } | 187 | } |
183 | 188 | ||
184 | |||
185 | } | 189 | } |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index bd61fd191..417080d90 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -1,44 +1,41 @@ | |||
1 | extern crate ra_syntax; | ||
2 | extern crate superslice; | ||
3 | extern crate itertools; | 1 | extern crate itertools; |
4 | extern crate join_to_string; | 2 | extern crate join_to_string; |
3 | extern crate ra_syntax; | ||
5 | extern crate rustc_hash; | 4 | extern crate rustc_hash; |
5 | extern crate superslice; | ||
6 | #[cfg(test)] | 6 | #[cfg(test)] |
7 | #[macro_use] | 7 | #[macro_use] |
8 | extern crate test_utils as _test_utils; | 8 | extern crate test_utils as _test_utils; |
9 | 9 | ||
10 | mod extend_selection; | ||
11 | mod symbols; | ||
12 | mod line_index; | ||
13 | mod edit; | ||
14 | mod folding_ranges; | ||
15 | mod code_actions; | 10 | mod code_actions; |
16 | mod typing; | ||
17 | mod completion; | 11 | mod completion; |
12 | mod edit; | ||
13 | mod extend_selection; | ||
14 | mod folding_ranges; | ||
15 | mod line_index; | ||
18 | mod scope; | 16 | mod scope; |
17 | mod symbols; | ||
19 | #[cfg(test)] | 18 | #[cfg(test)] |
20 | mod test_utils; | 19 | mod test_utils; |
20 | mod typing; | ||
21 | 21 | ||
22 | pub use self::{ | ||
23 | code_actions::{add_derive, add_impl, flip_comma, introduce_variable, LocalEdit}, | ||
24 | completion::{scope_completion, CompletionItem}, | ||
25 | edit::{Edit, EditBuilder}, | ||
26 | extend_selection::extend_selection, | ||
27 | folding_ranges::{folding_ranges, Fold, FoldKind}, | ||
28 | line_index::{LineCol, LineIndex}, | ||
29 | symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, | ||
30 | typing::{join_lines, on_enter, on_eq_typed}, | ||
31 | }; | ||
32 | pub use ra_syntax::AtomEdit; | ||
22 | use ra_syntax::{ | 33 | use ra_syntax::{ |
23 | File, TextUnit, TextRange, SmolStr, SyntaxNodeRef, | ||
24 | ast::{self, AstNode, NameOwner}, | ||
25 | algo::find_leaf_at_offset, | 34 | algo::find_leaf_at_offset, |
35 | ast::{self, AstNode, NameOwner}, | ||
36 | File, SmolStr, | ||
26 | SyntaxKind::{self, *}, | 37 | SyntaxKind::{self, *}, |
27 | }; | 38 | SyntaxNodeRef, TextRange, TextUnit, |
28 | pub use ra_syntax::AtomEdit; | ||
29 | pub use self::{ | ||
30 | line_index::{LineIndex, LineCol}, | ||
31 | extend_selection::extend_selection, | ||
32 | symbols::{StructureNode, file_structure, FileSymbol, file_symbols}, | ||
33 | edit::{EditBuilder, Edit}, | ||
34 | code_actions::{ | ||
35 | LocalEdit, | ||
36 | flip_comma, add_derive, add_impl, | ||
37 | introduce_variable, | ||
38 | }, | ||
39 | typing::{join_lines, on_eq_typed, on_enter}, | ||
40 | completion::{scope_completion, CompletionItem}, | ||
41 | folding_ranges::{Fold, FoldKind, folding_ranges} | ||
42 | }; | 39 | }; |
43 | 40 | ||
44 | #[derive(Debug)] | 41 | #[derive(Debug)] |
@@ -67,10 +64,7 @@ pub enum RunnableKind { | |||
67 | 64 | ||
68 | pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { | 65 | pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { |
69 | const BRACES: &[SyntaxKind] = &[ | 66 | const BRACES: &[SyntaxKind] = &[ |
70 | L_CURLY, R_CURLY, | 67 | L_CURLY, R_CURLY, L_BRACK, R_BRACK, L_PAREN, R_PAREN, L_ANGLE, R_ANGLE, |
71 | L_BRACK, R_BRACK, | ||
72 | L_PAREN, R_PAREN, | ||
73 | L_ANGLE, R_ANGLE, | ||
74 | ]; | 68 | ]; |
75 | let (brace_node, brace_idx) = find_leaf_at_offset(file.syntax(), offset) | 69 | let (brace_node, brace_idx) = find_leaf_at_offset(file.syntax(), offset) |
76 | .filter_map(|node| { | 70 | .filter_map(|node| { |
@@ -80,7 +74,8 @@ pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { | |||
80 | .next()?; | 74 | .next()?; |
81 | let parent = brace_node.parent()?; | 75 | let parent = brace_node.parent()?; |
82 | let matching_kind = BRACES[brace_idx ^ 1]; | 76 | let matching_kind = BRACES[brace_idx ^ 1]; |
83 | let matching_node = parent.children() | 77 | let matching_node = parent |
78 | .children() | ||
84 | .find(|node| node.kind() == matching_kind)?; | 79 | .find(|node| node.kind() == matching_kind)?; |
85 | Some(matching_node.range().start()) | 80 | Some(matching_node.range().start()) |
86 | } | 81 | } |
@@ -108,10 +103,13 @@ pub fn highlight(file: &File) -> Vec<HighlightedRange> { | |||
108 | } | 103 | } |
109 | 104 | ||
110 | pub fn diagnostics(file: &File) -> Vec<Diagnostic> { | 105 | pub fn diagnostics(file: &File) -> Vec<Diagnostic> { |
111 | file.errors().into_iter().map(|err| Diagnostic { | 106 | file.errors() |
112 | range: TextRange::offset_len(err.offset, 1.into()), | 107 | .into_iter() |
113 | msg: "Syntax Error: ".to_string() + &err.msg, | 108 | .map(|err| Diagnostic { |
114 | }).collect() | 109 | range: TextRange::offset_len(err.offset, 1.into()), |
110 | msg: "Syntax Error: ".to_string() + &err.msg, | ||
111 | }) | ||
112 | .collect() | ||
115 | } | 113 | } |
116 | 114 | ||
117 | pub fn syntax_tree(file: &File) -> String { | 115 | pub fn syntax_tree(file: &File) -> String { |
@@ -119,7 +117,8 @@ pub fn syntax_tree(file: &File) -> String { | |||
119 | } | 117 | } |
120 | 118 | ||
121 | pub fn runnables(file: &File) -> Vec<Runnable> { | 119 | pub fn runnables(file: &File) -> Vec<Runnable> { |
122 | file.syntax().descendants() | 120 | file.syntax() |
121 | .descendants() | ||
123 | .filter_map(ast::FnDef::cast) | 122 | .filter_map(ast::FnDef::cast) |
124 | .filter_map(|f| { | 123 | .filter_map(|f| { |
125 | let name = f.name()?.text(); | 124 | let name = f.name()?.text(); |
@@ -127,7 +126,7 @@ pub fn runnables(file: &File) -> Vec<Runnable> { | |||
127 | RunnableKind::Bin | 126 | RunnableKind::Bin |
128 | } else if f.has_atom_attr("test") { | 127 | } else if f.has_atom_attr("test") { |
129 | RunnableKind::Test { | 128 | RunnableKind::Test { |
130 | name: name.to_string() | 129 | name: name.to_string(), |
131 | } | 130 | } |
132 | } else { | 131 | } else { |
133 | return None; | 132 | return None; |
@@ -145,15 +144,18 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( | |||
145 | offset: TextUnit, | 144 | offset: TextUnit, |
146 | ) -> Option<N> { | 145 | ) -> Option<N> { |
147 | let leaves = find_leaf_at_offset(syntax, offset); | 146 | let leaves = find_leaf_at_offset(syntax, offset); |
148 | let leaf = leaves.clone() | 147 | let leaf = leaves |
148 | .clone() | ||
149 | .find(|leaf| !leaf.kind().is_trivia()) | 149 | .find(|leaf| !leaf.kind().is_trivia()) |
150 | .or_else(|| leaves.right_biased())?; | 150 | .or_else(|| leaves.right_biased())?; |
151 | leaf.ancestors() | 151 | leaf.ancestors().filter_map(N::cast).next() |
152 | .filter_map(N::cast) | ||
153 | .next() | ||
154 | } | 152 | } |
155 | 153 | ||
156 | pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) -> Option<(SmolStr, TextRange)> { | 154 | pub fn resolve_local_name( |
155 | file: &File, | ||
156 | offset: TextUnit, | ||
157 | name_ref: ast::NameRef, | ||
158 | ) -> Option<(SmolStr, TextRange)> { | ||
157 | let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), offset)?; | 159 | let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), offset)?; |
158 | let scopes = scope::FnScopes::new(fn_def); | 160 | let scopes = scope::FnScopes::new(fn_def); |
159 | let scope_entry = scope::resolve_local_name(name_ref, &scopes)?; | 161 | let scope_entry = scope::resolve_local_name(name_ref, &scopes)?; |
@@ -164,15 +166,17 @@ pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) | |||
164 | #[cfg(test)] | 166 | #[cfg(test)] |
165 | mod tests { | 167 | mod tests { |
166 | use super::*; | 168 | use super::*; |
167 | use crate::test_utils::{assert_eq_dbg, extract_offset, add_cursor}; | 169 | use crate::test_utils::{add_cursor, assert_eq_dbg, extract_offset}; |
168 | 170 | ||
169 | #[test] | 171 | #[test] |
170 | fn test_highlighting() { | 172 | fn test_highlighting() { |
171 | let file = File::parse(r#" | 173 | let file = File::parse( |
174 | r#" | ||
172 | // comment | 175 | // comment |
173 | fn main() {} | 176 | fn main() {} |
174 | println!("Hello, {}!", 92); | 177 | println!("Hello, {}!", 92); |
175 | "#); | 178 | "#, |
179 | ); | ||
176 | let hls = highlight(&file); | 180 | let hls = highlight(&file); |
177 | assert_eq_dbg( | 181 | assert_eq_dbg( |
178 | r#"[HighlightedRange { range: [1; 11), tag: "comment" }, | 182 | r#"[HighlightedRange { range: [1; 11), tag: "comment" }, |
@@ -187,7 +191,8 @@ fn main() {} | |||
187 | 191 | ||
188 | #[test] | 192 | #[test] |
189 | fn test_runnables() { | 193 | fn test_runnables() { |
190 | let file = File::parse(r#" | 194 | let file = File::parse( |
195 | r#" | ||
191 | fn main() {} | 196 | fn main() {} |
192 | 197 | ||
193 | #[test] | 198 | #[test] |
@@ -196,7 +201,8 @@ fn test_foo() {} | |||
196 | #[test] | 201 | #[test] |
197 | #[ignore] | 202 | #[ignore] |
198 | fn test_foo() {} | 203 | fn test_foo() {} |
199 | "#); | 204 | "#, |
205 | ); | ||
200 | let runnables = runnables(&file); | 206 | let runnables = runnables(&file); |
201 | assert_eq_dbg( | 207 | assert_eq_dbg( |
202 | r#"[Runnable { range: [1; 13), kind: Bin }, | 208 | r#"[Runnable { range: [1; 13), kind: Bin }, |
@@ -219,9 +225,6 @@ fn test_foo() {} | |||
219 | assert_eq_text!(after, &actual); | 225 | assert_eq_text!(after, &actual); |
220 | } | 226 | } |
221 | 227 | ||
222 | do_check( | 228 | do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); |
223 | "struct Foo { a: i32, }<|>", | ||
224 | "struct Foo <|>{ a: i32, }", | ||
225 | ); | ||
226 | } | 229 | } |
227 | } | 230 | } |
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs index 95d64b8a8..da0f2a7f7 100644 --- a/crates/ra_editor/src/line_index.rs +++ b/crates/ra_editor/src/line_index.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use superslice::Ext; | ||
2 | use crate::TextUnit; | 1 | use crate::TextUnit; |
2 | use superslice::Ext; | ||
3 | 3 | ||
4 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | 4 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
5 | pub struct LineIndex { | 5 | pub struct LineIndex { |
@@ -29,7 +29,10 @@ impl LineIndex { | |||
29 | let line = self.newlines.upper_bound(&offset) - 1; | 29 | let line = self.newlines.upper_bound(&offset) - 1; |
30 | let line_start_offset = self.newlines[line]; | 30 | let line_start_offset = self.newlines[line]; |
31 | let col = offset - line_start_offset; | 31 | let col = offset - line_start_offset; |
32 | return LineCol { line: line as u32, col }; | 32 | return LineCol { |
33 | line: line as u32, | ||
34 | col, | ||
35 | }; | ||
33 | } | 36 | } |
34 | 37 | ||
35 | pub fn offset(&self, line_col: LineCol) -> TextUnit { | 38 | pub fn offset(&self, line_col: LineCol) -> TextUnit { |
@@ -42,21 +45,105 @@ impl LineIndex { | |||
42 | fn test_line_index() { | 45 | fn test_line_index() { |
43 | let text = "hello\nworld"; | 46 | let text = "hello\nworld"; |
44 | let index = LineIndex::new(text); | 47 | let index = LineIndex::new(text); |
45 | assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() }); | 48 | assert_eq!( |
46 | assert_eq!(index.line_col(1.into()), LineCol { line: 0, col: 1.into() }); | 49 | index.line_col(0.into()), |
47 | assert_eq!(index.line_col(5.into()), LineCol { line: 0, col: 5.into() }); | 50 | LineCol { |
48 | assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 0.into() }); | 51 | line: 0, |
49 | assert_eq!(index.line_col(7.into()), LineCol { line: 1, col: 1.into() }); | 52 | col: 0.into() |
50 | assert_eq!(index.line_col(8.into()), LineCol { line: 1, col: 2.into() }); | 53 | } |
51 | assert_eq!(index.line_col(10.into()), LineCol { line: 1, col: 4.into() }); | 54 | ); |
52 | assert_eq!(index.line_col(11.into()), LineCol { line: 1, col: 5.into() }); | 55 | assert_eq!( |
53 | assert_eq!(index.line_col(12.into()), LineCol { line: 1, col: 6.into() }); | 56 | index.line_col(1.into()), |
57 | LineCol { | ||
58 | line: 0, | ||
59 | col: 1.into() | ||
60 | } | ||
61 | ); | ||
62 | assert_eq!( | ||
63 | index.line_col(5.into()), | ||
64 | LineCol { | ||
65 | line: 0, | ||
66 | col: 5.into() | ||
67 | } | ||
68 | ); | ||
69 | assert_eq!( | ||
70 | index.line_col(6.into()), | ||
71 | LineCol { | ||
72 | line: 1, | ||
73 | col: 0.into() | ||
74 | } | ||
75 | ); | ||
76 | assert_eq!( | ||
77 | index.line_col(7.into()), | ||
78 | LineCol { | ||
79 | line: 1, | ||
80 | col: 1.into() | ||
81 | } | ||
82 | ); | ||
83 | assert_eq!( | ||
84 | index.line_col(8.into()), | ||
85 | LineCol { | ||
86 | line: 1, | ||
87 | col: 2.into() | ||
88 | } | ||
89 | ); | ||
90 | assert_eq!( | ||
91 | index.line_col(10.into()), | ||
92 | LineCol { | ||
93 | line: 1, | ||
94 | col: 4.into() | ||
95 | } | ||
96 | ); | ||
97 | assert_eq!( | ||
98 | index.line_col(11.into()), | ||
99 | LineCol { | ||
100 | line: 1, | ||
101 | col: 5.into() | ||
102 | } | ||
103 | ); | ||
104 | assert_eq!( | ||
105 | index.line_col(12.into()), | ||
106 | LineCol { | ||
107 | line: 1, | ||
108 | col: 6.into() | ||
109 | } | ||
110 | ); | ||
54 | 111 | ||
55 | let text = "\nhello\nworld"; | 112 | let text = "\nhello\nworld"; |
56 | let index = LineIndex::new(text); | 113 | let index = LineIndex::new(text); |
57 | assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() }); | 114 | assert_eq!( |
58 | assert_eq!(index.line_col(1.into()), LineCol { line: 1, col: 0.into() }); | 115 | index.line_col(0.into()), |
59 | assert_eq!(index.line_col(2.into()), LineCol { line: 1, col: 1.into() }); | 116 | LineCol { |
60 | assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 5.into() }); | 117 | line: 0, |
61 | assert_eq!(index.line_col(7.into()), LineCol { line: 2, col: 0.into() }); | 118 | col: 0.into() |
119 | } | ||
120 | ); | ||
121 | assert_eq!( | ||
122 | index.line_col(1.into()), | ||
123 | LineCol { | ||
124 | line: 1, | ||
125 | col: 0.into() | ||
126 | } | ||
127 | ); | ||
128 | assert_eq!( | ||
129 | index.line_col(2.into()), | ||
130 | LineCol { | ||
131 | line: 1, | ||
132 | col: 1.into() | ||
133 | } | ||
134 | ); | ||
135 | assert_eq!( | ||
136 | index.line_col(6.into()), | ||
137 | LineCol { | ||
138 | line: 1, | ||
139 | col: 5.into() | ||
140 | } | ||
141 | ); | ||
142 | assert_eq!( | ||
143 | index.line_col(7.into()), | ||
144 | LineCol { | ||
145 | line: 2, | ||
146 | col: 0.into() | ||
147 | } | ||
148 | ); | ||
62 | } | 149 | } |
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 99d698b60..9088e5a60 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | |||
2 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
3 | 4 | ||
4 | use ra_syntax::{ | 5 | use ra_syntax::{ |
5 | SyntaxNodeRef, SyntaxNode, SmolStr, AstNode, | 6 | algo::generate, |
6 | ast::{self, NameOwner, LoopBodyOwner, ArgListOwner}, | 7 | ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, |
7 | algo::{generate} | 8 | AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, |
8 | }; | 9 | }; |
9 | 10 | ||
10 | type ScopeId = usize; | 11 | type ScopeId = usize; |
@@ -19,11 +20,12 @@ pub struct FnScopes { | |||
19 | impl FnScopes { | 20 | impl FnScopes { |
20 | pub fn new(fn_def: ast::FnDef) -> FnScopes { | 21 | pub fn new(fn_def: ast::FnDef) -> FnScopes { |
21 | let mut scopes = FnScopes { | 22 | let mut scopes = FnScopes { |
22 | self_param: fn_def.param_list() | 23 | self_param: fn_def |
24 | .param_list() | ||
23 | .and_then(|it| it.self_param()) | 25 | .and_then(|it| it.self_param()) |
24 | .map(|it| it.syntax().owned()), | 26 | .map(|it| it.syntax().owned()), |
25 | scopes: Vec::new(), | 27 | scopes: Vec::new(), |
26 | scope_for: FxHashMap::default() | 28 | scope_for: FxHashMap::default(), |
27 | }; | 29 | }; |
28 | let root = scopes.root_scope(); | 30 | let root = scopes.root_scope(); |
29 | scopes.add_params_bindings(root, fn_def.param_list()); | 31 | scopes.add_params_bindings(root, fn_def.param_list()); |
@@ -35,27 +37,38 @@ impl FnScopes { | |||
35 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | 37 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { |
36 | &self.scopes[scope].entries | 38 | &self.scopes[scope].entries |
37 | } | 39 | } |
38 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a { | 40 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { |
39 | generate(self.scope_for(node), move |&scope| self.scopes[scope].parent) | 41 | generate(self.scope_for(node), move |&scope| { |
42 | self.scopes[scope].parent | ||
43 | }) | ||
40 | } | 44 | } |
41 | fn root_scope(&mut self) -> ScopeId { | 45 | fn root_scope(&mut self) -> ScopeId { |
42 | let res = self.scopes.len(); | 46 | let res = self.scopes.len(); |
43 | self.scopes.push(ScopeData { parent: None, entries: vec![] }); | 47 | self.scopes.push(ScopeData { |
48 | parent: None, | ||
49 | entries: vec![], | ||
50 | }); | ||
44 | res | 51 | res |
45 | } | 52 | } |
46 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | 53 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { |
47 | let res = self.scopes.len(); | 54 | let res = self.scopes.len(); |
48 | self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] }); | 55 | self.scopes.push(ScopeData { |
56 | parent: Some(parent), | ||
57 | entries: vec![], | ||
58 | }); | ||
49 | res | 59 | res |
50 | } | 60 | } |
51 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { | 61 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { |
52 | let entries = pat.syntax().descendants() | 62 | let entries = pat |
63 | .syntax() | ||
64 | .descendants() | ||
53 | .filter_map(ast::BindPat::cast) | 65 | .filter_map(ast::BindPat::cast) |
54 | .filter_map(ScopeEntry::new); | 66 | .filter_map(ScopeEntry::new); |
55 | self.scopes[scope].entries.extend(entries); | 67 | self.scopes[scope].entries.extend(entries); |
56 | } | 68 | } |
57 | fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) { | 69 | fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) { |
58 | params.into_iter() | 70 | params |
71 | .into_iter() | ||
59 | .flat_map(|it| it.params()) | 72 | .flat_map(|it| it.params()) |
60 | .filter_map(|it| it.pat()) | 73 | .filter_map(|it| it.pat()) |
61 | .for_each(|it| self.add_bindings(scope, it)); | 74 | .for_each(|it| self.add_bindings(scope, it)); |
@@ -71,34 +84,33 @@ impl FnScopes { | |||
71 | } | 84 | } |
72 | 85 | ||
73 | pub struct ScopeEntry { | 86 | pub struct ScopeEntry { |
74 | syntax: SyntaxNode | 87 | syntax: SyntaxNode, |
75 | } | 88 | } |
76 | 89 | ||
77 | impl ScopeEntry { | 90 | impl ScopeEntry { |
78 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { | 91 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { |
79 | if pat.name().is_some() { | 92 | if pat.name().is_some() { |
80 | Some(ScopeEntry { syntax: pat.syntax().owned() }) | 93 | Some(ScopeEntry { |
94 | syntax: pat.syntax().owned(), | ||
95 | }) | ||
81 | } else { | 96 | } else { |
82 | None | 97 | None |
83 | } | 98 | } |
84 | } | 99 | } |
85 | pub fn name(&self) -> SmolStr { | 100 | pub fn name(&self) -> SmolStr { |
86 | self.ast().name() | 101 | self.ast().name().unwrap().text() |
87 | .unwrap() | ||
88 | .text() | ||
89 | } | 102 | } |
90 | pub fn ast(&self) -> ast::BindPat { | 103 | pub fn ast(&self) -> ast::BindPat { |
91 | ast::BindPat::cast(self.syntax.borrowed()) | 104 | ast::BindPat::cast(self.syntax.borrowed()).unwrap() |
92 | .unwrap() | ||
93 | } | 105 | } |
94 | } | 106 | } |
95 | 107 | ||
96 | impl fmt::Debug for ScopeEntry { | 108 | impl fmt::Debug for ScopeEntry { |
97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
98 | f.debug_struct("ScopeEntry") | 110 | f.debug_struct("ScopeEntry") |
99 | .field("name", &self.name()) | 111 | .field("name", &self.name()) |
100 | .field("syntax", &self.syntax) | 112 | .field("syntax", &self.syntax) |
101 | .finish() | 113 | .finish() |
102 | } | 114 | } |
103 | } | 115 | } |
104 | 116 | ||
@@ -132,16 +144,16 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco | |||
132 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | 144 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { |
133 | match expr { | 145 | match expr { |
134 | ast::Expr::IfExpr(e) => { | 146 | ast::Expr::IfExpr(e) => { |
135 | let cond_scope = e.condition().and_then(|cond| { | 147 | let cond_scope = e |
136 | compute_cond_scopes(cond, scopes, scope) | 148 | .condition() |
137 | }); | 149 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); |
138 | if let Some(block) = e.then_branch() { | 150 | if let Some(block) = e.then_branch() { |
139 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | 151 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); |
140 | } | 152 | } |
141 | if let Some(block) = e.else_branch() { | 153 | if let Some(block) = e.else_branch() { |
142 | compute_block_scopes(block, scopes, scope); | 154 | compute_block_scopes(block, scopes, scope); |
143 | } | 155 | } |
144 | }, | 156 | } |
145 | ast::Expr::BlockExpr(e) => { | 157 | ast::Expr::BlockExpr(e) => { |
146 | if let Some(block) = e.block() { | 158 | if let Some(block) = e.block() { |
147 | compute_block_scopes(block, scopes, scope); | 159 | compute_block_scopes(block, scopes, scope); |
@@ -153,9 +165,9 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
153 | } | 165 | } |
154 | } | 166 | } |
155 | ast::Expr::WhileExpr(e) => { | 167 | ast::Expr::WhileExpr(e) => { |
156 | let cond_scope = e.condition().and_then(|cond| { | 168 | let cond_scope = e |
157 | compute_cond_scopes(cond, scopes, scope) | 169 | .condition() |
158 | }); | 170 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); |
159 | if let Some(block) = e.loop_body() { | 171 | if let Some(block) = e.loop_body() { |
160 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | 172 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); |
161 | } | 173 | } |
@@ -201,25 +213,31 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
201 | } | 213 | } |
202 | } | 214 | } |
203 | } | 215 | } |
204 | _ => { | 216 | _ => expr |
205 | expr.syntax().children() | 217 | .syntax() |
206 | .filter_map(ast::Expr::cast) | 218 | .children() |
207 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)) | 219 | .filter_map(ast::Expr::cast) |
208 | } | 220 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)), |
209 | }; | 221 | }; |
210 | 222 | ||
211 | fn compute_call_scopes( | 223 | fn compute_call_scopes( |
212 | receiver: Option<ast::Expr>, | 224 | receiver: Option<ast::Expr>, |
213 | arg_list: Option<ast::ArgList>, | 225 | arg_list: Option<ast::ArgList>, |
214 | scopes: &mut FnScopes, scope: ScopeId, | 226 | scopes: &mut FnScopes, |
227 | scope: ScopeId, | ||
215 | ) { | 228 | ) { |
216 | arg_list.into_iter() | 229 | arg_list |
230 | .into_iter() | ||
217 | .flat_map(|it| it.args()) | 231 | .flat_map(|it| it.args()) |
218 | .chain(receiver) | 232 | .chain(receiver) |
219 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); | 233 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); |
220 | } | 234 | } |
221 | 235 | ||
222 | fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> { | 236 | fn compute_cond_scopes( |
237 | cond: ast::Condition, | ||
238 | scopes: &mut FnScopes, | ||
239 | scope: ScopeId, | ||
240 | ) -> Option<ScopeId> { | ||
223 | if let Some(expr) = cond.expr() { | 241 | if let Some(expr) = cond.expr() { |
224 | compute_expr_scopes(expr, scopes, scope); | 242 | compute_expr_scopes(expr, scopes, scope); |
225 | } | 243 | } |
@@ -236,14 +254,18 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
236 | #[derive(Debug)] | 254 | #[derive(Debug)] |
237 | struct ScopeData { | 255 | struct ScopeData { |
238 | parent: Option<ScopeId>, | 256 | parent: Option<ScopeId>, |
239 | entries: Vec<ScopeEntry> | 257 | entries: Vec<ScopeEntry>, |
240 | } | 258 | } |
241 | 259 | ||
242 | pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> { | 260 | pub fn resolve_local_name<'a>( |
261 | name_ref: ast::NameRef, | ||
262 | scopes: &'a FnScopes, | ||
263 | ) -> Option<&'a ScopeEntry> { | ||
243 | use rustc_hash::FxHashSet; | 264 | use rustc_hash::FxHashSet; |
244 | 265 | ||
245 | let mut shadowed = FxHashSet::default(); | 266 | let mut shadowed = FxHashSet::default(); |
246 | let ret = scopes.scope_chain(name_ref.syntax()) | 267 | let ret = scopes |
268 | .scope_chain(name_ref.syntax()) | ||
247 | .flat_map(|scope| scopes.entries(scope).iter()) | 269 | .flat_map(|scope| scopes.entries(scope).iter()) |
248 | .filter(|entry| shadowed.insert(entry.name())) | 270 | .filter(|entry| shadowed.insert(entry.name())) |
249 | .filter(|entry| entry.name() == name_ref.text()) | 271 | .filter(|entry| entry.name() == name_ref.text()) |
@@ -255,8 +277,8 @@ pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> O | |||
255 | #[cfg(test)] | 277 | #[cfg(test)] |
256 | mod tests { | 278 | mod tests { |
257 | use super::*; | 279 | use super::*; |
258 | use ra_syntax::File; | ||
259 | use crate::{find_node_at_offset, test_utils::extract_offset}; | 280 | use crate::{find_node_at_offset, test_utils::extract_offset}; |
281 | use ra_syntax::File; | ||
260 | 282 | ||
261 | fn do_check(code: &str, expected: &[&str]) { | 283 | fn do_check(code: &str, expected: &[&str]) { |
262 | let (off, code) = extract_offset(code); | 284 | let (off, code) = extract_offset(code); |
@@ -272,7 +294,8 @@ mod tests { | |||
272 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); | 294 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); |
273 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | 295 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); |
274 | let scopes = FnScopes::new(fn_def); | 296 | let scopes = FnScopes::new(fn_def); |
275 | let actual = scopes.scope_chain(marker.syntax()) | 297 | let actual = scopes |
298 | .scope_chain(marker.syntax()) | ||
276 | .flat_map(|scope| scopes.entries(scope)) | 299 | .flat_map(|scope| scopes.entries(scope)) |
277 | .map(|it| it.name()) | 300 | .map(|it| it.name()) |
278 | .collect::<Vec<_>>(); | 301 | .collect::<Vec<_>>(); |
@@ -281,7 +304,8 @@ mod tests { | |||
281 | 304 | ||
282 | #[test] | 305 | #[test] |
283 | fn test_lambda_scope() { | 306 | fn test_lambda_scope() { |
284 | do_check(r" | 307 | do_check( |
308 | r" | ||
285 | fn quux(foo: i32) { | 309 | fn quux(foo: i32) { |
286 | let f = |bar, baz: i32| { | 310 | let f = |bar, baz: i32| { |
287 | <|> | 311 | <|> |
@@ -293,7 +317,8 @@ mod tests { | |||
293 | 317 | ||
294 | #[test] | 318 | #[test] |
295 | fn test_call_scope() { | 319 | fn test_call_scope() { |
296 | do_check(r" | 320 | do_check( |
321 | r" | ||
297 | fn quux() { | 322 | fn quux() { |
298 | f(|x| <|> ); | 323 | f(|x| <|> ); |
299 | }", | 324 | }", |
@@ -303,7 +328,8 @@ mod tests { | |||
303 | 328 | ||
304 | #[test] | 329 | #[test] |
305 | fn test_metod_call_scope() { | 330 | fn test_metod_call_scope() { |
306 | do_check(r" | 331 | do_check( |
332 | r" | ||
307 | fn quux() { | 333 | fn quux() { |
308 | z.f(|x| <|> ); | 334 | z.f(|x| <|> ); |
309 | }", | 335 | }", |
@@ -313,7 +339,8 @@ mod tests { | |||
313 | 339 | ||
314 | #[test] | 340 | #[test] |
315 | fn test_loop_scope() { | 341 | fn test_loop_scope() { |
316 | do_check(r" | 342 | do_check( |
343 | r" | ||
317 | fn quux() { | 344 | fn quux() { |
318 | loop { | 345 | loop { |
319 | let x = (); | 346 | let x = (); |
@@ -326,7 +353,8 @@ mod tests { | |||
326 | 353 | ||
327 | #[test] | 354 | #[test] |
328 | fn test_match() { | 355 | fn test_match() { |
329 | do_check(r" | 356 | do_check( |
357 | r" | ||
330 | fn quux() { | 358 | fn quux() { |
331 | match () { | 359 | match () { |
332 | Some(x) => { | 360 | Some(x) => { |
@@ -340,7 +368,8 @@ mod tests { | |||
340 | 368 | ||
341 | #[test] | 369 | #[test] |
342 | fn test_shadow_variable() { | 370 | fn test_shadow_variable() { |
343 | do_check(r" | 371 | do_check( |
372 | r" | ||
344 | fn foo(x: String) { | 373 | fn foo(x: String) { |
345 | let x : &str = &x<|>; | 374 | let x : &str = &x<|>; |
346 | }", | 375 | }", |
@@ -356,14 +385,20 @@ mod tests { | |||
356 | 385 | ||
357 | let scopes = FnScopes::new(fn_def); | 386 | let scopes = FnScopes::new(fn_def); |
358 | 387 | ||
359 | let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap(); | 388 | let local_name = resolve_local_name(name_ref, &scopes) |
360 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | 389 | .unwrap() |
390 | .ast() | ||
391 | .name() | ||
392 | .unwrap(); | ||
393 | let expected_name = | ||
394 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | ||
361 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); | 395 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); |
362 | } | 396 | } |
363 | 397 | ||
364 | #[test] | 398 | #[test] |
365 | fn test_resolve_local_name() { | 399 | fn test_resolve_local_name() { |
366 | do_check_local_name(r#" | 400 | do_check_local_name( |
401 | r#" | ||
367 | fn foo(x: i32, y: u32) { | 402 | fn foo(x: i32, y: u32) { |
368 | { | 403 | { |
369 | let z = x * 2; | 404 | let z = x * 2; |
@@ -372,25 +407,30 @@ mod tests { | |||
372 | let t = x<|> * 3; | 407 | let t = x<|> * 3; |
373 | } | 408 | } |
374 | }"#, | 409 | }"#, |
375 | 21); | 410 | 21, |
411 | ); | ||
376 | } | 412 | } |
377 | 413 | ||
378 | #[test] | 414 | #[test] |
379 | fn test_resolve_local_name_declaration() { | 415 | fn test_resolve_local_name_declaration() { |
380 | do_check_local_name(r#" | 416 | do_check_local_name( |
417 | r#" | ||
381 | fn foo(x: String) { | 418 | fn foo(x: String) { |
382 | let x : &str = &x<|>; | 419 | let x : &str = &x<|>; |
383 | }"#, | 420 | }"#, |
384 | 21); | 421 | 21, |
422 | ); | ||
385 | } | 423 | } |
386 | 424 | ||
387 | #[test] | 425 | #[test] |
388 | fn test_resolve_local_name_shadow() { | 426 | fn test_resolve_local_name_shadow() { |
389 | do_check_local_name(r" | 427 | do_check_local_name( |
428 | r" | ||
390 | fn foo(x: String) { | 429 | fn foo(x: String) { |
391 | let x : &str = &x; | 430 | let x : &str = &x; |
392 | x<|> | 431 | x<|> |
393 | }", | 432 | }", |
394 | 46); | 433 | 46, |
434 | ); | ||
395 | } | 435 | } |
396 | } | 436 | } |
diff --git a/crates/ra_editor/src/scope/mod.rs b/crates/ra_editor/src/scope/mod.rs index 7d6d530f7..cc2d49392 100644 --- a/crates/ra_editor/src/scope/mod.rs +++ b/crates/ra_editor/src/scope/mod.rs | |||
@@ -2,7 +2,6 @@ mod fn_scope; | |||
2 | mod mod_scope; | 2 | mod mod_scope; |
3 | 3 | ||
4 | pub use self::{ | 4 | pub use self::{ |
5 | fn_scope::{FnScopes, resolve_local_name}, | 5 | fn_scope::{resolve_local_name, FnScopes}, |
6 | mod_scope::ModuleScope, | 6 | mod_scope::ModuleScope, |
7 | }; | 7 | }; |
8 | |||
diff --git a/crates/ra_editor/src/scope/mod_scope.rs b/crates/ra_editor/src/scope/mod_scope.rs index d2a3e7c58..8d7e408f8 100644 --- a/crates/ra_editor/src/scope/mod_scope.rs +++ b/crates/ra_editor/src/scope/mod_scope.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | AstNode, SyntaxNode, SyntaxNodeRef, SmolStr, | ||
3 | ast::{self, AstChildren}, | 2 | ast::{self, AstChildren}, |
3 | AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, | ||
4 | }; | 4 | }; |
5 | 5 | ||
6 | pub struct ModuleScope { | 6 | pub struct ModuleScope { |
@@ -13,7 +13,8 @@ pub struct Entry { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | enum EntryKind { | 15 | enum EntryKind { |
16 | Item, Import, | 16 | Item, |
17 | Import, | ||
17 | } | 18 | } |
18 | 19 | ||
19 | impl ModuleScope { | 20 | impl ModuleScope { |
@@ -34,9 +35,8 @@ impl ModuleScope { | |||
34 | collect_imports(tree, &mut entries); | 35 | collect_imports(tree, &mut entries); |
35 | } | 36 | } |
36 | continue; | 37 | continue; |
37 | }, | 38 | } |
38 | ast::ModuleItem::ExternCrateItem(_) | | 39 | ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue, |
39 | ast::ModuleItem::ImplItem(_) => continue, | ||
40 | }; | 40 | }; |
41 | entries.extend(entry) | 41 | entries.extend(entry) |
42 | } | 42 | } |
@@ -52,20 +52,22 @@ impl ModuleScope { | |||
52 | impl Entry { | 52 | impl Entry { |
53 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> { | 53 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> { |
54 | let name = item.name()?; | 54 | let name = item.name()?; |
55 | Some(Entry { node: name.syntax().owned(), kind: EntryKind::Item }) | 55 | Some(Entry { |
56 | node: name.syntax().owned(), | ||
57 | kind: EntryKind::Item, | ||
58 | }) | ||
56 | } | 59 | } |
57 | fn new_import(path: ast::Path) -> Option<Entry> { | 60 | fn new_import(path: ast::Path) -> Option<Entry> { |
58 | let name_ref = path.segment()?.name_ref()?; | 61 | let name_ref = path.segment()?.name_ref()?; |
59 | Some(Entry { node: name_ref.syntax().owned(), kind: EntryKind::Import }) | 62 | Some(Entry { |
63 | node: name_ref.syntax().owned(), | ||
64 | kind: EntryKind::Import, | ||
65 | }) | ||
60 | } | 66 | } |
61 | pub fn name(&self) -> SmolStr { | 67 | pub fn name(&self) -> SmolStr { |
62 | match self.kind { | 68 | match self.kind { |
63 | EntryKind::Item => | 69 | EntryKind::Item => ast::Name::cast(self.node.borrowed()).unwrap().text(), |
64 | ast::Name::cast(self.node.borrowed()).unwrap() | 70 | EntryKind::Import => ast::NameRef::cast(self.node.borrowed()).unwrap().text(), |
65 | .text(), | ||
66 | EntryKind::Import => | ||
67 | ast::NameRef::cast(self.node.borrowed()).unwrap() | ||
68 | .text(), | ||
69 | } | 71 | } |
70 | } | 72 | } |
71 | pub fn syntax(&self) -> SyntaxNodeRef { | 73 | pub fn syntax(&self) -> SyntaxNodeRef { |
@@ -75,32 +77,31 @@ impl Entry { | |||
75 | 77 | ||
76 | fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) { | 78 | fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) { |
77 | if let Some(use_tree_list) = tree.use_tree_list() { | 79 | if let Some(use_tree_list) = tree.use_tree_list() { |
78 | return use_tree_list.use_trees().for_each(|it| collect_imports(it, acc)); | 80 | return use_tree_list |
81 | .use_trees() | ||
82 | .for_each(|it| collect_imports(it, acc)); | ||
79 | } | 83 | } |
80 | if let Some(path) = tree.path() { | 84 | if let Some(path) = tree.path() { |
81 | acc.extend(Entry::new_import(path)); | 85 | acc.extend(Entry::new_import(path)); |
82 | } | 86 | } |
83 | } | 87 | } |
84 | 88 | ||
85 | |||
86 | #[cfg(test)] | 89 | #[cfg(test)] |
87 | mod tests { | 90 | mod tests { |
88 | use super::*; | 91 | use super::*; |
89 | use ra_syntax::{File, ast::ModuleItemOwner}; | 92 | use ra_syntax::{ast::ModuleItemOwner, File}; |
90 | 93 | ||
91 | fn do_check(code: &str, expected: &[&str]) { | 94 | fn do_check(code: &str, expected: &[&str]) { |
92 | let file = File::parse(&code); | 95 | let file = File::parse(&code); |
93 | let scope = ModuleScope::new(file.ast().items()); | 96 | let scope = ModuleScope::new(file.ast().items()); |
94 | let actual = scope.entries | 97 | let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>(); |
95 | .iter() | ||
96 | .map(|it| it.name()) | ||
97 | .collect::<Vec<_>>(); | ||
98 | assert_eq!(expected, actual.as_slice()); | 98 | assert_eq!(expected, actual.as_slice()); |
99 | } | 99 | } |
100 | 100 | ||
101 | #[test] | 101 | #[test] |
102 | fn test_module_scope() { | 102 | fn test_module_scope() { |
103 | do_check(" | 103 | do_check( |
104 | " | ||
104 | struct Foo; | 105 | struct Foo; |
105 | enum Bar {} | 106 | enum Bar {} |
106 | mod baz {} | 107 | mod baz {} |
@@ -110,6 +111,8 @@ mod tests { | |||
110 | t, | 111 | t, |
111 | }; | 112 | }; |
112 | type T = (); | 113 | type T = (); |
113 | ", &["Foo", "Bar", "baz", "quux", "z", "t", "T"]) | 114 | ", |
115 | &["Foo", "Bar", "baz", "quux", "z", "t", "T"], | ||
116 | ) | ||
114 | } | 117 | } |
115 | } | 118 | } |
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs index d9e4b2df7..b768b34bc 100644 --- a/crates/ra_editor/src/symbols.rs +++ b/crates/ra_editor/src/symbols.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | use crate::TextRange; | ||
2 | |||
1 | use ra_syntax::{ | 3 | use ra_syntax::{ |
2 | SyntaxKind, SyntaxNodeRef, AstNode, File, SmolStr, | ||
3 | ast::{self, NameOwner}, | ||
4 | algo::{ | 4 | algo::{ |
5 | visit::{visitor, Visitor}, | 5 | visit::{visitor, Visitor}, |
6 | walk::{walk, WalkEvent}, | 6 | walk::{walk, WalkEvent}, |
7 | }, | 7 | }, |
8 | ast::{self, NameOwner}, | ||
9 | AstNode, File, SmolStr, SyntaxKind, SyntaxNodeRef, | ||
8 | }; | 10 | }; |
9 | use crate::TextRange; | ||
10 | 11 | ||
11 | #[derive(Debug, Clone)] | 12 | #[derive(Debug, Clone)] |
12 | pub struct StructureNode { | 13 | pub struct StructureNode { |
@@ -25,9 +26,7 @@ pub struct FileSymbol { | |||
25 | } | 26 | } |
26 | 27 | ||
27 | pub fn file_symbols(file: &File) -> Vec<FileSymbol> { | 28 | pub fn file_symbols(file: &File) -> Vec<FileSymbol> { |
28 | file.syntax().descendants() | 29 | file.syntax().descendants().filter_map(to_symbol).collect() |
29 | .filter_map(to_symbol) | ||
30 | .collect() | ||
31 | } | 30 | } |
32 | 31 | ||
33 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | 32 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { |
@@ -51,23 +50,20 @@ fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | |||
51 | .accept(node)? | 50 | .accept(node)? |
52 | } | 51 | } |
53 | 52 | ||
54 | |||
55 | pub fn file_structure(file: &File) -> Vec<StructureNode> { | 53 | pub fn file_structure(file: &File) -> Vec<StructureNode> { |
56 | let mut res = Vec::new(); | 54 | let mut res = Vec::new(); |
57 | let mut stack = Vec::new(); | 55 | let mut stack = Vec::new(); |
58 | 56 | ||
59 | for event in walk(file.syntax()) { | 57 | for event in walk(file.syntax()) { |
60 | match event { | 58 | match event { |
61 | WalkEvent::Enter(node) => { | 59 | WalkEvent::Enter(node) => match structure_node(node) { |
62 | match structure_node(node) { | 60 | Some(mut symbol) => { |
63 | Some(mut symbol) => { | 61 | symbol.parent = stack.last().map(|&n| n); |
64 | symbol.parent = stack.last().map(|&n| n); | 62 | stack.push(res.len()); |
65 | stack.push(res.len()); | 63 | res.push(symbol); |
66 | res.push(symbol); | ||
67 | } | ||
68 | None => (), | ||
69 | } | 64 | } |
70 | } | 65 | None => (), |
66 | }, | ||
71 | WalkEvent::Exit(node) => { | 67 | WalkEvent::Exit(node) => { |
72 | if structure_node(node).is_some() { | 68 | if structure_node(node).is_some() { |
73 | stack.pop().unwrap(); | 69 | stack.pop().unwrap(); |
@@ -131,7 +127,8 @@ mod tests { | |||
131 | 127 | ||
132 | #[test] | 128 | #[test] |
133 | fn test_file_structure() { | 129 | fn test_file_structure() { |
134 | let file = File::parse(r#" | 130 | let file = File::parse( |
131 | r#" | ||
135 | struct Foo { | 132 | struct Foo { |
136 | x: i32 | 133 | x: i32 |
137 | } | 134 | } |
@@ -148,7 +145,8 @@ const C: i32 = 92; | |||
148 | impl E {} | 145 | impl E {} |
149 | 146 | ||
150 | impl fmt::Debug for E {} | 147 | impl fmt::Debug for E {} |
151 | "#); | 148 | "#, |
149 | ); | ||
152 | let symbols = file_structure(&file); | 150 | let symbols = file_structure(&file); |
153 | assert_eq_dbg( | 151 | assert_eq_dbg( |
154 | r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, | 152 | r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, |
diff --git a/crates/ra_editor/src/test_utils.rs b/crates/ra_editor/src/test_utils.rs index 49eb530d5..bc3d700f6 100644 --- a/crates/ra_editor/src/test_utils.rs +++ b/crates/ra_editor/src/test_utils.rs | |||
@@ -1,12 +1,8 @@ | |||
1 | use ra_syntax::{File, TextUnit, TextRange}; | ||
2 | pub use crate::_test_utils::*; | ||
3 | use crate::LocalEdit; | 1 | use crate::LocalEdit; |
2 | pub use crate::_test_utils::*; | ||
3 | use ra_syntax::{File, TextRange, TextUnit}; | ||
4 | 4 | ||
5 | pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( | 5 | pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>>(before: &str, after: &str, f: F) { |
6 | before: &str, | ||
7 | after: &str, | ||
8 | f: F, | ||
9 | ) { | ||
10 | let (before_cursor_pos, before) = extract_offset(before); | 6 | let (before_cursor_pos, before) = extract_offset(before); |
11 | let file = File::parse(&before); | 7 | let file = File::parse(&before); |
12 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | 8 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); |
@@ -19,7 +15,7 @@ pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( | |||
19 | assert_eq_text!(after, &actual); | 15 | assert_eq_text!(after, &actual); |
20 | } | 16 | } |
21 | 17 | ||
22 | pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>> ( | 18 | pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>>( |
23 | before: &str, | 19 | before: &str, |
24 | after: &str, | 20 | after: &str, |
25 | f: F, | 21 | f: F, |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 542b9e10b..50b52e7a1 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -1,32 +1,30 @@ | |||
1 | use std::mem; | 1 | use std::mem; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, | 4 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
5 | ast, | 5 | ast, |
6 | algo::{ | 6 | text_utils::{contains_offset_nonstrict, intersect}, |
7 | find_covering_node, find_leaf_at_offset, LeafAtOffset, | 7 | AstNode, File, SyntaxKind, |
8 | }, | ||
9 | text_utils::{intersect, contains_offset_nonstrict}, | ||
10 | SyntaxKind::*, | 8 | SyntaxKind::*, |
9 | SyntaxNodeRef, TextRange, TextUnit, | ||
11 | }; | 10 | }; |
12 | 11 | ||
13 | use crate::{LocalEdit, EditBuilder, find_node_at_offset}; | 12 | use crate::{find_node_at_offset, EditBuilder, LocalEdit}; |
14 | 13 | ||
15 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | 14 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { |
16 | let range = if range.is_empty() { | 15 | let range = if range.is_empty() { |
17 | let syntax = file.syntax(); | 16 | let syntax = file.syntax(); |
18 | let text = syntax.text().slice(range.start()..); | 17 | let text = syntax.text().slice(range.start()..); |
19 | let pos = match text.find('\n') { | 18 | let pos = match text.find('\n') { |
20 | None => return LocalEdit { | 19 | None => { |
21 | edit: EditBuilder::new().finish(), | 20 | return LocalEdit { |
22 | cursor_position: None | 21 | edit: EditBuilder::new().finish(), |
23 | }, | 22 | cursor_position: None, |
24 | Some(pos) => pos | 23 | } |
24 | } | ||
25 | Some(pos) => pos, | ||
25 | }; | 26 | }; |
26 | TextRange::offset_len( | 27 | TextRange::offset_len(range.start() + pos, TextUnit::of_char('\n')) |
27 | range.start() + pos, | ||
28 | TextUnit::of_char('\n'), | ||
29 | ) | ||
30 | } else { | 28 | } else { |
31 | range | 29 | range |
32 | }; | 30 | }; |
@@ -58,7 +56,9 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | |||
58 | } | 56 | } |
59 | 57 | ||
60 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | 58 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { |
61 | let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().and_then(|it| ast::Comment::cast(it))?; | 59 | let comment = find_leaf_at_offset(file.syntax(), offset) |
60 | .left_biased() | ||
61 | .and_then(|it| ast::Comment::cast(it))?; | ||
62 | 62 | ||
63 | if let ast::CommentFlavor::Multiline = comment.flavor() { | 63 | if let ast::CommentFlavor::Multiline = comment.flavor() { |
64 | return None; | 64 | return None; |
@@ -88,7 +88,7 @@ fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { | |||
88 | } | 88 | } |
89 | LeafAtOffset::Single(n) => { | 89 | LeafAtOffset::Single(n) => { |
90 | assert!(n == node); | 90 | assert!(n == node); |
91 | return Some("") | 91 | return Some(""); |
92 | } | 92 | } |
93 | LeafAtOffset::None => unreachable!(), | 93 | LeafAtOffset::None => unreachable!(), |
94 | }; | 94 | }; |
@@ -110,7 +110,12 @@ pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { | |||
110 | if contains_offset_nonstrict(expr_range, offset) && offset != expr_range.start() { | 110 | if contains_offset_nonstrict(expr_range, offset) && offset != expr_range.start() { |
111 | return None; | 111 | return None; |
112 | } | 112 | } |
113 | if file.syntax().text().slice(offset..expr_range.start()).contains('\n') { | 113 | if file |
114 | .syntax() | ||
115 | .text() | ||
116 | .slice(offset..expr_range.start()) | ||
117 | .contains('\n') | ||
118 | { | ||
114 | return None; | 119 | return None; |
115 | } | 120 | } |
116 | } else { | 121 | } else { |
@@ -125,12 +130,7 @@ pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { | |||
125 | }) | 130 | }) |
126 | } | 131 | } |
127 | 132 | ||
128 | fn remove_newline( | 133 | fn remove_newline(edit: &mut EditBuilder, node: SyntaxNodeRef, node_text: &str, offset: TextUnit) { |
129 | edit: &mut EditBuilder, | ||
130 | node: SyntaxNodeRef, | ||
131 | node_text: &str, | ||
132 | offset: TextUnit, | ||
133 | ) { | ||
134 | if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 { | 134 | if node.kind() != WHITESPACE || node_text.bytes().filter(|&b| b == b'\n').count() != 1 { |
135 | // The node is either the first or the last in the file | 135 | // The node is either the first or the last in the file |
136 | let suff = &node_text[TextRange::from_to( | 136 | let suff = &node_text[TextRange::from_to( |
@@ -156,7 +156,7 @@ fn remove_newline( | |||
156 | // | 156 | // |
157 | // into `my_function(<some-expr>)` | 157 | // into `my_function(<some-expr>)` |
158 | if join_single_expr_block(edit, node).is_some() { | 158 | if join_single_expr_block(edit, node).is_some() { |
159 | return | 159 | return; |
160 | } | 160 | } |
161 | 161 | ||
162 | // The node is between two other nodes | 162 | // The node is between two other nodes |
@@ -170,34 +170,28 @@ fn remove_newline( | |||
170 | // Adds: a single whitespace | 170 | // Adds: a single whitespace |
171 | edit.replace( | 171 | edit.replace( |
172 | TextRange::from_to(prev.range().start(), node.range().end()), | 172 | TextRange::from_to(prev.range().start(), node.range().end()), |
173 | " ".to_string() | 173 | " ".to_string(), |
174 | ); | 174 | ); |
175 | } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) { | 175 | } else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) { |
176 | // Removes: newline (incl. surrounding whitespace), start of the next comment | 176 | // Removes: newline (incl. surrounding whitespace), start of the next comment |
177 | edit.delete(TextRange::from_to( | 177 | edit.delete(TextRange::from_to( |
178 | node.range().start(), | 178 | node.range().start(), |
179 | next.syntax().range().start() + TextUnit::of_str(next.prefix()) | 179 | next.syntax().range().start() + TextUnit::of_str(next.prefix()), |
180 | )); | 180 | )); |
181 | } else { | 181 | } else { |
182 | // Remove newline but add a computed amount of whitespace characters | 182 | // Remove newline but add a computed amount of whitespace characters |
183 | edit.replace( | 183 | edit.replace(node.range(), compute_ws(prev, next).to_string()); |
184 | node.range(), | ||
185 | compute_ws(prev, next).to_string(), | ||
186 | ); | ||
187 | } | 184 | } |
188 | } | 185 | } |
189 | 186 | ||
190 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { | 187 | fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { |
191 | match (left, right) { | 188 | match (left, right) { |
192 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, | 189 | (COMMA, R_PAREN) | (COMMA, R_BRACK) => true, |
193 | _ => false | 190 | _ => false, |
194 | } | 191 | } |
195 | } | 192 | } |
196 | 193 | ||
197 | fn join_single_expr_block( | 194 | fn join_single_expr_block(edit: &mut EditBuilder, node: SyntaxNodeRef) -> Option<()> { |
198 | edit: &mut EditBuilder, | ||
199 | node: SyntaxNodeRef, | ||
200 | ) -> Option<()> { | ||
201 | let block = ast::Block::cast(node.parent()?)?; | 195 | let block = ast::Block::cast(node.parent()?)?; |
202 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; | 196 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; |
203 | let expr = single_expr(block)?; | 197 | let expr = single_expr(block)?; |
@@ -244,7 +238,7 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | |||
244 | #[cfg(test)] | 238 | #[cfg(test)] |
245 | mod tests { | 239 | mod tests { |
246 | use super::*; | 240 | use super::*; |
247 | use crate::test_utils::{check_action, extract_range, extract_offset, add_cursor}; | 241 | use crate::test_utils::{add_cursor, check_action, extract_offset, extract_range}; |
248 | 242 | ||
249 | fn check_join_lines(before: &str, after: &str) { | 243 | fn check_join_lines(before: &str, after: &str) { |
250 | check_action(before, after, |file, offset| { | 244 | check_action(before, after, |file, offset| { |
@@ -256,118 +250,142 @@ mod tests { | |||
256 | 250 | ||
257 | #[test] | 251 | #[test] |
258 | fn test_join_lines_comma() { | 252 | fn test_join_lines_comma() { |
259 | check_join_lines(r" | 253 | check_join_lines( |
254 | r" | ||
260 | fn foo() { | 255 | fn foo() { |
261 | <|>foo(1, | 256 | <|>foo(1, |
262 | ) | 257 | ) |
263 | } | 258 | } |
264 | ", r" | 259 | ", |
260 | r" | ||
265 | fn foo() { | 261 | fn foo() { |
266 | <|>foo(1) | 262 | <|>foo(1) |
267 | } | 263 | } |
268 | "); | 264 | ", |
265 | ); | ||
269 | } | 266 | } |
270 | 267 | ||
271 | #[test] | 268 | #[test] |
272 | fn test_join_lines_lambda_block() { | 269 | fn test_join_lines_lambda_block() { |
273 | check_join_lines(r" | 270 | check_join_lines( |
271 | r" | ||
274 | pub fn reparse(&self, edit: &AtomEdit) -> File { | 272 | pub fn reparse(&self, edit: &AtomEdit) -> File { |
275 | <|>self.incremental_reparse(edit).unwrap_or_else(|| { | 273 | <|>self.incremental_reparse(edit).unwrap_or_else(|| { |
276 | self.full_reparse(edit) | 274 | self.full_reparse(edit) |
277 | }) | 275 | }) |
278 | } | 276 | } |
279 | ", r" | 277 | ", |
278 | r" | ||
280 | pub fn reparse(&self, edit: &AtomEdit) -> File { | 279 | pub fn reparse(&self, edit: &AtomEdit) -> File { |
281 | <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) | 280 | <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) |
282 | } | 281 | } |
283 | "); | 282 | ", |
283 | ); | ||
284 | } | 284 | } |
285 | 285 | ||
286 | #[test] | 286 | #[test] |
287 | fn test_join_lines_block() { | 287 | fn test_join_lines_block() { |
288 | check_join_lines(r" | 288 | check_join_lines( |
289 | r" | ||
289 | fn foo() { | 290 | fn foo() { |
290 | foo(<|>{ | 291 | foo(<|>{ |
291 | 92 | 292 | 92 |
292 | }) | 293 | }) |
293 | }", r" | 294 | }", |
295 | r" | ||
294 | fn foo() { | 296 | fn foo() { |
295 | foo(<|>92) | 297 | foo(<|>92) |
296 | }"); | 298 | }", |
299 | ); | ||
297 | } | 300 | } |
298 | 301 | ||
299 | #[test] | 302 | #[test] |
300 | fn test_join_lines_normal_comments() { | 303 | fn test_join_lines_normal_comments() { |
301 | check_join_lines(r" | 304 | check_join_lines( |
305 | r" | ||
302 | fn foo() { | 306 | fn foo() { |
303 | // Hello<|> | 307 | // Hello<|> |
304 | // world! | 308 | // world! |
305 | } | 309 | } |
306 | ", r" | 310 | ", |
311 | r" | ||
307 | fn foo() { | 312 | fn foo() { |
308 | // Hello<|> world! | 313 | // Hello<|> world! |
309 | } | 314 | } |
310 | "); | 315 | ", |
316 | ); | ||
311 | } | 317 | } |
312 | 318 | ||
313 | #[test] | 319 | #[test] |
314 | fn test_join_lines_doc_comments() { | 320 | fn test_join_lines_doc_comments() { |
315 | check_join_lines(r" | 321 | check_join_lines( |
322 | r" | ||
316 | fn foo() { | 323 | fn foo() { |
317 | /// Hello<|> | 324 | /// Hello<|> |
318 | /// world! | 325 | /// world! |
319 | } | 326 | } |
320 | ", r" | 327 | ", |
328 | r" | ||
321 | fn foo() { | 329 | fn foo() { |
322 | /// Hello<|> world! | 330 | /// Hello<|> world! |
323 | } | 331 | } |
324 | "); | 332 | ", |
333 | ); | ||
325 | } | 334 | } |
326 | 335 | ||
327 | #[test] | 336 | #[test] |
328 | fn test_join_lines_mod_comments() { | 337 | fn test_join_lines_mod_comments() { |
329 | check_join_lines(r" | 338 | check_join_lines( |
339 | r" | ||
330 | fn foo() { | 340 | fn foo() { |
331 | //! Hello<|> | 341 | //! Hello<|> |
332 | //! world! | 342 | //! world! |
333 | } | 343 | } |
334 | ", r" | 344 | ", |
345 | r" | ||
335 | fn foo() { | 346 | fn foo() { |
336 | //! Hello<|> world! | 347 | //! Hello<|> world! |
337 | } | 348 | } |
338 | "); | 349 | ", |
350 | ); | ||
339 | } | 351 | } |
340 | 352 | ||
341 | #[test] | 353 | #[test] |
342 | fn test_join_lines_multiline_comments_1() { | 354 | fn test_join_lines_multiline_comments_1() { |
343 | check_join_lines(r" | 355 | check_join_lines( |
356 | r" | ||
344 | fn foo() { | 357 | fn foo() { |
345 | // Hello<|> | 358 | // Hello<|> |
346 | /* world! */ | 359 | /* world! */ |
347 | } | 360 | } |
348 | ", r" | 361 | ", |
362 | r" | ||
349 | fn foo() { | 363 | fn foo() { |
350 | // Hello<|> world! */ | 364 | // Hello<|> world! */ |
351 | } | 365 | } |
352 | "); | 366 | ", |
367 | ); | ||
353 | } | 368 | } |
354 | 369 | ||
355 | #[test] | 370 | #[test] |
356 | fn test_join_lines_multiline_comments_2() { | 371 | fn test_join_lines_multiline_comments_2() { |
357 | check_join_lines(r" | 372 | check_join_lines( |
373 | r" | ||
358 | fn foo() { | 374 | fn foo() { |
359 | // The<|> | 375 | // The<|> |
360 | /* quick | 376 | /* quick |
361 | brown | 377 | brown |
362 | fox! */ | 378 | fox! */ |
363 | } | 379 | } |
364 | ", r" | 380 | ", |
381 | r" | ||
365 | fn foo() { | 382 | fn foo() { |
366 | // The<|> quick | 383 | // The<|> quick |
367 | brown | 384 | brown |
368 | fox! */ | 385 | fox! */ |
369 | } | 386 | } |
370 | "); | 387 | ", |
388 | ); | ||
371 | } | 389 | } |
372 | 390 | ||
373 | fn check_join_lines_sel(before: &str, after: &str) { | 391 | fn check_join_lines_sel(before: &str, after: &str) { |
@@ -380,59 +398,71 @@ fn foo() { | |||
380 | 398 | ||
381 | #[test] | 399 | #[test] |
382 | fn test_join_lines_selection_fn_args() { | 400 | fn test_join_lines_selection_fn_args() { |
383 | check_join_lines_sel(r" | 401 | check_join_lines_sel( |
402 | r" | ||
384 | fn foo() { | 403 | fn foo() { |
385 | <|>foo(1, | 404 | <|>foo(1, |
386 | 2, | 405 | 2, |
387 | 3, | 406 | 3, |
388 | <|>) | 407 | <|>) |
389 | } | 408 | } |
390 | ", r" | 409 | ", |
410 | r" | ||
391 | fn foo() { | 411 | fn foo() { |
392 | foo(1, 2, 3) | 412 | foo(1, 2, 3) |
393 | } | 413 | } |
394 | "); | 414 | ", |
415 | ); | ||
395 | } | 416 | } |
396 | 417 | ||
397 | #[test] | 418 | #[test] |
398 | fn test_join_lines_selection_struct() { | 419 | fn test_join_lines_selection_struct() { |
399 | check_join_lines_sel(r" | 420 | check_join_lines_sel( |
421 | r" | ||
400 | struct Foo <|>{ | 422 | struct Foo <|>{ |
401 | f: u32, | 423 | f: u32, |
402 | }<|> | 424 | }<|> |
403 | ", r" | 425 | ", |
426 | r" | ||
404 | struct Foo { f: u32 } | 427 | struct Foo { f: u32 } |
405 | "); | 428 | ", |
429 | ); | ||
406 | } | 430 | } |
407 | 431 | ||
408 | #[test] | 432 | #[test] |
409 | fn test_join_lines_selection_dot_chain() { | 433 | fn test_join_lines_selection_dot_chain() { |
410 | check_join_lines_sel(r" | 434 | check_join_lines_sel( |
435 | r" | ||
411 | fn foo() { | 436 | fn foo() { |
412 | join(<|>type_params.type_params() | 437 | join(<|>type_params.type_params() |
413 | .filter_map(|it| it.name()) | 438 | .filter_map(|it| it.name()) |
414 | .map(|it| it.text())<|>) | 439 | .map(|it| it.text())<|>) |
415 | }", r" | 440 | }", |
441 | r" | ||
416 | fn foo() { | 442 | fn foo() { |
417 | join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text())) | 443 | join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text())) |
418 | }"); | 444 | }", |
445 | ); | ||
419 | } | 446 | } |
420 | 447 | ||
421 | #[test] | 448 | #[test] |
422 | fn test_join_lines_selection_lambda_block_body() { | 449 | fn test_join_lines_selection_lambda_block_body() { |
423 | check_join_lines_sel(r" | 450 | check_join_lines_sel( |
451 | r" | ||
424 | pub fn handle_find_matching_brace() { | 452 | pub fn handle_find_matching_brace() { |
425 | params.offsets | 453 | params.offsets |
426 | .map(|offset| <|>{ | 454 | .map(|offset| <|>{ |
427 | world.analysis().matching_brace(&file, offset).unwrap_or(offset) | 455 | world.analysis().matching_brace(&file, offset).unwrap_or(offset) |
428 | }<|>) | 456 | }<|>) |
429 | .collect(); | 457 | .collect(); |
430 | }", r" | 458 | }", |
459 | r" | ||
431 | pub fn handle_find_matching_brace() { | 460 | pub fn handle_find_matching_brace() { |
432 | params.offsets | 461 | params.offsets |
433 | .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset)) | 462 | .map(|offset| world.analysis().matching_brace(&file, offset).unwrap_or(offset)) |
434 | .collect(); | 463 | .collect(); |
435 | }"); | 464 | }", |
465 | ); | ||
436 | } | 466 | } |
437 | 467 | ||
438 | #[test] | 468 | #[test] |
@@ -454,15 +484,18 @@ pub fn handle_find_matching_brace() { | |||
454 | // let foo =; | 484 | // let foo =; |
455 | // } | 485 | // } |
456 | // "); | 486 | // "); |
457 | do_check(r" | 487 | do_check( |
488 | r" | ||
458 | fn foo() { | 489 | fn foo() { |
459 | let foo =<|> 1 + 1 | 490 | let foo =<|> 1 + 1 |
460 | } | 491 | } |
461 | ", r" | 492 | ", |
493 | r" | ||
462 | fn foo() { | 494 | fn foo() { |
463 | let foo = 1 + 1; | 495 | let foo = 1 + 1; |
464 | } | 496 | } |
465 | "); | 497 | ", |
498 | ); | ||
466 | // do_check(r" | 499 | // do_check(r" |
467 | // fn foo() { | 500 | // fn foo() { |
468 | // let foo =<|> | 501 | // let foo =<|> |
@@ -496,28 +529,34 @@ fn foo() { | |||
496 | assert!(apply_on_enter(text).is_none()) | 529 | assert!(apply_on_enter(text).is_none()) |
497 | } | 530 | } |
498 | 531 | ||
499 | do_check(r" | 532 | do_check( |
533 | r" | ||
500 | /// Some docs<|> | 534 | /// Some docs<|> |
501 | fn foo() { | 535 | fn foo() { |
502 | } | 536 | } |
503 | ", r" | 537 | ", |
538 | r" | ||
504 | /// Some docs | 539 | /// Some docs |
505 | /// <|> | 540 | /// <|> |
506 | fn foo() { | 541 | fn foo() { |
507 | } | 542 | } |
508 | "); | 543 | ", |
509 | do_check(r" | 544 | ); |
545 | do_check( | ||
546 | r" | ||
510 | impl S { | 547 | impl S { |
511 | /// Some<|> docs. | 548 | /// Some<|> docs. |
512 | fn foo() {} | 549 | fn foo() {} |
513 | } | 550 | } |
514 | ", r" | 551 | ", |
552 | r" | ||
515 | impl S { | 553 | impl S { |
516 | /// Some | 554 | /// Some |
517 | /// <|> docs. | 555 | /// <|> docs. |
518 | fn foo() {} | 556 | fn foo() {} |
519 | } | 557 | } |
520 | "); | 558 | ", |
559 | ); | ||
521 | do_check_noop(r"<|>//! docz"); | 560 | do_check_noop(r"<|>//! docz"); |
522 | } | 561 | } |
523 | } | 562 | } |