aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis/src')
-rw-r--r--crates/libanalysis/src/lib.rs83
-rw-r--r--crates/libanalysis/src/module_map.rs80
2 files changed, 126 insertions, 37 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index fe2c3c2e6..96d10a087 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -8,13 +8,13 @@ extern crate libsyntax2;
8extern crate libeditor; 8extern crate libeditor;
9extern crate fst; 9extern crate fst;
10extern crate rayon; 10extern crate rayon;
11extern crate relative_path;
11 12
12mod symbol_index; 13mod symbol_index;
13mod module_map; 14mod module_map;
14 15
15use std::{ 16use std::{
16 fmt, 17 fmt,
17 path::{Path, PathBuf},
18 panic, 18 panic,
19 sync::{ 19 sync::{
20 Arc, 20 Arc,
@@ -24,6 +24,7 @@ use std::{
24 time::Instant, 24 time::Instant,
25}; 25};
26 26
27use relative_path::{RelativePath,RelativePathBuf};
27use once_cell::sync::OnceCell; 28use once_cell::sync::OnceCell;
28use rayon::prelude::*; 29use rayon::prelude::*;
29 30
@@ -37,13 +38,16 @@ use libeditor::{Diagnostic, LineIndex, FileSymbol, find_node_at_offset};
37 38
38use self::{ 39use self::{
39 symbol_index::FileSymbols, 40 symbol_index::FileSymbols,
40 module_map::{ModuleMap, ChangeKind}, 41 module_map::{ModuleMap, ChangeKind, Problem},
41}; 42};
42pub use self::symbol_index::Query; 43pub use self::symbol_index::Query;
43 44
44pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 45pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
45 46
46pub type FileResolver = dyn Fn(FileId, &Path) -> Option<FileId> + Send + Sync; 47pub trait FileResolver: Send + Sync + 'static {
48 fn file_stem(&self, id: FileId) -> String;
49 fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
50}
47 51
48#[derive(Debug)] 52#[derive(Debug)]
49pub struct WorldState { 53pub struct WorldState {
@@ -84,7 +88,7 @@ impl WorldState {
84 88
85 pub fn snapshot( 89 pub fn snapshot(
86 &self, 90 &self,
87 file_resolver: impl Fn(FileId, &Path) -> Option<FileId> + 'static + Send + Sync, 91 file_resolver: impl FileResolver,
88 ) -> World { 92 ) -> World {
89 World { 93 World {
90 needs_reindex: AtomicBool::new(false), 94 needs_reindex: AtomicBool::new(false),
@@ -132,8 +136,20 @@ impl WorldState {
132} 136}
133 137
134#[derive(Debug)] 138#[derive(Debug)]
135pub enum QuickFix { 139pub struct QuickFix {
136 CreateFile(PathBuf), 140 pub fs_ops: Vec<FsOp>,
141}
142
143#[derive(Debug)]
144pub enum FsOp {
145 CreateFile {
146 anchor: FileId,
147 path: RelativePathBuf,
148 },
149 MoveFile {
150 file: FileId,
151 path: RelativePathBuf,
152 }
137} 153}
138 154
139impl World { 155impl World {
@@ -221,20 +237,49 @@ impl World {
221 .into_iter() 237 .into_iter()
222 .map(|d| (d, None)) 238 .map(|d| (d, None))
223 .collect::<Vec<_>>(); 239 .collect::<Vec<_>>();
224 for module in syntax.ast().modules() { 240
225 if module.has_semi() && self.resolve_module(file_id, module).is_empty() { 241 self.data.module_map.problems(
226 if let Some(name) = module.name() { 242 file_id,
227 let d = Diagnostic { 243 &*self.file_resolver,
228 range: name.syntax().range(), 244 &|file_id| self.file_syntax(file_id).unwrap(),
229 msg: "unresolved module".to_string(), 245 |name_node, problem| {
230 }; 246 let (diag, fix) = match problem {
231 let quick_fix = self.data.module_map.suggested_child_mod_path(module) 247 Problem::UnresolvedModule { candidate } => {
232 .map(QuickFix::CreateFile); 248 let diag = Diagnostic {
233 249 range: name_node.syntax().range(),
234 res.push((d, quick_fix)) 250 msg: "unresolved module".to_string(),
235 } 251 };
252 let fix = QuickFix {
253 fs_ops: vec![FsOp::CreateFile {
254 anchor: file_id,
255 path: candidate.clone(),
256 }]
257 };
258 (diag, fix)
259 }
260 Problem::NotDirOwner { move_to, candidate } => {
261 let diag = Diagnostic {
262 range: name_node.syntax().range(),
263 msg: "can't declare module at this location".to_string(),
264 };
265 let fix = QuickFix {
266 fs_ops: vec![
267 FsOp::MoveFile {
268 file: file_id,
269 path: move_to.clone(),
270 },
271 FsOp::CreateFile {
272 anchor: file_id,
273 path: move_to.join(candidate),
274 }
275 ],
276 };
277 (diag, fix)
278 }
279 };
280 res.push((diag, Some(fix)))
236 } 281 }
237 } 282 );
238 Ok(res) 283 Ok(res)
239 } 284 }
240 285
diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs
index 4f480591e..b65569c46 100644
--- a/crates/libanalysis/src/module_map.rs
+++ b/crates/libanalysis/src/module_map.rs
@@ -1,6 +1,4 @@
1use std::{ 1use relative_path::RelativePathBuf;
2 path::{PathBuf},
3};
4 2
5use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; 3use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
6use libsyntax2::{ 4use libsyntax2::{
@@ -43,6 +41,18 @@ struct Link {
43 owner: ModuleId, 41 owner: ModuleId,
44 syntax: SyntaxNode, 42 syntax: SyntaxNode,
45 points_to: Vec<ModuleId>, 43 points_to: Vec<ModuleId>,
44 problem: Option<Problem>,
45}
46
47#[derive(Clone, Debug)]
48pub enum Problem {
49 UnresolvedModule {
50 candidate: RelativePathBuf,
51 },
52 NotDirOwner {
53 move_to: RelativePathBuf,
54 candidate: RelativePathBuf,
55 }
46} 56}
47 57
48impl ModuleMap { 58impl ModuleMap {
@@ -93,9 +103,24 @@ impl ModuleMap {
93 res 103 res
94 } 104 }
95 105
96 pub fn suggested_child_mod_path(&self, m: ast::Module) -> Option<PathBuf> { 106 pub fn problems(
97 let name = m.name()?; 107 &self,
98 Some(PathBuf::from(format!("../{}.rs", name.text()))) 108 file: FileId,
109 file_resolver: &FileResolver,
110 syntax_provider: &SyntaxProvider,
111 mut cb: impl FnMut(ast::Name, &Problem),
112 ) {
113 let module = self.file2module(file);
114 let links = self.links(file_resolver, syntax_provider);
115 links
116 .links
117 .iter()
118 .filter(|link| link.owner == module)
119 .filter_map(|link| {
120 let problem = link.problem.as_ref()?;
121 Some((link, problem))
122 })
123 .for_each(|(link, problem)| cb(link.name_node(), problem))
99 } 124 }
100 125
101 fn links( 126 fn links(
@@ -176,14 +201,17 @@ impl Link {
176 owner, 201 owner,
177 syntax: module.syntax().owned(), 202 syntax: module.syntax().owned(),
178 points_to: Vec::new(), 203 points_to: Vec::new(),
204 problem: None,
179 }; 205 };
180 Some(link) 206 Some(link)
181 } 207 }
182 208
183 fn name(&self) -> SmolStr { 209 fn name(&self) -> SmolStr {
184 self.ast().name() 210 self.name_node().text()
185 .unwrap() 211 }
186 .text() 212
213 fn name_node(&self) -> ast::Name {
214 self.ast().name().unwrap()
187 } 215 }
188 216
189 fn ast(&self) -> ast::Module { 217 fn ast(&self) -> ast::Module {
@@ -192,14 +220,30 @@ impl Link {
192 } 220 }
193 221
194 fn resolve(&mut self, file_resolver: &FileResolver) { 222 fn resolve(&mut self, file_resolver: &FileResolver) {
195 let name = self.name(); 223 let mod_name = file_resolver.file_stem(self.owner.0);
196 let paths = &[ 224 let is_dir_owner =
197 PathBuf::from(format!("../{}.rs", name)), 225 mod_name == "mod" || mod_name == "lib" || mod_name == "main";
198 PathBuf::from(format!("../{}/mod.rs", name)), 226
199 ]; 227 let file_mod = RelativePathBuf::from(format!("../{}.rs", self.name()));
200 self.points_to = paths.iter() 228 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", self.name()));
201 .filter_map(|path| file_resolver(self.owner.0, path)) 229 if is_dir_owner {
202 .map(ModuleId) 230 self.points_to = [&file_mod, &dir_mod].iter()
203 .collect(); 231 .filter_map(|path| file_resolver.resolve(self.owner.0, path))
232 .map(ModuleId)
233 .collect();
234 self.problem = if self.points_to.is_empty() {
235 Some(Problem::UnresolvedModule {
236 candidate: file_mod,
237 })
238 } else {
239 None
240 }
241 } else {
242 self.points_to = Vec::new();
243 self.problem = Some(Problem::NotDirOwner {
244 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
245 candidate: file_mod,
246 });
247 }
204 } 248 }
205} 249}