|
1 | 1 | //! Completes mod declarations. |
2 | 2 |
|
3 | | -use base_db::FileLoader; |
4 | | -use hir::ModuleSource; |
| 3 | +use base_db::{SourceDatabaseExt, VfsPath}; |
| 4 | +use hir::{Module, ModuleSource}; |
| 5 | +use ide_db::RootDatabase; |
5 | 6 |
|
6 | 7 | use super::{completion_context::CompletionContext, completion_item::Completions}; |
7 | 8 |
|
8 | 9 | /// Complete mod declaration, i.e. `mod <|> ;` |
9 | 10 | pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) { |
10 | 11 | let module_names_for_import = ctx |
11 | | - .sema |
12 | | - // TODO kb this is wrong, since we need not the file module |
13 | | - .to_module_def(ctx.position.file_id) |
| 12 | + .scope |
| 13 | + .module() |
14 | 14 | .and_then(|current_module| { |
15 | | - dbg!(current_module.name(ctx.db)); |
16 | | - dbg!(current_module.definition_source(ctx.db)); |
17 | | - dbg!(current_module.declaration_source(ctx.db)); |
18 | | - let mut zz = Vec::new(); |
19 | | - let mut vv = Some(current_module); |
20 | | - while let Some(ModuleSource::Module(_)) = |
21 | | - vv.map(|vv| vv.definition_source(ctx.db).value) |
22 | | - { |
23 | | - zz.push(current_module.name(ctx.db)); |
24 | | - vv = current_module.parent(ctx.db); |
25 | | - } |
26 | | - dbg!(zz); |
27 | | - let definition_source = current_module.definition_source(ctx.db); |
| 15 | + let module_path = path_to_closest_containing_module_file(current_module, ctx.db); |
28 | 16 | // TODO kb filter out declarations in possible_sudmobule_names |
29 | 17 | // let declaration_source = current_module.declaration_source(ctx.db); |
30 | | - let module_definition_source_file = definition_source.file_id.original_file(ctx.db); |
31 | | - let mod_declaration_candidates = |
32 | | - ctx.db.possible_sudmobule_names(module_definition_source_file); |
| 18 | + let module_definition_source_file = |
| 19 | + current_module.definition_source(ctx.db).file_id.original_file(ctx.db); |
| 20 | + |
| 21 | + let source_root_id = ctx.db.file_source_root(module_definition_source_file); |
| 22 | + let source_root = ctx.db.source_root(source_root_id); |
| 23 | + let directory_to_look_for_submodules = source_root |
| 24 | + .path_for_file(&module_definition_source_file) |
| 25 | + .and_then(|module_file_path| get_directory_with_submodules(module_file_path))?; |
| 26 | + |
| 27 | + let mod_declaration_candidates = source_root |
| 28 | + .iter() |
| 29 | + .filter(|submodule_file| submodule_file != &module_definition_source_file) |
| 30 | + .filter_map(|submodule_file| { |
| 31 | + let submodule_path = source_root.path_for_file(&submodule_file)?; |
| 32 | + if submodule_path.parent()? == directory_to_look_for_submodules { |
| 33 | + submodule_path.file_name_and_extension() |
| 34 | + } else { |
| 35 | + None |
| 36 | + } |
| 37 | + }) |
| 38 | + .filter_map(|file_name_and_extension| { |
| 39 | + match file_name_and_extension { |
| 40 | + // TODO kb wrong resolution for nested non-file modules (mod tests { mod <|> }) |
| 41 | + // TODO kb in src/bin when a module is included into another, |
| 42 | + // the included file gets "moved" into a directory below and now cannot add any other modules |
| 43 | + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => None, |
| 44 | + (file_name, Some("rs")) => Some(file_name.to_owned()), |
| 45 | + (subdirectory_name, None) => { |
| 46 | + let mod_rs_path = directory_to_look_for_submodules |
| 47 | + .join(subdirectory_name)? |
| 48 | + .join("mod.rs")?; |
| 49 | + if source_root.file_for_path(&mod_rs_path).is_some() { |
| 50 | + Some(subdirectory_name.to_owned()) |
| 51 | + } else { |
| 52 | + None |
| 53 | + } |
| 54 | + } |
| 55 | + _ => None, |
| 56 | + } |
| 57 | + }) |
| 58 | + .collect::<Vec<_>>(); |
33 | 59 | dbg!(mod_declaration_candidates); |
34 | 60 | // TODO kb exlude existing children from the candidates |
35 | 61 | let existing_children = current_module.children(ctx.db).collect::<Vec<_>>(); |
36 | 62 | None::<Vec<String>> |
37 | 63 | }) |
38 | 64 | .unwrap_or_default(); |
39 | 65 | } |
| 66 | + |
| 67 | +fn path_to_closest_containing_module_file( |
| 68 | + current_module: Module, |
| 69 | + db: &RootDatabase, |
| 70 | +) -> Vec<Module> { |
| 71 | + let mut path = Vec::new(); |
| 72 | + |
| 73 | + let mut current_module = Some(current_module); |
| 74 | + while let Some(ModuleSource::Module(_)) = |
| 75 | + current_module.map(|module| module.definition_source(db).value) |
| 76 | + { |
| 77 | + if let Some(module) = current_module { |
| 78 | + path.insert(0, module); |
| 79 | + current_module = module.parent(db); |
| 80 | + } else { |
| 81 | + current_module = None; |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + path |
| 86 | +} |
| 87 | + |
| 88 | +fn get_directory_with_submodules(module_file_path: &VfsPath) -> Option<VfsPath> { |
| 89 | + let module_directory_path = module_file_path.parent()?; |
| 90 | + match module_file_path.file_name_and_extension()? { |
| 91 | + ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { |
| 92 | + Some(module_directory_path) |
| 93 | + } |
| 94 | + (regular_rust_file_name, Some("rs")) => { |
| 95 | + if matches!( |
| 96 | + ( |
| 97 | + module_directory_path |
| 98 | + .parent() |
| 99 | + .as_ref() |
| 100 | + .and_then(|path| path.file_name_and_extension()), |
| 101 | + module_directory_path.file_name_and_extension(), |
| 102 | + ), |
| 103 | + (Some(("src", None)), Some(("bin", None))) |
| 104 | + ) { |
| 105 | + // files in /src/bin/ can import each other directly |
| 106 | + Some(module_directory_path) |
| 107 | + } else { |
| 108 | + module_directory_path.join(regular_rust_file_name) |
| 109 | + } |
| 110 | + } |
| 111 | + _ => None, |
| 112 | + } |
| 113 | +} |
0 commit comments