2018-08-28 18:22:52 +03:00
|
|
|
use relative_path::RelativePathBuf;
|
2018-08-21 18:30:10 +03:00
|
|
|
|
2018-08-21 22:24:59 +03:00
|
|
|
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
2018-08-21 18:30:10 +03:00
|
|
|
use libsyntax2::{
|
2018-08-25 11:44:58 +03:00
|
|
|
File,
|
2018-08-25 11:40:17 +03:00
|
|
|
ast::{self, AstNode, NameOwner},
|
2018-08-21 22:24:59 +03:00
|
|
|
SyntaxNode, SmolStr,
|
2018-08-21 18:30:10 +03:00
|
|
|
};
|
|
|
|
|
use {FileId, FileResolver};
|
|
|
|
|
|
2018-08-25 11:44:58 +03:00
|
|
|
type SyntaxProvider<'a> = dyn Fn(FileId) -> File + 'a;
|
2018-08-21 22:24:59 +03:00
|
|
|
|
2018-08-21 18:30:10 +03:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
|
|
|
pub struct ModuleId(FileId);
|
|
|
|
|
|
2018-08-21 22:24:59 +03:00
|
|
|
#[derive(Debug, Default)]
|
2018-08-21 18:30:10 +03:00
|
|
|
pub struct ModuleMap {
|
2018-08-21 22:24:59 +03:00
|
|
|
state: RwLock<State>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum ChangeKind {
|
|
|
|
|
Delete, Insert, Update
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Clone for ModuleMap {
|
|
|
|
|
fn clone(&self) -> ModuleMap {
|
|
|
|
|
let state = self.state.read().clone();
|
|
|
|
|
ModuleMap { state: RwLock::new(state) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
|
|
|
struct State {
|
|
|
|
|
changes: Vec<(FileId, ChangeKind)>,
|
2018-08-21 18:30:10 +03:00
|
|
|
links: Vec<Link>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
struct Link {
|
|
|
|
|
owner: ModuleId,
|
|
|
|
|
syntax: SyntaxNode,
|
|
|
|
|
points_to: Vec<ModuleId>,
|
2018-08-28 18:22:52 +03:00
|
|
|
problem: Option<Problem>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub enum Problem {
|
|
|
|
|
UnresolvedModule {
|
|
|
|
|
candidate: RelativePathBuf,
|
|
|
|
|
},
|
|
|
|
|
NotDirOwner {
|
|
|
|
|
move_to: RelativePathBuf,
|
|
|
|
|
candidate: RelativePathBuf,
|
|
|
|
|
}
|
2018-08-21 18:30:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ModuleMap {
|
2018-08-21 22:24:59 +03:00
|
|
|
pub fn update_file(&mut self, file: FileId, change_kind: ChangeKind) {
|
|
|
|
|
self.state.get_mut().changes.push((file, change_kind));
|
2018-08-21 18:30:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn module2file(&self, m: ModuleId) -> FileId {
|
|
|
|
|
m.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn file2module(&self, file_id: FileId) -> ModuleId {
|
|
|
|
|
ModuleId(file_id)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 22:24:59 +03:00
|
|
|
pub fn child_module_by_name<'a>(
|
|
|
|
|
&self,
|
|
|
|
|
parent_mod: ModuleId,
|
|
|
|
|
child_mod: &str,
|
|
|
|
|
file_resolver: &FileResolver,
|
|
|
|
|
syntax_provider: &SyntaxProvider,
|
|
|
|
|
) -> Vec<ModuleId> {
|
|
|
|
|
self.links(file_resolver, syntax_provider)
|
|
|
|
|
.links
|
2018-08-21 18:30:10 +03:00
|
|
|
.iter()
|
|
|
|
|
.filter(|link| link.owner == parent_mod)
|
|
|
|
|
.filter(|link| link.name() == child_mod)
|
|
|
|
|
.filter_map(|it| it.points_to.first())
|
|
|
|
|
.map(|&it| it)
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 22:24:59 +03:00
|
|
|
pub fn parent_modules(
|
|
|
|
|
&self,
|
|
|
|
|
m: ModuleId,
|
|
|
|
|
file_resolver: &FileResolver,
|
|
|
|
|
syntax_provider: &SyntaxProvider,
|
|
|
|
|
) -> Vec<(ModuleId, SmolStr, SyntaxNode)> {
|
|
|
|
|
let links = self.links(file_resolver, syntax_provider);
|
|
|
|
|
let res = links
|
|
|
|
|
.links
|
2018-08-21 18:30:10 +03:00
|
|
|
.iter()
|
|
|
|
|
.filter(move |link| link.points_to.iter().any(|&it| it == m))
|
|
|
|
|
.map(|link| {
|
2018-08-21 22:24:59 +03:00
|
|
|
(link.owner, link.name().clone(), link.syntax.clone())
|
2018-08-21 18:30:10 +03:00
|
|
|
})
|
2018-08-21 22:24:59 +03:00
|
|
|
.collect();
|
|
|
|
|
res
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 18:22:52 +03:00
|
|
|
pub fn problems(
|
|
|
|
|
&self,
|
|
|
|
|
file: FileId,
|
|
|
|
|
file_resolver: &FileResolver,
|
|
|
|
|
syntax_provider: &SyntaxProvider,
|
|
|
|
|
mut cb: impl FnMut(ast::Name, &Problem),
|
|
|
|
|
) {
|
|
|
|
|
let module = self.file2module(file);
|
|
|
|
|
let links = self.links(file_resolver, syntax_provider);
|
|
|
|
|
links
|
|
|
|
|
.links
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|link| link.owner == module)
|
|
|
|
|
.filter_map(|link| {
|
|
|
|
|
let problem = link.problem.as_ref()?;
|
|
|
|
|
Some((link, problem))
|
|
|
|
|
})
|
|
|
|
|
.for_each(|(link, problem)| cb(link.name_node(), problem))
|
2018-08-27 20:58:38 +03:00
|
|
|
}
|
|
|
|
|
|
2018-08-21 22:24:59 +03:00
|
|
|
fn links(
|
|
|
|
|
&self,
|
|
|
|
|
file_resolver: &FileResolver,
|
|
|
|
|
syntax_provider: &SyntaxProvider,
|
|
|
|
|
) -> RwLockReadGuard<State> {
|
|
|
|
|
{
|
|
|
|
|
let guard = self.state.read();
|
|
|
|
|
if guard.changes.is_empty() {
|
|
|
|
|
return guard;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let mut guard = self.state.write();
|
|
|
|
|
if !guard.changes.is_empty() {
|
|
|
|
|
guard.apply_changes(file_resolver, syntax_provider);
|
|
|
|
|
}
|
|
|
|
|
assert!(guard.changes.is_empty());
|
|
|
|
|
RwLockWriteGuard::downgrade(guard)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl State {
|
|
|
|
|
pub fn apply_changes(
|
|
|
|
|
&mut self,
|
|
|
|
|
file_resolver: &FileResolver,
|
|
|
|
|
syntax_provider: &SyntaxProvider,
|
|
|
|
|
) {
|
|
|
|
|
let mut reresolve = false;
|
|
|
|
|
for (file_id, kind) in self.changes.drain(..) {
|
|
|
|
|
let mod_id = ModuleId(file_id);
|
|
|
|
|
self.links.retain(|link| link.owner != mod_id);
|
|
|
|
|
match kind {
|
|
|
|
|
ChangeKind::Delete => {
|
|
|
|
|
for link in self.links.iter_mut() {
|
|
|
|
|
link.points_to.retain(|&x| x != mod_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ChangeKind::Insert => {
|
|
|
|
|
let file = syntax_provider(file_id);
|
|
|
|
|
self.links.extend(
|
|
|
|
|
file
|
|
|
|
|
.ast()
|
|
|
|
|
.modules()
|
|
|
|
|
.filter_map(|it| Link::new(mod_id, it))
|
|
|
|
|
);
|
|
|
|
|
reresolve = true;
|
|
|
|
|
}
|
|
|
|
|
ChangeKind::Update => {
|
|
|
|
|
let file = syntax_provider(file_id);
|
|
|
|
|
self.links.extend(
|
|
|
|
|
file
|
|
|
|
|
.ast()
|
|
|
|
|
.modules()
|
|
|
|
|
.filter_map(|it| Link::new(mod_id, it))
|
|
|
|
|
.map(|mut link| {
|
|
|
|
|
link.resolve(file_resolver);
|
|
|
|
|
link
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if reresolve {
|
|
|
|
|
for link in self.links.iter_mut() {
|
|
|
|
|
link.resolve(file_resolver)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-21 18:30:10 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Link {
|
|
|
|
|
fn new(owner: ModuleId, module: ast::Module) -> Option<Link> {
|
|
|
|
|
if module.name().is_none() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let link = Link {
|
|
|
|
|
owner,
|
|
|
|
|
syntax: module.syntax().owned(),
|
|
|
|
|
points_to: Vec::new(),
|
2018-08-28 18:22:52 +03:00
|
|
|
problem: None,
|
2018-08-21 18:30:10 +03:00
|
|
|
};
|
|
|
|
|
Some(link)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn name(&self) -> SmolStr {
|
2018-08-28 18:22:52 +03:00
|
|
|
self.name_node().text()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn name_node(&self) -> ast::Name {
|
|
|
|
|
self.ast().name().unwrap()
|
2018-08-21 18:30:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn ast(&self) -> ast::Module {
|
|
|
|
|
ast::Module::cast(self.syntax.borrowed())
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resolve(&mut self, file_resolver: &FileResolver) {
|
2018-08-28 20:29:36 +03:00
|
|
|
if !self.ast().has_semi() {
|
|
|
|
|
self.problem = None;
|
|
|
|
|
self.points_to = Vec::new();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 18:22:52 +03:00
|
|
|
let mod_name = file_resolver.file_stem(self.owner.0);
|
|
|
|
|
let is_dir_owner =
|
|
|
|
|
mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
|
|
|
|
|
|
|
|
|
let file_mod = RelativePathBuf::from(format!("../{}.rs", self.name()));
|
|
|
|
|
let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", self.name()));
|
|
|
|
|
if is_dir_owner {
|
|
|
|
|
self.points_to = [&file_mod, &dir_mod].iter()
|
|
|
|
|
.filter_map(|path| file_resolver.resolve(self.owner.0, path))
|
|
|
|
|
.map(ModuleId)
|
|
|
|
|
.collect();
|
|
|
|
|
self.problem = if self.points_to.is_empty() {
|
|
|
|
|
Some(Problem::UnresolvedModule {
|
|
|
|
|
candidate: file_mod,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
self.points_to = Vec::new();
|
|
|
|
|
self.problem = Some(Problem::NotDirOwner {
|
|
|
|
|
move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
|
|
|
|
|
candidate: file_mod,
|
|
|
|
|
});
|
|
|
|
|
}
|
2018-08-21 18:30:10 +03:00
|
|
|
}
|
|
|
|
|
}
|