diff options
Diffstat (limited to 'crates/ra_hir/src/nameres/mod_resolution.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/mod_resolution.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/mod_resolution.rs b/crates/ra_hir/src/nameres/mod_resolution.rs new file mode 100644 index 000000000..94c9946ff --- /dev/null +++ b/crates/ra_hir/src/nameres/mod_resolution.rs | |||
@@ -0,0 +1,182 @@ | |||
1 | use std::{borrow::Cow, sync::Arc}; | ||
2 | |||
3 | use ra_db::{FileId, SourceRoot}; | ||
4 | use ra_syntax::SmolStr; | ||
5 | use relative_path::RelativePathBuf; | ||
6 | |||
7 | use crate::{DefDatabase, HirFileId, Name}; | ||
8 | |||
9 | #[derive(Clone, Copy)] | ||
10 | pub(super) struct ParentModule<'a> { | ||
11 | pub(super) name: &'a Name, | ||
12 | pub(super) attr_path: Option<&'a SmolStr>, | ||
13 | } | ||
14 | |||
15 | impl<'a> ParentModule<'a> { | ||
16 | fn attribute_path(&self) -> Option<&SmolStr> { | ||
17 | self.attr_path.filter(|p| !p.is_empty()) | ||
18 | } | ||
19 | } | ||
20 | |||
21 | pub(super) fn resolve_submodule( | ||
22 | db: &impl DefDatabase, | ||
23 | file_id: HirFileId, | ||
24 | name: &Name, | ||
25 | is_root: bool, | ||
26 | attr_path: Option<&SmolStr>, | ||
27 | parent_module: Option<ParentModule<'_>>, | ||
28 | ) -> Result<FileId, RelativePathBuf> { | ||
29 | let file_id = file_id.original_file(db); | ||
30 | let source_root_id = db.file_source_root(file_id); | ||
31 | let path = db.file_relative_path(file_id); | ||
32 | let root = RelativePathBuf::default(); | ||
33 | let dir_path = path.parent().unwrap_or(&root); | ||
34 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
35 | |||
36 | let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { | ||
37 | (Some(file_path), Some(parent_module)) => { | ||
38 | let file_path = normalize_attribute_path(file_path); | ||
39 | match parent_module.attribute_path() { | ||
40 | Some(parent_module_attr_path) => { | ||
41 | let path = dir_path | ||
42 | .join(format!( | ||
43 | "{}/{}", | ||
44 | normalize_attribute_path(parent_module_attr_path), | ||
45 | file_path | ||
46 | )) | ||
47 | .normalize(); | ||
48 | ResolutionMode::InlineModuleWithAttributePath( | ||
49 | InsideInlineModuleMode::WithAttributePath(path), | ||
50 | ) | ||
51 | } | ||
52 | None => { | ||
53 | let path = | ||
54 | dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize(); | ||
55 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath( | ||
56 | path, | ||
57 | )) | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | (None, Some(parent_module)) => match parent_module.attribute_path() { | ||
62 | Some(parent_module_attr_path) => { | ||
63 | let path = dir_path.join(format!( | ||
64 | "{}/{}.rs", | ||
65 | normalize_attribute_path(parent_module_attr_path), | ||
66 | name | ||
67 | )); | ||
68 | ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path)) | ||
69 | } | ||
70 | None => { | ||
71 | let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name)); | ||
72 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path)) | ||
73 | } | ||
74 | }, | ||
75 | (Some(file_path), None) => { | ||
76 | let file_path = normalize_attribute_path(file_path); | ||
77 | let path = dir_path.join(file_path.as_ref()).normalize(); | ||
78 | ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) | ||
79 | } | ||
80 | _ => { | ||
81 | let is_dir_owner = is_root || mod_name == "mod"; | ||
82 | if is_dir_owner { | ||
83 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
84 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
85 | ResolutionMode::OutOfLine(OutOfLineMode::RootOrModRs { | ||
86 | file: file_mod, | ||
87 | directory: dir_mod, | ||
88 | }) | ||
89 | } else { | ||
90 | let path = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
91 | ResolutionMode::OutOfLine(OutOfLineMode::FileInDirectory(path)) | ||
92 | } | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | resolve_mode.resolve(db.source_root(source_root_id)) | ||
97 | } | ||
98 | |||
99 | fn normalize_attribute_path(file_path: &SmolStr) -> Cow<str> { | ||
100 | let current_dir = "./"; | ||
101 | let windows_path_separator = r#"\"#; | ||
102 | let current_dir_normalize = if file_path.starts_with(current_dir) { | ||
103 | &file_path[current_dir.len()..] | ||
104 | } else { | ||
105 | file_path.as_str() | ||
106 | }; | ||
107 | if current_dir_normalize.contains(windows_path_separator) { | ||
108 | Cow::Owned(current_dir_normalize.replace(windows_path_separator, "/")) | ||
109 | } else { | ||
110 | Cow::Borrowed(current_dir_normalize) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | enum OutOfLineMode { | ||
115 | RootOrModRs { file: RelativePathBuf, directory: RelativePathBuf }, | ||
116 | FileInDirectory(RelativePathBuf), | ||
117 | WithAttributePath(RelativePathBuf), | ||
118 | } | ||
119 | |||
120 | impl OutOfLineMode { | ||
121 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
122 | match self { | ||
123 | OutOfLineMode::RootOrModRs { file, directory } => match source_root.files.get(file) { | ||
124 | None => resolve_simple_path(source_root, directory).map_err(|_| file.clone()), | ||
125 | file_id => resolve_find_result(file_id, file), | ||
126 | }, | ||
127 | OutOfLineMode::FileInDirectory(path) => resolve_simple_path(source_root, path), | ||
128 | OutOfLineMode::WithAttributePath(path) => resolve_simple_path(source_root, path), | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | enum InsideInlineModuleMode { | ||
134 | File(RelativePathBuf), | ||
135 | WithAttributePath(RelativePathBuf), | ||
136 | } | ||
137 | |||
138 | impl InsideInlineModuleMode { | ||
139 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
140 | match self { | ||
141 | InsideInlineModuleMode::File(path) => resolve_simple_path(source_root, path), | ||
142 | InsideInlineModuleMode::WithAttributePath(path) => { | ||
143 | resolve_simple_path(source_root, path) | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | enum ResolutionMode { | ||
150 | OutOfLine(OutOfLineMode), | ||
151 | InsideInlineModule(InsideInlineModuleMode), | ||
152 | InlineModuleWithAttributePath(InsideInlineModuleMode), | ||
153 | } | ||
154 | |||
155 | impl ResolutionMode { | ||
156 | pub fn resolve(&self, source_root: Arc<SourceRoot>) -> Result<FileId, RelativePathBuf> { | ||
157 | use self::ResolutionMode::*; | ||
158 | |||
159 | match self { | ||
160 | OutOfLine(mode) => mode.resolve(source_root), | ||
161 | InsideInlineModule(mode) => mode.resolve(source_root), | ||
162 | InlineModuleWithAttributePath(mode) => mode.resolve(source_root), | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | fn resolve_simple_path( | ||
168 | source_root: Arc<SourceRoot>, | ||
169 | path: &RelativePathBuf, | ||
170 | ) -> Result<FileId, RelativePathBuf> { | ||
171 | resolve_find_result(source_root.files.get(path), path) | ||
172 | } | ||
173 | |||
174 | fn resolve_find_result( | ||
175 | file_id: Option<&FileId>, | ||
176 | path: &RelativePathBuf, | ||
177 | ) -> Result<FileId, RelativePathBuf> { | ||
178 | match file_id { | ||
179 | Some(file_id) => Ok(file_id.clone()), | ||
180 | None => Err(path.clone()), | ||
181 | } | ||
182 | } | ||