diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index d01144627f..fee0d10d6c 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -17,7 +17,6 @@ use rayon::prelude::*; use std::{ fmt, - mem, path::{Path}, sync::{ Arc, @@ -36,18 +35,16 @@ use libeditor::{LineIndex, FileSymbol, find_node}; use self::{ symbol_index::FileSymbols, - module_map::ModuleMap, + module_map::{ModuleMap, ChangeKind}, }; pub use self::symbol_index::Query; pub type Result = ::std::result::Result; -const INDEXING_THRESHOLD: usize = 128; pub type FileResolver = dyn Fn(FileId, &Path) -> Option + Send + Sync; #[derive(Debug)] pub struct WorldState { - updates: Vec, data: Arc } @@ -79,32 +76,16 @@ pub struct FileId(pub u32); impl WorldState { pub fn new() -> WorldState { WorldState { - updates: Vec::new(), data: Arc::new(WorldData::default()), } } pub fn snapshot( - &mut self, + &self, file_resolver: impl Fn(FileId, &Path) -> Option + '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 { - needs_reindex: AtomicBool::new(needs_reindex), + needs_reindex: AtomicBool::new(false), file_resolver: Arc::new(file_resolver), data: self.data.clone() } @@ -115,21 +96,26 @@ impl WorldState { } pub fn change_files(&mut self, changes: impl Iterator)>) { - let mut updates = Vec::new(); - { - let data = self.data_mut(); - for (file_id, text) in changes { - data.file_map.remove(&file_id); - if let Some(text) = text { - let file_data = FileData::new(text); - data.file_map.insert(file_id, Arc::new(file_data)); + let data = self.data_mut(); + for (file_id, text) in changes { + let change_kind = if data.file_map.remove(&file_id).is_some() { + if text.is_some() { + ChangeKind::Update } else { - data.file_map.remove(&file_id); + ChangeKind::Delete } - updates.push(file_id); + } else { + ChangeKind::Insert + }; + data.module_map.update_file(file_id, change_kind); + data.file_map.remove(&file_id); + if let Some(text) = text { + let file_data = FileData::new(text); + data.file_map.insert(file_id, Arc::new(file_data)); + } else { + data.file_map.remove(&file_id); } } - self.updates.extend(updates) } fn data_mut(&mut self) -> &mut WorldData { @@ -171,13 +157,17 @@ impl World { let module_map = &self.data.module_map; let id = module_map.file2module(id); module_map - .parent_modules(id) + .parent_modules( + id, + &*self.file_resolver, + &|file_id| self.file_syntax(file_id).unwrap(), + ) .into_iter() - .map(|(id, m)| { + .map(|(id, name, node)| { let id = module_map.module2file(id); let sym = FileSymbol { - name: m.name().unwrap().text(), - node_range: m.syntax().range(), + name, + node_range: node.range(), kind: MODULE, }; (id, sym) @@ -235,7 +225,11 @@ impl World { let module_map = &self.data.module_map; let id = module_map.file2module(id); module_map - .child_module_by_name(id, name.as_str()) + .child_module_by_name( + id, name.as_str(), + &*self.file_resolver, + &|file_id| self.file_syntax(file_id).unwrap(), + ) .into_iter() .map(|id| module_map.module2file(id)) .collect() diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs index 9b4c778b61..83e6e57f71 100644 --- a/crates/libanalysis/src/module_map.rs +++ b/crates/libanalysis/src/module_map.rs @@ -2,17 +2,38 @@ use std::{ path::{PathBuf}, }; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use libsyntax2::{ - ast::{self, AstNode, NameOwner}, - SyntaxNode, ParsedFile, SmolStr, + ast::{self, AstNode, NameOwner, ParsedFile}, + SyntaxNode, SmolStr, }; use {FileId, FileResolver}; +type SyntaxProvider<'a> = dyn Fn(FileId) -> ParsedFile + 'a; + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct ModuleId(FileId); -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] pub struct ModuleMap { + state: RwLock, +} + +#[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)>, links: Vec, } @@ -24,31 +45,8 @@ struct Link { } 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 update_file(&mut self, file: FileId, change_kind: ChangeKind) { + self.state.get_mut().changes.push((file, change_kind)); } pub fn module2file(&self, m: ModuleId) -> FileId { @@ -59,8 +57,15 @@ impl ModuleMap { ModuleId(file_id) } - pub fn child_module_by_name(&self, parent_mod: ModuleId, child_mod: &str) -> Vec { - self.links + pub fn child_module_by_name<'a>( + &self, + parent_mod: ModuleId, + child_mod: &str, + file_resolver: &FileResolver, + syntax_provider: &SyntaxProvider, + ) -> Vec { + self.links(file_resolver, syntax_provider) + .links .iter() .filter(|link| link.owner == parent_mod) .filter(|link| link.name() == child_mod) @@ -69,13 +74,90 @@ impl ModuleMap { .collect() } - pub fn parent_modules<'a>(&'a self, m: ModuleId) -> impl Iterator)> + 'a { - self.links + 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 .iter() .filter(move |link| link.points_to.iter().any(|&it| it == m)) .map(|link| { - (link.owner, link.ast()) + (link.owner, link.name().clone(), link.syntax.clone()) }) + .collect(); + res + } + + fn links( + &self, + file_resolver: &FileResolver, + syntax_provider: &SyntaxProvider, + ) -> RwLockReadGuard { + { + 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) + } + } } } diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index 12a903dac5..752d6ddb21 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs @@ -252,7 +252,7 @@ fn on_notification( fn handle_request_on_threadpool( req: &mut Option, pool: &ThreadPool, - world: &mut ServerWorldState, + world: &ServerWorldState, sender: &Sender, f: fn(ServerWorld, R::Params) -> Result, ) -> Result<()> diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 9850822cde..c0d2efb863 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs @@ -86,7 +86,7 @@ impl ServerWorldState { Ok(()) } - pub fn snapshot(&mut self) -> ServerWorld { + pub fn snapshot(&self) -> ServerWorld { let pm = self.path_map.clone(); ServerWorld { analysis: self.analysis.snapshot(move |id, path| {