Module map implementation

This commit is contained in:
Aleksey Kladov 2018-08-21 18:30:10 +03:00
parent 4d8be26584
commit b937262c9b
7 changed files with 247 additions and 47 deletions

View file

@ -10,16 +10,18 @@ extern crate fst;
extern crate rayon; extern crate rayon;
mod symbol_index; mod symbol_index;
mod module_map;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use rayon::prelude::*; use rayon::prelude::*;
use std::{ use std::{
fmt, fmt,
path::{Path, PathBuf}, mem,
path::{Path},
sync::{ sync::{
Arc, Arc,
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicBool, Ordering::SeqCst},
}, },
collections::hash_map::HashMap, collections::hash_map::HashMap,
time::Instant, time::Instant,
@ -32,7 +34,10 @@ use libsyntax2::{
}; };
use libeditor::{LineIndex, FileSymbol, find_node}; use libeditor::{LineIndex, FileSymbol, find_node};
use self::symbol_index::FileSymbols; use self::{
symbol_index::FileSymbols,
module_map::ModuleMap,
};
pub use self::symbol_index::Query; pub use self::symbol_index::Query;
pub type Result<T> = ::std::result::Result<T, ::failure::Error>; pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
@ -42,11 +47,12 @@ pub type FileResolver = dyn Fn(FileId, &Path) -> Option<FileId> + Send + Sync;
#[derive(Debug)] #[derive(Debug)]
pub struct WorldState { pub struct WorldState {
updates: Vec<FileId>,
data: Arc<WorldData> data: Arc<WorldData>
} }
#[derive(Clone)]
pub struct World { pub struct World {
needs_reindex: AtomicBool,
file_resolver: Arc<FileResolver>, file_resolver: Arc<FileResolver>,
data: Arc<WorldData>, data: Arc<WorldData>,
} }
@ -57,18 +63,48 @@ impl fmt::Debug for World {
} }
} }
impl Clone for World {
fn clone(&self) -> World {
World {
needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)),
file_resolver: Arc::clone(&self.file_resolver),
data: Arc::clone(&self.data),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32); pub struct FileId(pub u32);
impl WorldState { impl WorldState {
pub fn new() -> WorldState { pub fn new() -> WorldState {
WorldState { WorldState {
data: Arc::new(WorldData::default()) updates: Vec::new(),
data: Arc::new(WorldData::default()),
} }
} }
pub fn snapshot(&self, file_resolver: impl Fn(FileId, &Path) -> Option<FileId> + 'static + Send + Sync) -> World { pub fn snapshot(
&mut self,
file_resolver: impl Fn(FileId, &Path) -> Option<FileId> + 'static + Send + Sync,
) -> World {
let needs_reindex = self.updates.len() >= INDEXING_THRESHOLD;
if !self.updates.is_empty() {
let updates = mem::replace(&mut self.updates, Vec::new());
let data = self.data_mut();
for file_id in updates {
let syntax = data.file_map
.get(&file_id)
.map(|it| it.syntax());
data.module_map.update_file(
file_id,
syntax,
&file_resolver,
);
}
}
World { World {
needs_reindex: AtomicBool::new(needs_reindex),
file_resolver: Arc::new(file_resolver), file_resolver: Arc::new(file_resolver),
data: self.data.clone() data: self.data.clone()
} }
@ -79,28 +115,28 @@ impl WorldState {
} }
pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) { pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) {
let data = self.data_mut(); let mut updates = Vec::new();
let mut cnt = 0; {
for (id, text) in changes { let data = self.data_mut();
cnt += 1; for (file_id, text) in changes {
data.file_map.remove(&id); data.file_map.remove(&file_id);
if let Some(text) = text { if let Some(text) = text {
let file_data = FileData::new(text); let file_data = FileData::new(text);
data.file_map.insert(id, Arc::new(file_data)); data.file_map.insert(file_id, Arc::new(file_data));
} else { } else {
data.file_map.remove(&id); data.file_map.remove(&file_id);
}
updates.push(file_id);
} }
} }
*data.unindexed.get_mut() += cnt; self.updates.extend(updates)
} }
fn data_mut(&mut self) -> &mut WorldData { fn data_mut(&mut self) -> &mut WorldData {
if Arc::get_mut(&mut self.data).is_none() { if Arc::get_mut(&mut self.data).is_none() {
self.data = Arc::new(WorldData { self.data = Arc::new(WorldData {
unindexed: AtomicUsize::new(
self.data.unindexed.load(SeqCst)
),
file_map: self.data.file_map.clone(), file_map: self.data.file_map.clone(),
module_map: self.data.module_map.clone(),
}); });
} }
Arc::get_mut(&mut self.data).unwrap() Arc::get_mut(&mut self.data).unwrap()
@ -131,6 +167,24 @@ impl World {
.collect() .collect()
} }
pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> {
let module_map = &self.data.module_map;
let id = module_map.file2module(id);
module_map
.parent_modules(id)
.into_iter()
.map(|(id, m)| {
let id = module_map.module2file(id);
let sym = FileSymbol {
name: m.name().unwrap().text(),
node_range: m.syntax().range(),
kind: MODULE,
};
(id, sym)
})
.collect()
}
pub fn approximately_resolve_symbol( pub fn approximately_resolve_symbol(
&self, &self,
id: FileId, id: FileId,
@ -178,32 +232,22 @@ impl World {
Some(name) => name.text(), Some(name) => name.text(),
None => return Vec::new(), None => return Vec::new(),
}; };
let paths = &[ let module_map = &self.data.module_map;
PathBuf::from(format!("../{}.rs", name)), let id = module_map.file2module(id);
PathBuf::from(format!("../{}/mod.rs", name)), module_map
]; .child_module_by_name(id, name.as_str())
paths.iter() .into_iter()
.filter_map(|path| self.resolve_relative_path(id, path)) .map(|id| module_map.module2file(id))
.collect() .collect()
} }
fn resolve_relative_path(&self, id: FileId, path: &Path) -> Option<FileId> {
(self.file_resolver)(id, path)
}
fn reindex(&self) { fn reindex(&self) {
let data = &*self.data; if self.needs_reindex.compare_and_swap(false, true, SeqCst) {
let unindexed = data.unindexed.load(SeqCst);
if unindexed < INDEXING_THRESHOLD {
return;
}
if unindexed == data.unindexed.compare_and_swap(unindexed, 0, SeqCst) {
let now = Instant::now(); let now = Instant::now();
let data = &*self.data;
data.file_map data.file_map
.par_iter() .par_iter()
.for_each(|(_, data)| { .for_each(|(_, data)| drop(data.symbols()));
data.symbols();
});
info!("parallel indexing took {:?}", now.elapsed()); info!("parallel indexing took {:?}", now.elapsed());
} }
} }
@ -218,8 +262,8 @@ impl World {
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct WorldData { struct WorldData {
unindexed: AtomicUsize,
file_map: HashMap<FileId, Arc<FileData>>, file_map: HashMap<FileId, Arc<FileData>>,
module_map: ModuleMap,
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -0,0 +1,117 @@
use std::{
path::{PathBuf},
};
use libsyntax2::{
ast::{self, AstNode, NameOwner},
SyntaxNode, ParsedFile, SmolStr,
};
use {FileId, FileResolver};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ModuleId(FileId);
#[derive(Clone, Debug, Default)]
pub struct ModuleMap {
links: Vec<Link>,
}
#[derive(Clone, Debug)]
struct Link {
owner: ModuleId,
syntax: SyntaxNode,
points_to: Vec<ModuleId>,
}
impl ModuleMap {
pub fn update_file(
&mut self,
file_id: FileId,
syntax: Option<&ParsedFile>,
file_resolver: &FileResolver,
) {
let mod_id = ModuleId(file_id);
self.links.retain(|link| link.owner != mod_id);
match syntax {
None => {
for link in self.links.iter_mut() {
link.points_to.retain(|&x| x != mod_id);
}
}
Some(syntax) => {
self.links.extend(
syntax.ast().modules().filter_map(|it| {
Link::new(mod_id, it)
})
)
}
}
self.links.iter_mut().for_each(|link| {
link.resolve(file_resolver)
})
}
pub fn module2file(&self, m: ModuleId) -> FileId {
m.0
}
pub fn file2module(&self, file_id: FileId) -> ModuleId {
ModuleId(file_id)
}
pub fn child_module_by_name(&self, parent_mod: ModuleId, child_mod: &str) -> Vec<ModuleId> {
self.links
.iter()
.filter(|link| link.owner == parent_mod)
.filter(|link| link.name() == child_mod)
.filter_map(|it| it.points_to.first())
.map(|&it| it)
.collect()
}
pub fn parent_modules<'a>(&'a self, m: ModuleId) -> impl Iterator<Item=(ModuleId, ast::Module<'a>)> + 'a {
self.links
.iter()
.filter(move |link| link.points_to.iter().any(|&it| it == m))
.map(|link| {
(link.owner, link.ast())
})
}
}
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(),
};
Some(link)
}
fn name(&self) -> SmolStr {
self.ast().name()
.unwrap()
.text()
}
fn ast(&self) -> ast::Module {
ast::Module::cast(self.syntax.borrowed())
.unwrap()
}
fn resolve(&mut self, file_resolver: &FileResolver) {
let name = self.name();
let paths = &[
PathBuf::from(format!("../{}.rs", name)),
PathBuf::from(format!("../{}/mod.rs", name)),
];
self.points_to = paths.iter()
.filter_map(|path| file_resolver(self.owner.0, path))
.map(ModuleId)
.collect();
}
}

View file

@ -43,3 +43,24 @@ fn test_resolve_module() {
&symbols, &symbols,
); );
} }
#[test]
fn test_resolve_parent_module() {
let mut world = WorldState::new();
world.change_file(FileId(1), Some("mod foo;".to_string()));
world.change_file(FileId(2), Some("".to_string()));
let snap = world.snapshot(|id, path| {
assert_eq!(id, FileId(1));
if path == PathBuf::from("../foo/mod.rs") {
return None;
}
assert_eq!(path, PathBuf::from("../foo.rs"));
Some(FileId(2))
});
let symbols = snap.parent_module(FileId(2));
assert_eq_dbg(
r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
&symbols,
);
}

View file

@ -127,6 +127,12 @@ impl<'a> File<'a> {
.children() .children()
.filter_map(FnDef::cast) .filter_map(FnDef::cast)
} }
pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
self.syntax()
.children()
.filter_map(Module::cast)
}
} }
// FnDef // FnDef
@ -239,7 +245,13 @@ impl<'a> AstNode<'a> for Module<'a> {
impl<'a> ast::NameOwner<'a> for Module<'a> {} impl<'a> ast::NameOwner<'a> for Module<'a> {}
impl<'a> ast::AttrsOwner<'a> for Module<'a> {} impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
impl<'a> Module<'a> {} impl<'a> Module<'a> {
pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
self.syntax()
.children()
.filter_map(Module::cast)
}
}
// Name // Name
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View file

@ -218,7 +218,8 @@ Grammar(
ast: { ast: {
"File": ( "File": (
collections: [ collections: [
["functions", "FnDef"] ["functions", "FnDef"],
["modules", "Module"],
] ]
), ),
"FnDef": ( traits: ["NameOwner", "AttrsOwner"] ), "FnDef": ( traits: ["NameOwner", "AttrsOwner"] ),
@ -231,7 +232,12 @@ Grammar(
"NamedField": ( traits: ["NameOwner", "AttrsOwner"] ), "NamedField": ( traits: ["NameOwner", "AttrsOwner"] ),
"EnumDef": ( traits: ["NameOwner", "AttrsOwner"] ), "EnumDef": ( traits: ["NameOwner", "AttrsOwner"] ),
"TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ), "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ),
"Module": ( traits: ["NameOwner", "AttrsOwner"] ), "Module": (
traits: ["NameOwner", "AttrsOwner"],
collections: [
["modules", "Module"]
]
),
"ConstDef": ( traits: ["NameOwner", "AttrsOwner"] ), "ConstDef": ( traits: ["NameOwner", "AttrsOwner"] ),
"StaticDef": ( traits: ["NameOwner", "AttrsOwner"] ), "StaticDef": ( traits: ["NameOwner", "AttrsOwner"] ),
"TypeDef": ( traits: ["NameOwner", "AttrsOwner"] ), "TypeDef": ( traits: ["NameOwner", "AttrsOwner"] ),

View file

@ -94,7 +94,7 @@ pub(super) fn main_loop(
Event::Msg(msg) => { Event::Msg(msg) => {
match msg { match msg {
RawMsg::Request(req) => { RawMsg::Request(req) => {
if !on_request(io, &state, pool, &task_sender, req)? { if !on_request(io, &mut state, pool, &task_sender, req)? {
return Ok(()); return Ok(());
} }
} }
@ -114,7 +114,7 @@ pub(super) fn main_loop(
fn on_request( fn on_request(
io: &mut Io, io: &mut Io,
world: &ServerWorldState, world: &mut ServerWorldState,
pool: &ThreadPool, pool: &ThreadPool,
sender: &Sender<Task>, sender: &Sender<Task>,
req: RawRequest, req: RawRequest,
@ -252,7 +252,7 @@ fn on_notification(
fn handle_request_on_threadpool<R: req::ClientRequest>( fn handle_request_on_threadpool<R: req::ClientRequest>(
req: &mut Option<RawRequest>, req: &mut Option<RawRequest>,
pool: &ThreadPool, pool: &ThreadPool,
world: &ServerWorldState, world: &mut ServerWorldState,
sender: &Sender<Task>, sender: &Sender<Task>,
f: fn(ServerWorld, R::Params) -> Result<R::Result>, f: fn(ServerWorld, R::Params) -> Result<R::Result>,
) -> Result<()> ) -> Result<()>

View file

@ -86,7 +86,7 @@ impl ServerWorldState {
Ok(()) Ok(())
} }
pub fn snapshot(&self) -> ServerWorld { pub fn snapshot(&mut self) -> ServerWorld {
let pm = self.path_map.clone(); let pm = self.path_map.clone();
ServerWorld { ServerWorld {
analysis: self.analysis.snapshot(move |id, path| { analysis: self.analysis.snapshot(move |id, path| {