From b67295134bf5c518b39bc88abbe1bc5b9d7d3baf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Oct 2018 10:56:31 +0300 Subject: Move FnDescriptors to analyzer --- crates/ra_editor/src/scope/fn_scope.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'crates/ra_editor') diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index f10bdf657..f5c113fd9 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs @@ -83,6 +83,7 @@ impl FnScopes { } } +#[derive(PartialEq, Eq)] pub struct ScopeEntry { syntax: SyntaxNode, } @@ -251,7 +252,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] struct ScopeData { parent: Option, entries: Vec, -- cgit v1.2.3 From c02be1502c76cc504ccf7f73dce929585c94377c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Oct 2018 15:13:49 +0300 Subject: move resolve local name --- crates/ra_editor/src/lib.rs | 10 +---- crates/ra_editor/src/scope/fn_scope.rs | 73 ---------------------------------- crates/ra_editor/src/scope/mod.rs | 2 +- 3 files changed, 2 insertions(+), 83 deletions(-) (limited to 'crates/ra_editor') diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index b73eb4ac7..ddcb6c6a2 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs @@ -151,15 +151,7 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( leaf.ancestors().filter_map(N::cast).next() } -pub fn resolve_local_name( - name_ref: ast::NameRef, -) -> Option<(SmolStr, TextRange)> { - let fn_def = name_ref.syntax().ancestors().find_map(ast::FnDef::cast)?; - let scopes = scope::FnScopes::new(fn_def); - let scope_entry = scope::resolve_local_name(name_ref, &scopes)?; - let name = scope_entry.ast().name()?; - Some((scope_entry.name(), name.syntax().range())) -} + #[cfg(test)] mod tests { diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index f5c113fd9..4cb1f077c 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs @@ -258,22 +258,6 @@ struct ScopeData { entries: Vec, } -pub fn resolve_local_name<'a>( - name_ref: ast::NameRef, - scopes: &'a FnScopes, -) -> Option<&'a ScopeEntry> { - use rustc_hash::FxHashSet; - - let mut shadowed = FxHashSet::default(); - let ret = scopes - .scope_chain(name_ref.syntax()) - .flat_map(|scope| scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .filter(|entry| entry.name() == name_ref.text()) - .nth(0); - ret -} - #[cfg(test)] mod tests { use super::*; @@ -376,61 +360,4 @@ mod tests { &["x"], ); } - - fn do_check_local_name(code: &str, expected_offset: u32) { - let (off, code) = extract_offset(code); - let file = File::parse(&code); - let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); - let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); - - let scopes = FnScopes::new(fn_def); - - let local_name = resolve_local_name(name_ref, &scopes) - .unwrap() - .ast() - .name() - .unwrap(); - let expected_name = - find_node_at_offset::(file.syntax(), expected_offset.into()).unwrap(); - assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); - } - - #[test] - fn test_resolve_local_name() { - do_check_local_name( - r#" - fn foo(x: i32, y: u32) { - { - let z = x * 2; - } - { - let t = x<|> * 3; - } - }"#, - 21, - ); - } - - #[test] - fn test_resolve_local_name_declaration() { - do_check_local_name( - r#" - fn foo(x: String) { - let x : &str = &x<|>; - }"#, - 21, - ); - } - - #[test] - fn test_resolve_local_name_shadow() { - do_check_local_name( - r" - fn foo(x: String) { - let x : &str = &x; - x<|> - }", - 46, - ); - } } diff --git a/crates/ra_editor/src/scope/mod.rs b/crates/ra_editor/src/scope/mod.rs index cc2d49392..483f5e63c 100644 --- a/crates/ra_editor/src/scope/mod.rs +++ b/crates/ra_editor/src/scope/mod.rs @@ -2,6 +2,6 @@ mod fn_scope; mod mod_scope; pub use self::{ - fn_scope::{resolve_local_name, FnScopes}, + fn_scope::{FnScopes}, mod_scope::ModuleScope, }; -- cgit v1.2.3 From c09e14a4ff02f774460a70472e1aeb3c598e01dc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Oct 2018 21:03:00 +0300 Subject: remove old completion --- crates/ra_editor/src/completion.rs | 602 -------------------------------- crates/ra_editor/src/lib.rs | 5 +- crates/ra_editor/src/scope/fn_scope.rs | 363 ------------------- crates/ra_editor/src/scope/mod.rs | 7 - crates/ra_editor/src/scope/mod_scope.rs | 124 ------- 5 files changed, 1 insertion(+), 1100 deletions(-) delete mode 100644 crates/ra_editor/src/completion.rs delete mode 100644 crates/ra_editor/src/scope/fn_scope.rs delete mode 100644 crates/ra_editor/src/scope/mod.rs delete mode 100644 crates/ra_editor/src/scope/mod_scope.rs (limited to 'crates/ra_editor') diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs deleted file mode 100644 index 20c8546a4..000000000 --- a/crates/ra_editor/src/completion.rs +++ /dev/null @@ -1,602 +0,0 @@ -/// FIXME: move completion from ra_editor to ra_analysis - -use rustc_hash::{FxHashMap, FxHashSet}; - -use ra_syntax::{ - algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx}, - ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner}, - AstNode, File, - SyntaxKind::*, - SyntaxNodeRef, TextUnit, -}; - -use crate::{ - find_node_at_offset, - scope::{FnScopes, ModuleScope}, - AtomEdit, -}; - -#[derive(Debug)] -pub struct CompletionItem { - /// What user sees in pop-up - pub label: String, - /// What string is used for filtering, defaults to label - pub lookup: Option, - /// What is inserted, defaults to label - pub snippet: Option, -} - -pub fn scope_completion(file: &File, offset: TextUnit) -> Option> { - // Insert a fake ident to get a valid parse tree - let file = { - let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); - file.reparse(&edit) - }; - let mut has_completions = false; - let mut res = Vec::new(); - if let Some(name_ref) = find_node_at_offset::(file.syntax(), offset) { - has_completions = true; - complete_name_ref(&file, name_ref, &mut res); - // special case, `trait T { fn foo(i_am_a_name_ref) {} }` - if is_node::(name_ref.syntax()) { - param_completions(name_ref.syntax(), &mut res); - } - let name_range = name_ref.syntax().range(); - let top_node = name_ref - .syntax() - .ancestors() - .take_while(|it| it.range() == name_range) - .last() - .unwrap(); - match top_node.parent().map(|it| it.kind()) { - Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res), - _ => (), - } - } - if let Some(name) = find_node_at_offset::(file.syntax(), offset) { - if is_node::(name.syntax()) { - has_completions = true; - param_completions(name.syntax(), &mut res); - } - } - if has_completions { - Some(res) - } else { - None - } -} - -pub fn complete_module_items(items: AstChildren, this_item: Option, acc: &mut Vec) { - let scope = ModuleScope::new(items); - acc.extend( - scope - .entries() - .iter() - .filter(|entry| Some(entry.syntax()) != this_item.map(|it| it.syntax())) - .map(|entry| CompletionItem { - label: entry.name().to_string(), - lookup: None, - snippet: None, - }), - ); -} - -fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec) { - if !is_node::(name_ref.syntax()) { - return; - } - let mut visited_fn = false; - for node in name_ref.syntax().ancestors() { - if let Some(items) = visitor() - .visit::(|it| Some(it.items())) - .visit::(|it| Some(it.item_list()?.items())) - .accept(node) - { - if let Some(items) = items { - complete_module_items(items, Some(name_ref), acc); - } - break; - } else if !visited_fn { - if let Some(fn_def) = ast::FnDef::cast(node) { - visited_fn = true; - complete_expr_keywords(&file, fn_def, name_ref, acc); - complete_expr_snippets(acc); - let scopes = FnScopes::new(fn_def); - complete_fn(name_ref, &scopes, acc); - } - } - } -} - -fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec) { - let mut params = FxHashMap::default(); - for node in ctx.ancestors() { - let _ = visitor_ctx(&mut params) - .visit::(process) - .visit::(process) - .accept(node); - } - params - .into_iter() - .filter_map(|(label, (count, param))| { - let lookup = param.pat()?.syntax().text().to_string(); - if count < 2 { - None - } else { - Some((label, lookup)) - } - }) - .for_each(|(label, lookup)| { - acc.push(CompletionItem { - label, - lookup: Some(lookup), - snippet: None, - }) - }); - - fn process<'a, N: ast::FnDefOwner<'a>>( - node: N, - params: &mut FxHashMap)>, - ) { - node.functions() - .filter_map(|it| it.param_list()) - .flat_map(|it| it.params()) - .for_each(|param| { - let text = param.syntax().text().to_string(); - params.entry(text).or_insert((0, param)).0 += 1; - }) - } -} - -fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { - match node.ancestors().filter_map(N::cast).next() { - None => false, - Some(n) => n.syntax().range() == node.range(), - } -} - -fn complete_expr_keywords( - file: &File, - fn_def: ast::FnDef, - name_ref: ast::NameRef, - acc: &mut Vec, -) { - acc.push(keyword("if", "if $0 {}")); - acc.push(keyword("match", "match $0 {}")); - acc.push(keyword("while", "while $0 {}")); - acc.push(keyword("loop", "loop {$0}")); - - if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { - if let Some(if_expr) = find_node_at_offset::(file.syntax(), off) { - if if_expr.syntax().range().end() < name_ref.syntax().range().start() { - acc.push(keyword("else", "else {$0}")); - acc.push(keyword("else if", "else if $0 {}")); - } - } - } - if is_in_loop_body(name_ref) { - acc.push(keyword("continue", "continue")); - acc.push(keyword("break", "break")); - } - acc.extend(complete_return(fn_def, name_ref)); -} - -fn is_in_loop_body(name_ref: ast::NameRef) -> bool { - for node in name_ref.syntax().ancestors() { - if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { - break; - } - let loop_body = visitor() - .visit::(LoopBodyOwner::loop_body) - .visit::(LoopBodyOwner::loop_body) - .visit::(LoopBodyOwner::loop_body) - .accept(node); - if let Some(Some(body)) = loop_body { - if name_ref.syntax().range().is_subrange(&body.syntax().range()) { - return true; - } - } - } - false -} - -fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option { - // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast) - // .next() - // .and_then(|it| it.syntax().parent()) - // .and_then(ast::Block::cast) - // .is_some(); - - // if is_last_in_block { - // return None; - // } - - let is_stmt = match name_ref - .syntax() - .ancestors() - .filter_map(ast::ExprStmt::cast) - .next() - { - None => false, - Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), - }; - let snip = match (is_stmt, fn_def.ret_type().is_some()) { - (true, true) => "return $0;", - (true, false) => "return;", - (false, true) => "return $0", - (false, false) => "return", - }; - Some(keyword("return", snip)) -} - -fn keyword(kw: &str, snip: &str) -> CompletionItem { - CompletionItem { - label: kw.to_string(), - lookup: None, - snippet: Some(snip.to_string()), - } -} - -fn complete_expr_snippets(acc: &mut Vec) { - acc.push(CompletionItem { - label: "pd".to_string(), - lookup: None, - snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()), - }); - acc.push(CompletionItem { - label: "ppd".to_string(), - lookup: None, - snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), - }); -} - -fn complete_mod_item_snippets(acc: &mut Vec) { - acc.push(CompletionItem { - label: "tfn".to_string(), - lookup: None, - snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()), - }); - acc.push(CompletionItem { - label: "pub(crate)".to_string(), - lookup: None, - snippet: Some("pub(crate) $0".to_string()), - }) -} - -fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec) { - let mut shadowed = FxHashSet::default(); - acc.extend( - scopes - .scope_chain(name_ref.syntax()) - .flat_map(|scope| scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .map(|entry| CompletionItem { - label: entry.name().to_string(), - lookup: None, - snippet: None, - }), - ); - if scopes.self_param.is_some() { - acc.push(CompletionItem { - label: "self".to_string(), - lookup: None, - snippet: None, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use test_utils::{assert_eq_dbg, extract_offset}; - - fn check_scope_completion(code: &str, expected_completions: &str) { - let (off, code) = extract_offset(&code); - let file = File::parse(&code); - let completions = scope_completion(&file, off) - .unwrap() - .into_iter() - .filter(|c| c.snippet.is_none()) - .collect::>(); - assert_eq_dbg(expected_completions, &completions); - } - - fn check_snippet_completion(code: &str, expected_completions: &str) { - let (off, code) = extract_offset(&code); - let file = File::parse(&code); - let completions = scope_completion(&file, off) - .unwrap() - .into_iter() - .filter(|c| c.snippet.is_some()) - .collect::>(); - assert_eq_dbg(expected_completions, &completions); - } - - #[test] - fn test_completion_let_scope() { - check_scope_completion( - r" - fn quux(x: i32) { - let y = 92; - 1 + <|>; - let z = (); - } - ", - r#"[CompletionItem { label: "y", lookup: None, snippet: None }, - CompletionItem { label: "x", lookup: None, snippet: None }, - CompletionItem { label: "quux", lookup: None, snippet: None }]"#, - ); - } - - #[test] - fn test_completion_if_let_scope() { - check_scope_completion( - r" - fn quux() { - if let Some(x) = foo() { - let y = 92; - }; - if let Some(a) = bar() { - let b = 62; - 1 + <|> - } - } - ", - r#"[CompletionItem { label: "b", lookup: None, snippet: None }, - CompletionItem { label: "a", lookup: None, snippet: None }, - CompletionItem { label: "quux", lookup: None, snippet: None }]"#, - ); - } - - #[test] - fn test_completion_for_scope() { - check_scope_completion( - r" - fn quux() { - for x in &[1, 2, 3] { - <|> - } - } - ", - r#"[CompletionItem { label: "x", lookup: None, snippet: None }, - CompletionItem { label: "quux", lookup: None, snippet: None }]"#, - ); - } - - #[test] - fn test_completion_mod_scope() { - check_scope_completion( - r" - struct Foo; - enum Baz {} - fn quux() { - <|> - } - ", - r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, - CompletionItem { label: "Baz", lookup: None, snippet: None }, - CompletionItem { label: "quux", lookup: None, snippet: None }]"#, - ); - } - - #[test] - fn test_completion_mod_scope_no_self_use() { - check_scope_completion( - r" - use foo<|>; - ", - r#"[]"#, - ); - } - - #[test] - fn test_completion_mod_scope_nested() { - check_scope_completion( - r" - struct Foo; - mod m { - struct Bar; - fn quux() { <|> } - } - ", - r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, - CompletionItem { label: "quux", lookup: None, snippet: None }]"#, - ); - } - - #[test] - fn test_complete_type() { - check_scope_completion( - r" - struct Foo; - fn x() -> <|> - ", - r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, - CompletionItem { label: "x", lookup: None, snippet: None }]"#, - ) - } - - #[test] - fn test_complete_shadowing() { - check_scope_completion( - r" - fn foo() -> { - let bar = 92; - { - let bar = 62; - <|> - } - } - ", - r#"[CompletionItem { label: "bar", lookup: None, snippet: None }, - CompletionItem { label: "foo", lookup: None, snippet: None }]"#, - ) - } - - #[test] - fn test_complete_self() { - check_scope_completion( - r" - impl S { fn foo(&self) { <|> } } - ", - r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#, - ) - } - - #[test] - fn test_completion_kewords() { - check_snippet_completion(r" - fn quux() { - <|> - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - } - - #[test] - fn test_completion_else() { - check_snippet_completion(r" - fn quux() { - if true { - () - } <|> - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") }, - CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - } - - #[test] - fn test_completion_return_value() { - check_snippet_completion(r" - fn quux() -> i32 { - <|> - 92 - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - check_snippet_completion(r" - fn quux() { - <|> - 92 - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return;") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - } - - #[test] - fn test_completion_return_no_stmt() { - check_snippet_completion(r" - fn quux() -> i32 { - match () { - () => <|> - } - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - } - - #[test] - fn test_continue_break_completion() { - check_snippet_completion(r" - fn quux() -> i32 { - loop { <|> } - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "continue", lookup: None, snippet: Some("continue") }, - CompletionItem { label: "break", lookup: None, snippet: Some("break") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - check_snippet_completion(r" - fn quux() -> i32 { - loop { || { <|> } } - } - ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") }, - CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") }, - CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") }, - CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") }, - CompletionItem { label: "return", lookup: None, snippet: Some("return $0") }, - CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") }, - CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#); - } - - #[test] - fn test_param_completion_last_param() { - check_scope_completion(r" - fn foo(file_id: FileId) {} - fn bar(file_id: FileId) {} - fn baz(file<|>) {} - ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); - } - - #[test] - fn test_param_completion_nth_param() { - check_scope_completion(r" - fn foo(file_id: FileId) {} - fn bar(file_id: FileId) {} - fn baz(file<|>, x: i32) {} - ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); - } - - #[test] - fn test_param_completion_trait_param() { - check_scope_completion(r" - pub(crate) trait SourceRoot { - pub fn contains(&self, file_id: FileId) -> bool; - pub fn module_map(&self) -> &ModuleMap; - pub fn lines(&self, file_id: FileId) -> &LineIndex; - pub fn syntax(&self, file<|>) - } - ", r#"[CompletionItem { label: "self", lookup: None, snippet: None }, - CompletionItem { label: "SourceRoot", lookup: None, snippet: None }, - CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); - } - - #[test] - fn test_item_snippets() { - // check_snippet_completion(r" - // <|> - // ", - // r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n $0\n}") }]"##, - // ); - check_snippet_completion(r" - #[cfg(test)] - mod tests { - <|> - } - ", - r##"[CompletionItem { label: "tfn", lookup: None, snippet: Some("#[test]\nfn $1() {\n $0\n}") }, - CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##, - ); - } -} diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index ddcb6c6a2..02a1b2d45 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs @@ -8,12 +8,10 @@ extern crate superslice; extern crate test_utils as _test_utils; mod code_actions; -mod completion; mod edit; mod extend_selection; mod folding_ranges; mod line_index; -mod scope; mod symbols; #[cfg(test)] mod test_utils; @@ -21,7 +19,6 @@ mod typing; pub use self::{ code_actions::{add_derive, add_impl, flip_comma, introduce_variable, LocalEdit}, - completion::{scope_completion, complete_module_items, CompletionItem}, edit::{Edit, EditBuilder}, extend_selection::extend_selection, folding_ranges::{folding_ranges, Fold, FoldKind}, @@ -33,7 +30,7 @@ pub use ra_syntax::AtomEdit; use ra_syntax::{ algo::find_leaf_at_offset, ast::{self, AstNode, NameOwner}, - File, SmolStr, + File, SyntaxKind::{self, *}, SyntaxNodeRef, TextRange, TextUnit, }; diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs deleted file mode 100644 index 4cb1f077c..000000000 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ /dev/null @@ -1,363 +0,0 @@ -use std::fmt; - -use rustc_hash::FxHashMap; - -use ra_syntax::{ - algo::generate, - ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, - AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, -}; - -type ScopeId = usize; - -#[derive(Debug)] -pub struct FnScopes { - pub self_param: Option, - scopes: Vec, - scope_for: FxHashMap, -} - -impl FnScopes { - pub fn new(fn_def: ast::FnDef) -> FnScopes { - let mut scopes = FnScopes { - self_param: fn_def - .param_list() - .and_then(|it| it.self_param()) - .map(|it| it.syntax().owned()), - scopes: Vec::new(), - scope_for: FxHashMap::default(), - }; - let root = scopes.root_scope(); - scopes.add_params_bindings(root, fn_def.param_list()); - if let Some(body) = fn_def.body() { - compute_block_scopes(body, &mut scopes, root) - } - scopes - } - pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { - &self.scopes[scope].entries - } - pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator + 'a { - generate(self.scope_for(node), move |&scope| { - self.scopes[scope].parent - }) - } - fn root_scope(&mut self) -> ScopeId { - let res = self.scopes.len(); - self.scopes.push(ScopeData { - parent: None, - entries: vec![], - }); - res - } - fn new_scope(&mut self, parent: ScopeId) -> ScopeId { - let res = self.scopes.len(); - self.scopes.push(ScopeData { - parent: Some(parent), - entries: vec![], - }); - res - } - fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { - let entries = pat - .syntax() - .descendants() - .filter_map(ast::BindPat::cast) - .filter_map(ScopeEntry::new); - self.scopes[scope].entries.extend(entries); - } - fn add_params_bindings(&mut self, scope: ScopeId, params: Option) { - params - .into_iter() - .flat_map(|it| it.params()) - .filter_map(|it| it.pat()) - .for_each(|it| self.add_bindings(scope, it)); - } - fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { - self.scope_for.insert(node.owned(), scope); - } - fn scope_for(&self, node: SyntaxNodeRef) -> Option { - node.ancestors() - .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope)) - .next() - } -} - -#[derive(PartialEq, Eq)] -pub struct ScopeEntry { - syntax: SyntaxNode, -} - -impl ScopeEntry { - fn new(pat: ast::BindPat) -> Option { - if pat.name().is_some() { - Some(ScopeEntry { - syntax: pat.syntax().owned(), - }) - } else { - None - } - } - pub fn name(&self) -> SmolStr { - self.ast().name().unwrap().text() - } - pub fn ast(&self) -> ast::BindPat { - ast::BindPat::cast(self.syntax.borrowed()).unwrap() - } -} - -impl fmt::Debug for ScopeEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ScopeEntry") - .field("name", &self.name()) - .field("syntax", &self.syntax) - .finish() - } -} - -fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { - for stmt in block.statements() { - match stmt { - ast::Stmt::LetStmt(stmt) => { - if let Some(expr) = stmt.initializer() { - scopes.set_scope(expr.syntax(), scope); - compute_expr_scopes(expr, scopes, scope); - } - scope = scopes.new_scope(scope); - if let Some(pat) = stmt.pat() { - scopes.add_bindings(scope, pat); - } - } - ast::Stmt::ExprStmt(expr_stmt) => { - if let Some(expr) = expr_stmt.expr() { - scopes.set_scope(expr.syntax(), scope); - compute_expr_scopes(expr, scopes, scope); - } - } - } - } - if let Some(expr) = block.expr() { - scopes.set_scope(expr.syntax(), scope); - compute_expr_scopes(expr, scopes, scope); - } -} - -fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { - match expr { - ast::Expr::IfExpr(e) => { - let cond_scope = e - .condition() - .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); - if let Some(block) = e.then_branch() { - compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); - } - if let Some(block) = e.else_branch() { - compute_block_scopes(block, scopes, scope); - } - } - ast::Expr::BlockExpr(e) => { - if let Some(block) = e.block() { - compute_block_scopes(block, scopes, scope); - } - } - ast::Expr::LoopExpr(e) => { - if let Some(block) = e.loop_body() { - compute_block_scopes(block, scopes, scope); - } - } - ast::Expr::WhileExpr(e) => { - let cond_scope = e - .condition() - .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); - if let Some(block) = e.loop_body() { - compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); - } - } - ast::Expr::ForExpr(e) => { - if let Some(expr) = e.iterable() { - compute_expr_scopes(expr, scopes, scope); - } - let mut scope = scope; - if let Some(pat) = e.pat() { - scope = scopes.new_scope(scope); - scopes.add_bindings(scope, pat); - } - if let Some(block) = e.loop_body() { - compute_block_scopes(block, scopes, scope); - } - } - ast::Expr::LambdaExpr(e) => { - let scope = scopes.new_scope(scope); - scopes.add_params_bindings(scope, e.param_list()); - if let Some(body) = e.body() { - scopes.set_scope(body.syntax(), scope); - compute_expr_scopes(body, scopes, scope); - } - } - ast::Expr::CallExpr(e) => { - compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); - } - ast::Expr::MethodCallExpr(e) => { - compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); - } - ast::Expr::MatchExpr(e) => { - if let Some(expr) = e.expr() { - compute_expr_scopes(expr, scopes, scope); - } - for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) { - let scope = scopes.new_scope(scope); - for pat in arm.pats() { - scopes.add_bindings(scope, pat); - } - if let Some(expr) = arm.expr() { - compute_expr_scopes(expr, scopes, scope); - } - } - } - _ => expr - .syntax() - .children() - .filter_map(ast::Expr::cast) - .for_each(|expr| compute_expr_scopes(expr, scopes, scope)), - }; - - fn compute_call_scopes( - receiver: Option, - arg_list: Option, - scopes: &mut FnScopes, - scope: ScopeId, - ) { - arg_list - .into_iter() - .flat_map(|it| it.args()) - .chain(receiver) - .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); - } - - fn compute_cond_scopes( - cond: ast::Condition, - scopes: &mut FnScopes, - scope: ScopeId, - ) -> Option { - if let Some(expr) = cond.expr() { - compute_expr_scopes(expr, scopes, scope); - } - if let Some(pat) = cond.pat() { - let s = scopes.new_scope(scope); - scopes.add_bindings(s, pat); - Some(s) - } else { - None - } - } -} - -#[derive(Debug, PartialEq, Eq)] -struct ScopeData { - parent: Option, - entries: Vec, -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{find_node_at_offset, test_utils::extract_offset}; - use ra_syntax::File; - - fn do_check(code: &str, expected: &[&str]) { - let (off, code) = extract_offset(code); - let code = { - let mut buf = String::new(); - let off = u32::from(off) as usize; - buf.push_str(&code[..off]); - buf.push_str("marker"); - buf.push_str(&code[off..]); - buf - }; - let file = File::parse(&code); - let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); - let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); - let scopes = FnScopes::new(fn_def); - let actual = scopes - .scope_chain(marker.syntax()) - .flat_map(|scope| scopes.entries(scope)) - .map(|it| it.name()) - .collect::>(); - assert_eq!(actual.as_slice(), expected); - } - - #[test] - fn test_lambda_scope() { - do_check( - r" - fn quux(foo: i32) { - let f = |bar, baz: i32| { - <|> - }; - }", - &["bar", "baz", "foo"], - ); - } - - #[test] - fn test_call_scope() { - do_check( - r" - fn quux() { - f(|x| <|> ); - }", - &["x"], - ); - } - - #[test] - fn test_metod_call_scope() { - do_check( - r" - fn quux() { - z.f(|x| <|> ); - }", - &["x"], - ); - } - - #[test] - fn test_loop_scope() { - do_check( - r" - fn quux() { - loop { - let x = (); - <|> - }; - }", - &["x"], - ); - } - - #[test] - fn test_match() { - do_check( - r" - fn quux() { - match () { - Some(x) => { - <|> - } - }; - }", - &["x"], - ); - } - - #[test] - fn test_shadow_variable() { - do_check( - r" - fn foo(x: String) { - let x : &str = &x<|>; - }", - &["x"], - ); - } -} diff --git a/crates/ra_editor/src/scope/mod.rs b/crates/ra_editor/src/scope/mod.rs deleted file mode 100644 index 483f5e63c..000000000 --- a/crates/ra_editor/src/scope/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod fn_scope; -mod mod_scope; - -pub use self::{ - fn_scope::{FnScopes}, - mod_scope::ModuleScope, -}; diff --git a/crates/ra_editor/src/scope/mod_scope.rs b/crates/ra_editor/src/scope/mod_scope.rs deleted file mode 100644 index 818749a12..000000000 --- a/crates/ra_editor/src/scope/mod_scope.rs +++ /dev/null @@ -1,124 +0,0 @@ -/// FIXME: this is now moved to ra_analysis::descriptors::module::scope. -/// -/// Current copy will be deleted as soon as we move the rest of the completion -/// to the analyezer. - - -use ra_syntax::{ - ast::{self, AstChildren}, - AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, -}; - -pub struct ModuleScope { - entries: Vec, -} - -pub struct Entry { - node: SyntaxNode, - kind: EntryKind, -} - -enum EntryKind { - Item, - Import, -} - -impl ModuleScope { - pub fn new(items: AstChildren) -> ModuleScope { - let mut entries = Vec::new(); - for item in items { - let entry = match item { - ast::ModuleItem::StructDef(item) => Entry::new_item(item), - ast::ModuleItem::EnumDef(item) => Entry::new_item(item), - ast::ModuleItem::FnDef(item) => Entry::new_item(item), - ast::ModuleItem::ConstDef(item) => Entry::new_item(item), - ast::ModuleItem::StaticDef(item) => Entry::new_item(item), - ast::ModuleItem::TraitDef(item) => Entry::new_item(item), - ast::ModuleItem::TypeDef(item) => Entry::new_item(item), - ast::ModuleItem::Module(item) => Entry::new_item(item), - ast::ModuleItem::UseItem(item) => { - if let Some(tree) = item.use_tree() { - collect_imports(tree, &mut entries); - } - continue; - } - ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue, - }; - entries.extend(entry) - } - - ModuleScope { entries } - } - - pub fn entries(&self) -> &[Entry] { - self.entries.as_slice() - } -} - -impl Entry { - fn new_item<'a>(item: impl ast::NameOwner<'a>) -> Option { - let name = item.name()?; - Some(Entry { - node: name.syntax().owned(), - kind: EntryKind::Item, - }) - } - fn new_import(path: ast::Path) -> Option { - let name_ref = path.segment()?.name_ref()?; - Some(Entry { - node: name_ref.syntax().owned(), - kind: EntryKind::Import, - }) - } - pub fn name(&self) -> SmolStr { - match self.kind { - EntryKind::Item => ast::Name::cast(self.node.borrowed()).unwrap().text(), - EntryKind::Import => ast::NameRef::cast(self.node.borrowed()).unwrap().text(), - } - } - pub fn syntax(&self) -> SyntaxNodeRef { - self.node.borrowed() - } -} - -fn collect_imports(tree: ast::UseTree, acc: &mut Vec) { - if let Some(use_tree_list) = tree.use_tree_list() { - return use_tree_list - .use_trees() - .for_each(|it| collect_imports(it, acc)); - } - if let Some(path) = tree.path() { - acc.extend(Entry::new_import(path)); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ra_syntax::{ast::ModuleItemOwner, File}; - - fn do_check(code: &str, expected: &[&str]) { - let file = File::parse(&code); - let scope = ModuleScope::new(file.ast().items()); - let actual = scope.entries.iter().map(|it| it.name()).collect::>(); - assert_eq!(expected, actual.as_slice()); - } - - #[test] - fn test_module_scope() { - do_check( - " - struct Foo; - enum Bar {} - mod baz {} - fn quux() {} - use x::{ - y::z, - t, - }; - type T = (); - ", - &["Foo", "Bar", "baz", "quux", "z", "t", "T"], - ) - } -} -- cgit v1.2.3