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') 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