From 4bed588001a1d6cd5c83a3eefc6ef77c439de40b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 28 Aug 2020 21:28:30 +0300 Subject: First steps for mod<|> completion --- crates/ide/src/completion/completion_context.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 47355d5dc..4d8b3670b 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here -use base_db::SourceDatabase; -use hir::{Semantics, SemanticsScope, Type}; +use base_db::{FileLoader, SourceDatabase}; +use hir::{ModuleSource, Semantics, SemanticsScope, Type}; use ide_db::RootDatabase; use syntax::{ algo::{find_covering_element, find_node_at_offset}, @@ -112,6 +112,27 @@ impl<'a> CompletionContext<'a> { }; let fake_ident_token = file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); + { + let module_names_for_import = sema + .to_module_def(position.file_id) + .and_then(|current_module| { + let definition_source = current_module.definition_source(db); + if !matches!(definition_source.value, ModuleSource::SourceFile(_)) { + return None; + } + let definition_source_file = definition_source.file_id.original_file(db); + + // TODO kb for all possible candidates + let zz = db.list_some_random_files_todo(definition_source_file); + dbg!(zz); + // TODO kb exlude existing children from the candidates + let existing_children = current_module.children(db).collect::>(); + dbg!(existing_children); + None::> + }) + .unwrap_or_default(); + dbg!(module_names_for_import); + }; let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); let original_token = -- cgit v1.2.3 From 17870a3e2c39770a99f9ab5ce090abbe1dc334d2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 3 Sep 2020 23:18:23 +0300 Subject: Better API --- crates/ide/src/completion/completion_context.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 4d8b3670b..cbfc77a46 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -120,11 +120,10 @@ impl<'a> CompletionContext<'a> { if !matches!(definition_source.value, ModuleSource::SourceFile(_)) { return None; } - let definition_source_file = definition_source.file_id.original_file(db); - - // TODO kb for all possible candidates - let zz = db.list_some_random_files_todo(definition_source_file); - dbg!(zz); + let module_definition_source_file = definition_source.file_id.original_file(db); + let mod_declaration_candidates = + db.possible_sudmobules(module_definition_source_file); + dbg!(mod_declaration_candidates); // TODO kb exlude existing children from the candidates let existing_children = current_module.children(db).collect::>(); dbg!(existing_children); -- cgit v1.2.3 From 8aa740dab46f138cacdf6391d46c87d6df810161 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Sep 2020 02:25:00 +0300 Subject: Happy path implemented --- crates/ide/src/completion/completion_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index cbfc77a46..b4c6eeb35 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -122,7 +122,7 @@ impl<'a> CompletionContext<'a> { } let module_definition_source_file = definition_source.file_id.original_file(db); let mod_declaration_candidates = - db.possible_sudmobules(module_definition_source_file); + db.possible_sudmobule_names(module_definition_source_file); dbg!(mod_declaration_candidates); // TODO kb exlude existing children from the candidates let existing_children = current_module.children(db).collect::>(); -- cgit v1.2.3 From 486c5c3285682408b125613475a34a0bc9a2c097 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Sep 2020 15:13:31 +0300 Subject: Exclude special files --- crates/ide/src/completion/completion_context.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index b4c6eeb35..74cd16e0a 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -117,9 +117,6 @@ impl<'a> CompletionContext<'a> { .to_module_def(position.file_id) .and_then(|current_module| { let definition_source = current_module.definition_source(db); - if !matches!(definition_source.value, ModuleSource::SourceFile(_)) { - return None; - } let module_definition_source_file = definition_source.file_id.original_file(db); let mod_declaration_candidates = db.possible_sudmobule_names(module_definition_source_file); -- cgit v1.2.3 From b2bcc5278db23c3ba0a4f47a3ef6ee411aaaa8dc Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 6 Sep 2020 01:41:18 +0300 Subject: Properly handle special cases (binaries, mod.rs) --- crates/ide/src/completion/completion_context.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 74cd16e0a..a8fe44083 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here use base_db::{FileLoader, SourceDatabase}; -use hir::{ModuleSource, Semantics, SemanticsScope, Type}; +use hir::{Semantics, SemanticsScope, Type}; use ide_db::RootDatabase; use syntax::{ algo::{find_covering_element, find_node_at_offset}, @@ -123,11 +123,9 @@ impl<'a> CompletionContext<'a> { dbg!(mod_declaration_candidates); // TODO kb exlude existing children from the candidates let existing_children = current_module.children(db).collect::>(); - dbg!(existing_children); None::> }) .unwrap_or_default(); - dbg!(module_names_for_import); }; let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); -- cgit v1.2.3 From 6ba479cd058aa54a9f161085c7ff9ac1f12d8df3 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 7 Sep 2020 19:21:39 +0300 Subject: Finally cretae the mod completion module --- crates/ide/src/completion.rs | 2 ++ crates/ide/src/completion/complete_mod.rs | 39 +++++++++++++++++++++++++ crates/ide/src/completion/completion_context.rs | 18 +----------- 3 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 crates/ide/src/completion/complete_mod.rs (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index 33bed6991..daea2aa95 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs @@ -19,6 +19,7 @@ mod complete_unqualified_path; mod complete_postfix; mod complete_macro_in_item_position; mod complete_trait_impl; +mod complete_mod; use ide_db::RootDatabase; @@ -124,6 +125,7 @@ pub(crate) fn completions( complete_postfix::complete_postfix(&mut acc, &ctx); complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); complete_trait_impl::complete_trait_impl(&mut acc, &ctx); + complete_mod::complete_mod(&mut acc, &ctx); Some(acc) } diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs new file mode 100644 index 000000000..4c1e79603 --- /dev/null +++ b/crates/ide/src/completion/complete_mod.rs @@ -0,0 +1,39 @@ +//! Completes mod declarations. + +use base_db::FileLoader; +use hir::ModuleSource; + +use super::{completion_context::CompletionContext, completion_item::Completions}; + +/// Complete mod declaration, i.e. `mod <|> ;` +pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) { + let module_names_for_import = ctx + .sema + // TODO kb this is wrong, since we need not the file module + .to_module_def(ctx.position.file_id) + .and_then(|current_module| { + dbg!(current_module.name(ctx.db)); + dbg!(current_module.definition_source(ctx.db)); + dbg!(current_module.declaration_source(ctx.db)); + let mut zz = Vec::new(); + let mut vv = Some(current_module); + while let Some(ModuleSource::Module(_)) = + vv.map(|vv| vv.definition_source(ctx.db).value) + { + zz.push(current_module.name(ctx.db)); + vv = current_module.parent(ctx.db); + } + dbg!(zz); + let definition_source = current_module.definition_source(ctx.db); + // TODO kb filter out declarations in possible_sudmobule_names + // let declaration_source = current_module.declaration_source(ctx.db); + let module_definition_source_file = definition_source.file_id.original_file(ctx.db); + let mod_declaration_candidates = + ctx.db.possible_sudmobule_names(module_definition_source_file); + dbg!(mod_declaration_candidates); + // TODO kb exlude existing children from the candidates + let existing_children = current_module.children(ctx.db).collect::>(); + None::> + }) + .unwrap_or_default(); +} diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index a8fe44083..31886942a 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here use base_db::{FileLoader, SourceDatabase}; -use hir::{Semantics, SemanticsScope, Type}; +use hir::{ModuleSource, Semantics, SemanticsScope, Type}; use ide_db::RootDatabase; use syntax::{ algo::{find_covering_element, find_node_at_offset}, @@ -112,22 +112,6 @@ impl<'a> CompletionContext<'a> { }; let fake_ident_token = file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); - { - let module_names_for_import = sema - .to_module_def(position.file_id) - .and_then(|current_module| { - let definition_source = current_module.definition_source(db); - let module_definition_source_file = definition_source.file_id.original_file(db); - let mod_declaration_candidates = - db.possible_sudmobule_names(module_definition_source_file); - dbg!(mod_declaration_candidates); - // TODO kb exlude existing children from the candidates - let existing_children = current_module.children(db).collect::>(); - None::> - }) - .unwrap_or_default(); - }; - let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); let original_token = original_file.syntax().token_at_offset(position.offset).left_biased()?; -- cgit v1.2.3 From f9c14ac7204c38633e70b3efd47a5b1f9056afd0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 7 Sep 2020 20:52:37 +0300 Subject: Move most of the logic into the completion module --- crates/ide/src/completion/complete_mod.rs | 116 +++++++++++++++++++----- crates/ide/src/completion/completion_context.rs | 5 +- 2 files changed, 98 insertions(+), 23 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 4c1e79603..da3d93bad 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -1,35 +1,61 @@ //! Completes mod declarations. -use base_db::FileLoader; -use hir::ModuleSource; +use base_db::{SourceDatabaseExt, VfsPath}; +use hir::{Module, ModuleSource}; +use ide_db::RootDatabase; use super::{completion_context::CompletionContext, completion_item::Completions}; /// Complete mod declaration, i.e. `mod <|> ;` pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) { let module_names_for_import = ctx - .sema - // TODO kb this is wrong, since we need not the file module - .to_module_def(ctx.position.file_id) + .scope + .module() .and_then(|current_module| { - dbg!(current_module.name(ctx.db)); - dbg!(current_module.definition_source(ctx.db)); - dbg!(current_module.declaration_source(ctx.db)); - let mut zz = Vec::new(); - let mut vv = Some(current_module); - while let Some(ModuleSource::Module(_)) = - vv.map(|vv| vv.definition_source(ctx.db).value) - { - zz.push(current_module.name(ctx.db)); - vv = current_module.parent(ctx.db); - } - dbg!(zz); - let definition_source = current_module.definition_source(ctx.db); + let module_path = path_to_closest_containing_module_file(current_module, ctx.db); // TODO kb filter out declarations in possible_sudmobule_names // let declaration_source = current_module.declaration_source(ctx.db); - let module_definition_source_file = definition_source.file_id.original_file(ctx.db); - let mod_declaration_candidates = - ctx.db.possible_sudmobule_names(module_definition_source_file); + let module_definition_source_file = + current_module.definition_source(ctx.db).file_id.original_file(ctx.db); + + let source_root_id = ctx.db.file_source_root(module_definition_source_file); + let source_root = ctx.db.source_root(source_root_id); + let directory_to_look_for_submodules = source_root + .path_for_file(&module_definition_source_file) + .and_then(|module_file_path| get_directory_with_submodules(module_file_path))?; + + let mod_declaration_candidates = source_root + .iter() + .filter(|submodule_file| submodule_file != &module_definition_source_file) + .filter_map(|submodule_file| { + let submodule_path = source_root.path_for_file(&submodule_file)?; + if submodule_path.parent()? == directory_to_look_for_submodules { + submodule_path.file_name_and_extension() + } else { + None + } + }) + .filter_map(|file_name_and_extension| { + match file_name_and_extension { + // TODO kb wrong resolution for nested non-file modules (mod tests { mod <|> }) + // TODO kb in src/bin when a module is included into another, + // the included file gets "moved" into a directory below and now cannot add any other modules + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => None, + (file_name, Some("rs")) => Some(file_name.to_owned()), + (subdirectory_name, None) => { + let mod_rs_path = directory_to_look_for_submodules + .join(subdirectory_name)? + .join("mod.rs")?; + if source_root.file_for_path(&mod_rs_path).is_some() { + Some(subdirectory_name.to_owned()) + } else { + None + } + } + _ => None, + } + }) + .collect::>(); dbg!(mod_declaration_candidates); // TODO kb exlude existing children from the candidates let existing_children = current_module.children(ctx.db).collect::>(); @@ -37,3 +63,51 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) { }) .unwrap_or_default(); } + +fn path_to_closest_containing_module_file( + current_module: Module, + db: &RootDatabase, +) -> Vec { + let mut path = Vec::new(); + + let mut current_module = Some(current_module); + while let Some(ModuleSource::Module(_)) = + current_module.map(|module| module.definition_source(db).value) + { + if let Some(module) = current_module { + path.insert(0, module); + current_module = module.parent(db); + } else { + current_module = None; + } + } + + path +} + +fn get_directory_with_submodules(module_file_path: &VfsPath) -> Option { + let module_directory_path = module_file_path.parent()?; + match module_file_path.file_name_and_extension()? { + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { + Some(module_directory_path) + } + (regular_rust_file_name, Some("rs")) => { + if matches!( + ( + module_directory_path + .parent() + .as_ref() + .and_then(|path| path.file_name_and_extension()), + module_directory_path.file_name_and_extension(), + ), + (Some(("src", None)), Some(("bin", None))) + ) { + // files in /src/bin/ can import each other directly + Some(module_directory_path) + } else { + module_directory_path.join(regular_rust_file_name) + } + } + _ => None, + } +} diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 31886942a..47355d5dc 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -1,7 +1,7 @@ //! FIXME: write short doc here -use base_db::{FileLoader, SourceDatabase}; -use hir::{ModuleSource, Semantics, SemanticsScope, Type}; +use base_db::SourceDatabase; +use hir::{Semantics, SemanticsScope, Type}; use ide_db::RootDatabase; use syntax::{ algo::{find_covering_element, find_node_at_offset}, @@ -112,6 +112,7 @@ impl<'a> CompletionContext<'a> { }; let fake_ident_token = file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); + let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); let original_token = original_file.syntax().token_at_offset(position.offset).left_biased()?; -- cgit v1.2.3 From 3fd6f451417fee0e8d95d06fb298c94b22bca917 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 7 Sep 2020 21:39:23 +0300 Subject: Properly handle nested submodules in the same file --- crates/ide/src/completion/complete_mod.rs | 149 ++++++++++++++++-------------- 1 file changed, 79 insertions(+), 70 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index da3d93bad..def8b8968 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -7,87 +7,66 @@ use ide_db::RootDatabase; use super::{completion_context::CompletionContext, completion_item::Completions}; /// Complete mod declaration, i.e. `mod <|> ;` -pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) { - let module_names_for_import = ctx - .scope - .module() - .and_then(|current_module| { - let module_path = path_to_closest_containing_module_file(current_module, ctx.db); - // TODO kb filter out declarations in possible_sudmobule_names - // let declaration_source = current_module.declaration_source(ctx.db); - let module_definition_source_file = - current_module.definition_source(ctx.db).file_id.original_file(ctx.db); +pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + let current_module = ctx.scope.module()?; - let source_root_id = ctx.db.file_source_root(module_definition_source_file); - let source_root = ctx.db.source_root(source_root_id); - let directory_to_look_for_submodules = source_root - .path_for_file(&module_definition_source_file) - .and_then(|module_file_path| get_directory_with_submodules(module_file_path))?; + // TODO kb filter out declarations in possible_sudmobule_names + // let declaration_source = current_module.declaration_source(ctx.db); + let module_definition_source_file = + current_module.definition_source(ctx.db).file_id.original_file(ctx.db); + let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_source_file)); + let directory_to_look_for_submodules = directory_to_look_for_submodules( + current_module, + ctx.db, + source_root.path_for_file(&module_definition_source_file)?, + )?; - let mod_declaration_candidates = source_root - .iter() - .filter(|submodule_file| submodule_file != &module_definition_source_file) - .filter_map(|submodule_file| { - let submodule_path = source_root.path_for_file(&submodule_file)?; - if submodule_path.parent()? == directory_to_look_for_submodules { - submodule_path.file_name_and_extension() + let mod_declaration_candidates = source_root + .iter() + .filter(|submodule_file| submodule_file != &module_definition_source_file) + .filter_map(|submodule_file| { + let submodule_path = source_root.path_for_file(&submodule_file)?; + if submodule_path.parent()? == directory_to_look_for_submodules { + submodule_path.file_name_and_extension() + } else { + None + } + }) + .filter_map(|file_name_and_extension| { + match file_name_and_extension { + // TODO kb in src/bin when a module is included into another, + // the included file gets "moved" into a directory below and now cannot add any other modules + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => None, + (file_name, Some("rs")) => Some(file_name.to_owned()), + (subdirectory_name, None) => { + let mod_rs_path = + directory_to_look_for_submodules.join(subdirectory_name)?.join("mod.rs")?; + if source_root.file_for_path(&mod_rs_path).is_some() { + Some(subdirectory_name.to_owned()) } else { None } - }) - .filter_map(|file_name_and_extension| { - match file_name_and_extension { - // TODO kb wrong resolution for nested non-file modules (mod tests { mod <|> }) - // TODO kb in src/bin when a module is included into another, - // the included file gets "moved" into a directory below and now cannot add any other modules - ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => None, - (file_name, Some("rs")) => Some(file_name.to_owned()), - (subdirectory_name, None) => { - let mod_rs_path = directory_to_look_for_submodules - .join(subdirectory_name)? - .join("mod.rs")?; - if source_root.file_for_path(&mod_rs_path).is_some() { - Some(subdirectory_name.to_owned()) - } else { - None - } - } - _ => None, - } - }) - .collect::>(); - dbg!(mod_declaration_candidates); - // TODO kb exlude existing children from the candidates - let existing_children = current_module.children(ctx.db).collect::>(); - None::> + } + _ => None, + } }) - .unwrap_or_default(); -} + .collect::>(); + dbg!(mod_declaration_candidates); -fn path_to_closest_containing_module_file( - current_module: Module, - db: &RootDatabase, -) -> Vec { - let mut path = Vec::new(); - - let mut current_module = Some(current_module); - while let Some(ModuleSource::Module(_)) = - current_module.map(|module| module.definition_source(db).value) - { - if let Some(module) = current_module { - path.insert(0, module); - current_module = module.parent(db); - } else { - current_module = None; - } - } + // TODO kb exlude existing children from the candidates + let existing_children = current_module.children(ctx.db).collect::>(); - path + Some(()) } -fn get_directory_with_submodules(module_file_path: &VfsPath) -> Option { +fn directory_to_look_for_submodules( + module: Module, + db: &RootDatabase, + module_file_path: &VfsPath, +) -> Option { let module_directory_path = module_file_path.parent()?; - match module_file_path.file_name_and_extension()? { + + let base_directory = match module_file_path.file_name_and_extension()? { ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { Some(module_directory_path) } @@ -109,5 +88,35 @@ fn get_directory_with_submodules(module_file_path: &VfsPath) -> Option } } _ => None, + }?; + + let mut resulting_path = base_directory; + for module in module_chain_to_containing_module_file(module, db) { + if let Some(name) = module.name(db) { + resulting_path = resulting_path.join(&name.to_string())?; + } + } + + Some(resulting_path) +} + +fn module_chain_to_containing_module_file( + current_module: Module, + db: &RootDatabase, +) -> Vec { + let mut path = Vec::new(); + + let mut current_module = Some(current_module); + while let Some(ModuleSource::Module(_)) = + current_module.map(|module| module.definition_source(db).value) + { + if let Some(module) = current_module { + path.insert(0, module); + current_module = module.parent(db); + } else { + current_module = None; + } } + + path } -- cgit v1.2.3 From cc43abcde87ed4a834f3b56a96ef165d8e4f0d86 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 00:00:00 +0300 Subject: Less false positive completion candidates --- crates/ide/src/completion/complete_mod.rs | 103 +++++++++++++++++------------- 1 file changed, 59 insertions(+), 44 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index def8b8968..c5757a310 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -3,6 +3,7 @@ use base_db::{SourceDatabaseExt, VfsPath}; use hir::{Module, ModuleSource}; use ide_db::RootDatabase; +use rustc_hash::FxHashSet; use super::{completion_context::CompletionContext, completion_item::Completions}; @@ -10,84 +11,98 @@ use super::{completion_context::CompletionContext, completion_item::Completions} pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let current_module = ctx.scope.module()?; - // TODO kb filter out declarations in possible_sudmobule_names - // let declaration_source = current_module.declaration_source(ctx.db); - let module_definition_source_file = + let module_definition_file = current_module.definition_source(ctx.db).file_id.original_file(ctx.db); - let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_source_file)); + let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file)); let directory_to_look_for_submodules = directory_to_look_for_submodules( current_module, ctx.db, - source_root.path_for_file(&module_definition_source_file)?, + source_root.path_for_file(&module_definition_file)?, )?; + let existing_mod_declarations = current_module + .children(ctx.db) + .filter_map(|module| Some(module.name(ctx.db)?.to_string())) + .collect::>(); + + let module_declaration_file = + current_module.declaration_source(ctx.db).map(|module_declaration_source_file| { + module_declaration_source_file.file_id.original_file(ctx.db) + }); + let mod_declaration_candidates = source_root .iter() - .filter(|submodule_file| submodule_file != &module_definition_source_file) + .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file) + .filter(|submodule_candidate_file| { + Some(submodule_candidate_file) != module_declaration_file.as_ref() + }) .filter_map(|submodule_file| { let submodule_path = source_root.path_for_file(&submodule_file)?; - if submodule_path.parent()? == directory_to_look_for_submodules { + if !is_special_rust_file_path(&submodule_path) + && submodule_path.parent()? == directory_to_look_for_submodules + { submodule_path.file_name_and_extension() } else { None } }) - .filter_map(|file_name_and_extension| { - match file_name_and_extension { - // TODO kb in src/bin when a module is included into another, - // the included file gets "moved" into a directory below and now cannot add any other modules - ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => None, - (file_name, Some("rs")) => Some(file_name.to_owned()), - (subdirectory_name, None) => { - let mod_rs_path = - directory_to_look_for_submodules.join(subdirectory_name)?.join("mod.rs")?; - if source_root.file_for_path(&mod_rs_path).is_some() { - Some(subdirectory_name.to_owned()) - } else { - None - } + .filter_map(|submodule_file_name_and_extension| match submodule_file_name_and_extension { + (file_name, Some("rs")) => Some(file_name.to_owned()), + (subdirectory_name, None) => { + let mod_rs_path = + directory_to_look_for_submodules.join(subdirectory_name)?.join("mod.rs")?; + if source_root.file_for_path(&mod_rs_path).is_some() { + Some(subdirectory_name.to_owned()) + } else { + None } - _ => None, } + _ => None, }) + .filter(|name| !existing_mod_declarations.contains(name)) .collect::>(); dbg!(mod_declaration_candidates); // TODO kb exlude existing children from the candidates - let existing_children = current_module.children(ctx.db).collect::>(); Some(()) } +fn is_special_rust_file_path(path: &VfsPath) -> bool { + matches!( + path.file_name_and_extension(), + Some(("mod", Some("rs"))) | Some(("lib", Some("rs"))) | Some(("main", Some("rs"))) + ) +} + fn directory_to_look_for_submodules( module: Module, db: &RootDatabase, module_file_path: &VfsPath, ) -> Option { let module_directory_path = module_file_path.parent()?; - - let base_directory = match module_file_path.file_name_and_extension()? { - ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { + let base_directory = if is_special_rust_file_path(module_file_path) { + Some(module_directory_path) + } else if let (regular_rust_file_name, Some("rs")) = + module_file_path.file_name_and_extension()? + { + if matches!( + ( + module_directory_path + .parent() + .as_ref() + .and_then(|path| path.file_name_and_extension()), + module_directory_path.file_name_and_extension(), + ), + (Some(("src", None)), Some(("bin", None))) + ) { + // files in /src/bin/ can import each other directly Some(module_directory_path) + } else { + module_directory_path.join(regular_rust_file_name) } - (regular_rust_file_name, Some("rs")) => { - if matches!( - ( - module_directory_path - .parent() - .as_ref() - .and_then(|path| path.file_name_and_extension()), - module_directory_path.file_name_and_extension(), - ), - (Some(("src", None)), Some(("bin", None))) - ) { - // files in /src/bin/ can import each other directly - Some(module_directory_path) - } else { - module_directory_path.join(regular_rust_file_name) - } - } - _ => None, + } else { + None }?; let mut resulting_path = base_directory; -- cgit v1.2.3 From 57a260f579fec4082aa9e7a30d4b190f06d45877 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 00:54:58 +0300 Subject: Properly reacto to keywords --- crates/ide/src/completion/complete_attribute.rs | 4 ++++ crates/ide/src/completion/complete_mod.rs | 8 +++++++- crates/ide/src/completion/complete_qualified_path.rs | 2 +- crates/ide/src/completion/complete_unqualified_path.rs | 1 + crates/ide/src/completion/completion_context.rs | 7 +++++-- crates/ide/src/completion/patterns.rs | 10 ++++++++++ 6 files changed, 28 insertions(+), 4 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs index 0abfaebcb..6394189f0 100644 --- a/crates/ide/src/completion/complete_attribute.rs +++ b/crates/ide/src/completion/complete_attribute.rs @@ -13,6 +13,10 @@ use crate::completion::{ }; pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + if ctx.mod_is_prev { + return None; + } + let attribute = ctx.attribute_under_caret.as_ref()?; match (attribute.path(), attribute.token_tree()) { (Some(path), Some(token_tree)) if path.to_string() == "derive" => { diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index c5757a310..5d41d0f69 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -9,6 +9,12 @@ use super::{completion_context::CompletionContext, completion_item::Completions} /// Complete mod declaration, i.e. `mod <|> ;` pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + let _p = profile::span("completion::complete_mod"); + + if !ctx.mod_is_prev { + return None; + } + let current_module = ctx.scope.module()?; let module_definition_file = @@ -63,7 +69,7 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op .collect::>(); dbg!(mod_declaration_candidates); - // TODO kb exlude existing children from the candidates + // TODO kb actually add the results Some(()) } diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index accb09f7e..351461351 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs @@ -13,7 +13,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon None => return, }; - if ctx.attribute_under_caret.is_some() { + if ctx.attribute_under_caret.is_some() || ctx.mod_is_prev { return; } diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index 1f1b682a7..9f2dc16ab 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs @@ -13,6 +13,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.record_lit_syntax.is_some() || ctx.record_pat_syntax.is_some() || ctx.attribute_under_caret.is_some() + || ctx.mod_is_prev { return; } diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 47355d5dc..d289aac27 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -19,7 +19,7 @@ use crate::{ has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, - is_in_loop_body, is_match_arm, unsafe_is_prev, + is_in_loop_body, is_match_arm, mod_is_prev, unsafe_is_prev, }, CompletionConfig, }, @@ -77,6 +77,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_path_type: bool, pub(super) has_type_args: bool, pub(super) attribute_under_caret: Option, + pub(super) mod_is_prev: bool, pub(super) unsafe_is_prev: bool, pub(super) if_is_prev: bool, pub(super) block_expr_parent: bool, @@ -152,6 +153,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, attribute_under_caret: None, + mod_is_prev: false, unsafe_is_prev: false, in_loop_body: false, ref_pat_parent: false, @@ -238,7 +240,8 @@ impl<'a> CompletionContext<'a> { self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); self.is_match_arm = is_match_arm(syntax_element.clone()); self.has_item_list_or_source_file_parent = - has_item_list_or_source_file_parent(syntax_element); + has_item_list_or_source_file_parent(syntax_element.clone()); + self.mod_is_prev = mod_is_prev(syntax_element); } fn fill( diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index c6ae589db..bc4ce4d6f 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -115,6 +115,16 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { .filter(|it| it.kind() == IF_KW) .is_some() } + +// TODO kb generify? +pub(crate) fn mod_is_prev(element: SyntaxElement) -> bool { + element + .into_token() + .and_then(|it| previous_non_trivia_token(it)) + .filter(|it| it.kind() == MOD_KW) + .is_some() +} + #[test] fn test_if_is_prev() { check_pattern_is_applicable(r"if l<|>", if_is_prev); -- cgit v1.2.3 From 9fb83211f95a450fdadf05f8f64be053f14dc57e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 01:24:16 +0300 Subject: Complete semicolon when needed --- crates/ide/src/completion/complete_attribute.rs | 2 +- crates/ide/src/completion/complete_mod.rs | 33 +++++++++++++++------- .../ide/src/completion/complete_qualified_path.rs | 2 +- .../src/completion/complete_unqualified_path.rs | 2 +- crates/ide/src/completion/completion_context.rs | 8 +++--- crates/ide/src/completion/patterns.rs | 9 ------ 6 files changed, 30 insertions(+), 26 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs index 6394189f0..ef4fb6a91 100644 --- a/crates/ide/src/completion/complete_attribute.rs +++ b/crates/ide/src/completion/complete_attribute.rs @@ -13,7 +13,7 @@ use crate::completion::{ }; pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - if ctx.mod_is_prev { + if ctx.mod_under_caret.is_some() { return None; } diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 5d41d0f69..f1795d2f7 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -5,15 +5,22 @@ use hir::{Module, ModuleSource}; use ide_db::RootDatabase; use rustc_hash::FxHashSet; -use super::{completion_context::CompletionContext, completion_item::Completions}; +use crate::{CompletionItem, CompletionItemKind}; + +use super::{ + completion_context::CompletionContext, completion_item::CompletionKind, + completion_item::Completions, +}; /// Complete mod declaration, i.e. `mod <|> ;` pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let _p = profile::span("completion::complete_mod"); + let mod_under_caret = match &ctx.mod_under_caret { + Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None, + Some(mod_under_caret) => mod_under_caret, + None => return None, + }; - if !ctx.mod_is_prev { - return None; - } + let _p = profile::span("completion::complete_mod"); let current_module = ctx.scope.module()?; @@ -36,7 +43,7 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op module_declaration_source_file.file_id.original_file(ctx.db) }); - let mod_declaration_candidates = source_root + source_root .iter() .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file) .filter(|submodule_candidate_file| { @@ -66,10 +73,16 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op _ => None, }) .filter(|name| !existing_mod_declarations.contains(name)) - .collect::>(); - dbg!(mod_declaration_candidates); - - // TODO kb actually add the results + .for_each(|submodule_name| { + let mut label = submodule_name; + if mod_under_caret.semicolon_token().is_none() { + label.push(';') + } + acc.add( + CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) + .kind(CompletionItemKind::Module), + ) + }); Some(()) } diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index 351461351..184488a73 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs @@ -13,7 +13,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon None => return, }; - if ctx.attribute_under_caret.is_some() || ctx.mod_is_prev { + if ctx.attribute_under_caret.is_some() || ctx.mod_under_caret.is_some() { return; } diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index 9f2dc16ab..f2189dfde 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs @@ -13,7 +13,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.record_lit_syntax.is_some() || ctx.record_pat_syntax.is_some() || ctx.attribute_under_caret.is_some() - || ctx.mod_is_prev + || ctx.mod_under_caret.is_some() { return; } diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index d289aac27..ea4830843 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -19,7 +19,7 @@ use crate::{ has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, - is_in_loop_body, is_match_arm, mod_is_prev, unsafe_is_prev, + is_in_loop_body, is_match_arm, unsafe_is_prev, }, CompletionConfig, }, @@ -77,7 +77,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_path_type: bool, pub(super) has_type_args: bool, pub(super) attribute_under_caret: Option, - pub(super) mod_is_prev: bool, + pub(super) mod_under_caret: Option, pub(super) unsafe_is_prev: bool, pub(super) if_is_prev: bool, pub(super) block_expr_parent: bool, @@ -153,7 +153,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, attribute_under_caret: None, - mod_is_prev: false, + mod_under_caret: None, unsafe_is_prev: false, in_loop_body: false, ref_pat_parent: false, @@ -241,7 +241,7 @@ impl<'a> CompletionContext<'a> { self.is_match_arm = is_match_arm(syntax_element.clone()); self.has_item_list_or_source_file_parent = has_item_list_or_source_file_parent(syntax_element.clone()); - self.mod_is_prev = mod_is_prev(syntax_element); + self.mod_under_caret = find_node_at_offset(&file_with_fake_ident, offset); } fn fill( diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index bc4ce4d6f..b17ddf133 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs @@ -116,15 +116,6 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { .is_some() } -// TODO kb generify? -pub(crate) fn mod_is_prev(element: SyntaxElement) -> bool { - element - .into_token() - .and_then(|it| previous_non_trivia_token(it)) - .filter(|it| it.kind() == MOD_KW) - .is_some() -} - #[test] fn test_if_is_prev() { check_pattern_is_applicable(r"if l<|>", if_is_prev); -- cgit v1.2.3 From dbf70cd015454cf125fda9b006251fa2782fbc7f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 01:45:05 +0300 Subject: Properly handle mod.rs imports --- crates/ide/src/completion/complete_mod.rs | 87 ++++++++++++++----------------- 1 file changed, 40 insertions(+), 47 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index f1795d2f7..b5f2d636c 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -51,26 +51,26 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op }) .filter_map(|submodule_file| { let submodule_path = source_root.path_for_file(&submodule_file)?; - if !is_special_rust_file_path(&submodule_path) - && submodule_path.parent()? == directory_to_look_for_submodules - { - submodule_path.file_name_and_extension() - } else { - None - } - }) - .filter_map(|submodule_file_name_and_extension| match submodule_file_name_and_extension { - (file_name, Some("rs")) => Some(file_name.to_owned()), - (subdirectory_name, None) => { - let mod_rs_path = - directory_to_look_for_submodules.join(subdirectory_name)?.join("mod.rs")?; - if source_root.file_for_path(&mod_rs_path).is_some() { - Some(subdirectory_name.to_owned()) - } else { - None + let directory_with_submodule = submodule_path.parent()?; + match submodule_path.file_name_and_extension()? { + ("lib", Some("rs")) | ("main", Some("rs")) => None, + ("mod", Some("rs")) => { + if directory_with_submodule.parent()? == directory_to_look_for_submodules { + match directory_with_submodule.file_name_and_extension()? { + (directory_name, None) => Some(directory_name.to_owned()), + _ => None, + } + } else { + None + } } + (file_name, Some("rs")) + if directory_with_submodule == directory_to_look_for_submodules => + { + Some(file_name.to_owned()) + } + _ => None, } - _ => None, }) .filter(|name| !existing_mod_declarations.contains(name)) .for_each(|submodule_name| { @@ -87,41 +87,34 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op Some(()) } -fn is_special_rust_file_path(path: &VfsPath) -> bool { - matches!( - path.file_name_and_extension(), - Some(("mod", Some("rs"))) | Some(("lib", Some("rs"))) | Some(("main", Some("rs"))) - ) -} - fn directory_to_look_for_submodules( module: Module, db: &RootDatabase, module_file_path: &VfsPath, ) -> Option { - let module_directory_path = module_file_path.parent()?; - let base_directory = if is_special_rust_file_path(module_file_path) { - Some(module_directory_path) - } else if let (regular_rust_file_name, Some("rs")) = - module_file_path.file_name_and_extension()? - { - if matches!( - ( - module_directory_path - .parent() - .as_ref() - .and_then(|path| path.file_name_and_extension()), - module_directory_path.file_name_and_extension(), - ), - (Some(("src", None)), Some(("bin", None))) - ) { - // files in /src/bin/ can import each other directly - Some(module_directory_path) - } else { - module_directory_path.join(regular_rust_file_name) + let directory_with_module_path = module_file_path.parent()?; + let base_directory = match module_file_path.file_name_and_extension()? { + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { + Some(directory_with_module_path) + } + (regular_rust_file_name, Some("rs")) => { + if matches!( + ( + directory_with_module_path + .parent() + .as_ref() + .and_then(|path| path.file_name_and_extension()), + directory_with_module_path.file_name_and_extension(), + ), + (Some(("src", None)), Some(("bin", None))) + ) { + // files in /src/bin/ can import each other directly + Some(directory_with_module_path) + } else { + directory_with_module_path.join(regular_rust_file_name) + } } - } else { - None + _ => None, }?; let mut resulting_path = base_directory; -- cgit v1.2.3 From 5aebf54fd4d075389ce2c74a460ea0c48ccdbad2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 02:26:55 +0300 Subject: Add tests --- crates/ide/src/completion/complete_mod.rs | 153 ++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index b5f2d636c..049e99674 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -147,3 +147,156 @@ fn module_chain_to_containing_module_file( path } + +#[cfg(test)] +mod tests { + use crate::completion::{test_utils::completion_list, CompletionKind}; + use expect_test::{expect, Expect}; + + fn check(ra_fixture: &str, expect: Expect) { + let actual = completion_list(ra_fixture, CompletionKind::Magic); + expect.assert_eq(&actual); + } + + #[test] + fn lib_module_completion() { + check( + r#" + //- /lib.rs + mod <|> + //- /foo.rs + fn foo() {} + //- /foo/ignored_foo.rs + fn ignored_foo() {} + //- /bar/mod.rs + fn bar() {} + //- /bar/ignored_bar.rs + fn ignored_bar() {} + "#, + expect![[r#" + md bar; + md foo; + "#]], + ); + } + + #[test] + fn main_module_completion() { + check( + r#" + //- /main.rs + mod <|> + //- /foo.rs + fn foo() {} + //- /foo/ignored_foo.rs + fn ignored_foo() {} + //- /bar/mod.rs + fn bar() {} + //- /bar/ignored_bar.rs + fn ignored_bar() {} + "#, + expect![[r#" + md bar; + md foo; + "#]], + ); + } + + #[test] + fn main_test_module_completion() { + check( + r#" + //- /main.rs + mod tests { + mod <|>; + } + //- /tests/foo.rs + fn foo() {} + "#, + expect![[r#" + md foo + "#]], + ); + } + + #[test] + fn directly_nested_module_completion() { + check( + r#" + //- /lib.rs + mod foo; + //- /foo.rs + mod <|>; + //- /foo/bar.rs + fn bar() {} + //- /foo/bar/ignored_bar.rs + fn ignored_bar() {} + //- /foo/baz/mod.rs + fn baz() {} + //- /foo/moar/ignored_moar.rs + fn ignored_moar() {} + "#, + expect![[r#" + md bar + md baz + "#]], + ); + } + + #[test] + fn nested_in_source_module_completion() { + check( + r#" + //- /lib.rs + mod foo; + //- /foo.rs + mod bar { + mod <|> + } + //- /foo/bar/baz.rs + fn baz() {} + "#, + expect![[r#" + md baz; + "#]], + ); + } + + // FIXME binart modules are not picked up in tests + // #[test] + // fn regular_bin_module_completion() { + // check( + // r#" + // //- /src/main.rs + // fn main() {} + // //- /src/main/foo.rs + // mod <|> + // //- /src/main/bar.rs + // fn bar() {} + // //- /src/main/bar/bar_ignored.rs + // fn bar_ignored() {} + // "#, + // expect![[r#" + // md bar; + // "#]], + // ); + // } + + #[test] + fn already_declared_bin_module_completion_omitted() { + check( + r#" + //- /src/main.rs + fn main() {} + //- /src/main/foo.rs + mod <|> + //- /src/main/bar.rs + mod foo; + fn bar() {} + //- /src/main/bar/bar_ignored.rs + fn bar_ignored() {} + "#, + expect![[r#""#]], + ); + } +} -- cgit v1.2.3 From a7d75463c7d1473e4276601688baa22c10eec255 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Sep 2020 02:34:11 +0300 Subject: Fix the tests --- crates/ide/src/completion/complete_attribute.rs | 2 +- crates/ide/src/completion/complete_mod.rs | 2 +- crates/ide/src/completion/complete_qualified_path.rs | 2 +- crates/ide/src/completion/complete_unqualified_path.rs | 2 +- crates/ide/src/completion/completion_context.rs | 8 +++++--- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs index ef4fb6a91..f4a9864d1 100644 --- a/crates/ide/src/completion/complete_attribute.rs +++ b/crates/ide/src/completion/complete_attribute.rs @@ -13,7 +13,7 @@ use crate::completion::{ }; pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - if ctx.mod_under_caret.is_some() { + if ctx.mod_declaration_under_caret.is_some() { return None; } diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 049e99674..d457ff6bf 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -14,7 +14,7 @@ use super::{ /// Complete mod declaration, i.e. `mod <|> ;` pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let mod_under_caret = match &ctx.mod_under_caret { + let mod_under_caret = match &ctx.mod_declaration_under_caret { Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None, Some(mod_under_caret) => mod_under_caret, None => return None, diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index 184488a73..79de50792 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs @@ -13,7 +13,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon None => return, }; - if ctx.attribute_under_caret.is_some() || ctx.mod_under_caret.is_some() { + if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { return; } diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index f2189dfde..8eda4b64d 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs @@ -13,7 +13,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.record_lit_syntax.is_some() || ctx.record_pat_syntax.is_some() || ctx.attribute_under_caret.is_some() - || ctx.mod_under_caret.is_some() + || ctx.mod_declaration_under_caret.is_some() { return; } diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index ea4830843..161f59c1e 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs @@ -77,7 +77,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_path_type: bool, pub(super) has_type_args: bool, pub(super) attribute_under_caret: Option, - pub(super) mod_under_caret: Option, + pub(super) mod_declaration_under_caret: Option, pub(super) unsafe_is_prev: bool, pub(super) if_is_prev: bool, pub(super) block_expr_parent: bool, @@ -153,7 +153,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, attribute_under_caret: None, - mod_under_caret: None, + mod_declaration_under_caret: None, unsafe_is_prev: false, in_loop_body: false, ref_pat_parent: false, @@ -241,7 +241,9 @@ impl<'a> CompletionContext<'a> { self.is_match_arm = is_match_arm(syntax_element.clone()); self.has_item_list_or_source_file_parent = has_item_list_or_source_file_parent(syntax_element.clone()); - self.mod_under_caret = find_node_at_offset(&file_with_fake_ident, offset); + self.mod_declaration_under_caret = + find_node_at_offset::(&file_with_fake_ident, offset) + .filter(|module| module.item_list().is_none()); } fn fill( -- cgit v1.2.3 From 9863798480aa9642e31bfd41ee899d2e7329b5e5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 10 Sep 2020 01:45:49 +0300 Subject: Rename the method to avoid false promises --- crates/ide/src/completion/complete_mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index d457ff6bf..2ea0d5034 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -52,11 +52,11 @@ pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op .filter_map(|submodule_file| { let submodule_path = source_root.path_for_file(&submodule_file)?; let directory_with_submodule = submodule_path.parent()?; - match submodule_path.file_name_and_extension()? { + match submodule_path.name_and_extension()? { ("lib", Some("rs")) | ("main", Some("rs")) => None, ("mod", Some("rs")) => { if directory_with_submodule.parent()? == directory_to_look_for_submodules { - match directory_with_submodule.file_name_and_extension()? { + match directory_with_submodule.name_and_extension()? { (directory_name, None) => Some(directory_name.to_owned()), _ => None, } @@ -93,7 +93,7 @@ fn directory_to_look_for_submodules( module_file_path: &VfsPath, ) -> Option { let directory_with_module_path = module_file_path.parent()?; - let base_directory = match module_file_path.file_name_and_extension()? { + let base_directory = match module_file_path.name_and_extension()? { ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { Some(directory_with_module_path) } @@ -103,8 +103,8 @@ fn directory_to_look_for_submodules( directory_with_module_path .parent() .as_ref() - .and_then(|path| path.file_name_and_extension()), - directory_with_module_path.file_name_and_extension(), + .and_then(|path| path.name_and_extension()), + directory_with_module_path.name_and_extension(), ), (Some(("src", None)), Some(("bin", None))) ) { -- cgit v1.2.3 From 492e3c40f666f53d9e6b8329cadc57ff36e38ba9 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 10 Sep 2020 01:58:29 +0300 Subject: One more test --- crates/ide/src/completion/complete_mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 2ea0d5034..7dbf4aee4 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -180,6 +180,21 @@ mod tests { ); } + #[test] + fn no_module_completion_with_module_body() { + check( + r#" + //- /lib.rs + mod <|> { + + } + //- /foo.rs + fn foo() {} + "#, + expect![[r#""#]], + ); + } + #[test] fn main_module_completion() { check( -- cgit v1.2.3 From ca698a0b8c78e5ba7738fc0f0f6f77718e70a83e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 11 Sep 2020 14:16:15 +0300 Subject: Adjust the test comment --- crates/ide/src/completion/complete_mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'crates/ide/src') diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 7dbf4aee4..3cfc2e131 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs @@ -277,18 +277,25 @@ mod tests { ); } - // FIXME binart modules are not picked up in tests + // FIXME binary modules are not supported in tests properly + // Binary modules are a bit special, they allow importing the modules from `/src/bin` + // and that's why are good to test two things: + // * no cycles are allowed in mod declarations + // * no modules from the parent directory are proposed + // Unfortunately, binary modules support is in cargo not rustc, + // hence the test does not work now + // // #[test] // fn regular_bin_module_completion() { // check( // r#" - // //- /src/main.rs + // //- /src/bin.rs // fn main() {} - // //- /src/main/foo.rs + // //- /src/bin/foo.rs // mod <|> - // //- /src/main/bar.rs + // //- /src/bin/bar.rs // fn bar() {} - // //- /src/main/bar/bar_ignored.rs + // //- /src/bin/bar/bar_ignored.rs // fn bar_ignored() {} // "#, // expect![[r#" @@ -301,14 +308,14 @@ mod tests { fn already_declared_bin_module_completion_omitted() { check( r#" - //- /src/main.rs + //- /src/bin.rs fn main() {} - //- /src/main/foo.rs + //- /src/bin/foo.rs mod <|> - //- /src/main/bar.rs + //- /src/bin/bar.rs mod foo; fn bar() {} - //- /src/main/bar/bar_ignored.rs + //- /src/bin/bar/bar_ignored.rs fn bar_ignored() {} "#, expect![[r#""#]], -- cgit v1.2.3