aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-10-16 14:44:24 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-10-16 14:44:24 +0100
commit1216878f7be20dd0e652fb8cdc395009fdcfae07 (patch)
tree6551967cc8c6e921b66071453ad7888a9121d326 /crates/ra_editor
parent39cb6c6d3f78b193f5873c3492e530bbd24d5dd2 (diff)
parent61f3a438d3a729a6be941bca1ff4c6a97a33f221 (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.rs49
-rw-r--r--crates/ra_editor/src/completion.rs178
-rw-r--r--crates/ra_editor/src/edit.rs13
-rw-r--r--crates/ra_editor/src/extend_selection.rs58
-rw-r--r--crates/ra_editor/src/folding_ranges.rs36
-rw-r--r--crates/ra_editor/src/lib.rs103
-rw-r--r--crates/ra_editor/src/line_index.rs119
-rw-r--r--crates/ra_editor/src/scope/fn_scope.rs150
-rw-r--r--crates/ra_editor/src/scope/mod.rs3
-rw-r--r--crates/ra_editor/src/scope/mod_scope.rs47
-rw-r--r--crates/ra_editor/src/symbols.rs34
-rw-r--r--crates/ra_editor/src/test_utils.rs12
-rw-r--r--crates/ra_editor/src/typing.rs205
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 @@
1use join_to_string::join; 1use join_to_string::join;
2 2
3use ra_syntax::{ 3use 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
14use crate::{EditBuilder, Edit, find_node_at_offset}; 11use crate::{find_node_at_offset, Edit, EditBuilder};
15 12
16#[derive(Debug)] 13#[derive(Debug)]
17pub struct LocalEdit { 14pub 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
100pub fn introduce_variable<'a>(file: &'a File, range: TextRange) -> Option<impl FnOnce() -> LocalEdit + 'a> { 100pub 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 "
192fn foo() { 199fn foo() {
193 foo(<|>1 + 1<|>); 200 foo(<|>1 + 1<|>);
194}", " 201}",
202 "
195fn foo() { 203fn 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() {
204check_action_range( 212 check_action_range(
205 " 213 "
206fn foo() { 214fn foo() {
207 <|>1 + 1<|>; 215 <|>1 + 1<|>;
208}", " 216}",
217 "
209fn foo() { 218fn 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 @@
1use rustc_hash::{FxHashMap, FxHashSet}; 1use rustc_hash::{FxHashMap, FxHashSet};
2 2
3use ra_syntax::{ 3use 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
12use crate::{ 12use 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
27pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> { 28pub 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 153fn complete_expr_keywords(
138fn 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
210fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { 235fn 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
225fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { 248fn 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
234fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { 256fn 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 @@
1use crate::{TextRange, TextUnit}; 1use crate::{TextRange, TextUnit};
2use ra_syntax::{ 2use 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)]
8pub struct Edit { 5pub struct Edit {
@@ -11,7 +8,7 @@ pub struct Edit {
11 8
12#[derive(Debug)] 9#[derive(Debug)]
13pub struct EditBuilder { 10pub struct EditBuilder {
14 atoms: Vec<AtomEdit> 11 atoms: Vec<AtomEdit>,
15} 12}
16 13
17impl EditBuilder { 14impl 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 @@
1use ra_syntax::{ 1use 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
7pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> { 8pub 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#"
192fn main() { foo<|>+bar;} 190fn 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#"
198fn main() { foo+<|>bar;} 196fn 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#"
223impl S { 213impl 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 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2 2
3use ra_syntax::{ 3use 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
56fn has_newline( 61fn 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 @@
1extern crate ra_syntax;
2extern crate superslice;
3extern crate itertools; 1extern crate itertools;
4extern crate join_to_string; 2extern crate join_to_string;
3extern crate ra_syntax;
5extern crate rustc_hash; 4extern crate rustc_hash;
5extern crate superslice;
6#[cfg(test)] 6#[cfg(test)]
7#[macro_use] 7#[macro_use]
8extern crate test_utils as _test_utils; 8extern crate test_utils as _test_utils;
9 9
10mod extend_selection;
11mod symbols;
12mod line_index;
13mod edit;
14mod folding_ranges;
15mod code_actions; 10mod code_actions;
16mod typing;
17mod completion; 11mod completion;
12mod edit;
13mod extend_selection;
14mod folding_ranges;
15mod line_index;
18mod scope; 16mod scope;
17mod symbols;
19#[cfg(test)] 18#[cfg(test)]
20mod test_utils; 19mod test_utils;
20mod typing;
21 21
22pub 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};
32pub use ra_syntax::AtomEdit;
22use ra_syntax::{ 33use 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,
28pub use ra_syntax::AtomEdit;
29pub 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
68pub fn matching_brace(file: &File, offset: TextUnit) -> Option<TextUnit> { 65pub 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
110pub fn diagnostics(file: &File) -> Vec<Diagnostic> { 105pub 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
117pub fn syntax_tree(file: &File) -> String { 115pub fn syntax_tree(file: &File) -> String {
@@ -119,7 +117,8 @@ pub fn syntax_tree(file: &File) -> String {
119} 117}
120 118
121pub fn runnables(file: &File) -> Vec<Runnable> { 119pub 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
156pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) -> Option<(SmolStr, TextRange)> { 154pub 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)]
165mod tests { 167mod 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
173fn main() {} 176fn 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#"
191fn main() {} 196fn main() {}
192 197
193#[test] 198#[test]
@@ -196,7 +201,8 @@ fn test_foo() {}
196#[test] 201#[test]
197#[ignore] 202#[ignore]
198fn test_foo() {} 203fn 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 @@
1use superslice::Ext;
2use crate::TextUnit; 1use crate::TextUnit;
2use superslice::Ext;
3 3
4#[derive(Clone, Debug, Hash, PartialEq, Eq)] 4#[derive(Clone, Debug, Hash, PartialEq, Eq)]
5pub struct LineIndex { 5pub 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 {
42fn test_line_index() { 45fn 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 @@
1use std::fmt; 1use std::fmt;
2
2use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
3 4
4use ra_syntax::{ 5use 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
10type ScopeId = usize; 11type ScopeId = usize;
@@ -19,11 +20,12 @@ pub struct FnScopes {
19impl FnScopes { 20impl 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
73pub struct ScopeEntry { 86pub struct ScopeEntry {
74 syntax: SyntaxNode 87 syntax: SyntaxNode,
75} 88}
76 89
77impl ScopeEntry { 90impl 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
96impl fmt::Debug for ScopeEntry { 108impl 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
132fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { 144fn 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)]
237struct ScopeData { 255struct ScopeData {
238 parent: Option<ScopeId>, 256 parent: Option<ScopeId>,
239 entries: Vec<ScopeEntry> 257 entries: Vec<ScopeEntry>,
240} 258}
241 259
242pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> { 260pub 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)]
256mod tests { 278mod 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;
2mod mod_scope; 2mod mod_scope;
3 3
4pub use self::{ 4pub 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 @@
1use ra_syntax::{ 1use ra_syntax::{
2 AstNode, SyntaxNode, SyntaxNodeRef, SmolStr,
3 ast::{self, AstChildren}, 2 ast::{self, AstChildren},
3 AstNode, SmolStr, SyntaxNode, SyntaxNodeRef,
4}; 4};
5 5
6pub struct ModuleScope { 6pub struct ModuleScope {
@@ -13,7 +13,8 @@ pub struct Entry {
13} 13}
14 14
15enum EntryKind { 15enum EntryKind {
16 Item, Import, 16 Item,
17 Import,
17} 18}
18 19
19impl ModuleScope { 20impl 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 {
52impl Entry { 52impl 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
76fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) { 78fn 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)]
87mod tests { 90mod 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 @@
1use crate::TextRange;
2
1use ra_syntax::{ 3use 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};
9use crate::TextRange;
10 11
11#[derive(Debug, Clone)] 12#[derive(Debug, Clone)]
12pub struct StructureNode { 13pub struct StructureNode {
@@ -25,9 +26,7 @@ pub struct FileSymbol {
25} 26}
26 27
27pub fn file_symbols(file: &File) -> Vec<FileSymbol> { 28pub 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
33fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { 32fn 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
55pub fn file_structure(file: &File) -> Vec<StructureNode> { 53pub 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#"
135struct Foo { 132struct Foo {
136 x: i32 133 x: i32
137} 134}
@@ -148,7 +145,8 @@ const C: i32 = 92;
148impl E {} 145impl E {}
149 146
150impl fmt::Debug for E {} 147impl 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 @@
1use ra_syntax::{File, TextUnit, TextRange};
2pub use crate::_test_utils::*;
3use crate::LocalEdit; 1use crate::LocalEdit;
2pub use crate::_test_utils::*;
3use ra_syntax::{File, TextRange, TextUnit};
4 4
5pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( 5pub 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
22pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>> ( 18pub 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 @@
1use std::mem; 1use std::mem;
2 2
3use ra_syntax::{ 3use 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
13use crate::{LocalEdit, EditBuilder, find_node_at_offset}; 12use crate::{find_node_at_offset, EditBuilder, LocalEdit};
14 13
15pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { 14pub 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
60pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { 58pub 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
128fn remove_newline( 133fn 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
190fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { 187fn 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
197fn join_single_expr_block( 194fn 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)]
245mod tests { 239mod 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"
260fn foo() { 255fn foo() {
261 <|>foo(1, 256 <|>foo(1,
262 ) 257 )
263} 258}
264", r" 259",
260 r"
265fn foo() { 261fn 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"
274pub fn reparse(&self, edit: &AtomEdit) -> File { 272pub 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"
280pub fn reparse(&self, edit: &AtomEdit) -> File { 279pub 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"
289fn foo() { 290fn foo() {
290 foo(<|>{ 291 foo(<|>{
291 92 292 92
292 }) 293 })
293}", r" 294}",
295 r"
294fn foo() { 296fn 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"
302fn foo() { 306fn foo() {
303 // Hello<|> 307 // Hello<|>
304 // world! 308 // world!
305} 309}
306", r" 310",
311 r"
307fn foo() { 312fn 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"
316fn foo() { 323fn foo() {
317 /// Hello<|> 324 /// Hello<|>
318 /// world! 325 /// world!
319} 326}
320", r" 327",
328 r"
321fn foo() { 329fn 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"
330fn foo() { 340fn foo() {
331 //! Hello<|> 341 //! Hello<|>
332 //! world! 342 //! world!
333} 343}
334", r" 344",
345 r"
335fn foo() { 346fn 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"
344fn foo() { 357fn foo() {
345 // Hello<|> 358 // Hello<|>
346 /* world! */ 359 /* world! */
347} 360}
348", r" 361",
362 r"
349fn foo() { 363fn 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"
358fn foo() { 374fn foo() {
359 // The<|> 375 // The<|>
360 /* quick 376 /* quick
361 brown 377 brown
362 fox! */ 378 fox! */
363} 379}
364", r" 380",
381 r"
365fn foo() { 382fn 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"
384fn foo() { 403fn foo() {
385 <|>foo(1, 404 <|>foo(1,
386 2, 405 2,
387 3, 406 3,
388 <|>) 407 <|>)
389} 408}
390 ", r" 409 ",
410 r"
391fn foo() { 411fn 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"
400struct Foo <|>{ 422struct Foo <|>{
401 f: u32, 423 f: u32,
402}<|> 424}<|>
403 ", r" 425 ",
426 r"
404struct Foo { f: u32 } 427struct 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"
411fn foo() { 436fn 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"
416fn foo() { 442fn 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"
424pub fn handle_find_matching_brace() { 452pub 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"
431pub fn handle_find_matching_brace() { 460pub 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"
458fn foo() { 489fn foo() {
459 let foo =<|> 1 + 1 490 let foo =<|> 1 + 1
460} 491}
461", r" 492",
493 r"
462fn foo() { 494fn 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<|>
501fn foo() { 535fn foo() {
502} 536}
503", r" 537",
538 r"
504/// Some docs 539/// Some docs
505/// <|> 540/// <|>
506fn foo() { 541fn foo() {
507} 542}
508"); 543",
509 do_check(r" 544 );
545 do_check(
546 r"
510impl S { 547impl S {
511 /// Some<|> docs. 548 /// Some<|> docs.
512 fn foo() {} 549 fn foo() {}
513} 550}
514", r" 551",
552 r"
515impl S { 553impl 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}