From 505895a25f98423de07c3cec4793b66a19d098c7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 10 Sep 2018 12:57:40 +0300 Subject: store file rsovler --- crates/libanalysis/src/imp.rs | 65 +++++++++++++++++-------- crates/libanalysis/src/lib.rs | 21 ++++---- crates/libanalysis/src/module_map.rs | 41 +++++++--------- crates/libanalysis/src/roots.rs | 7 ++- crates/libanalysis/tests/tests.rs | 93 +++++++++++++++++------------------- crates/server/src/main_loop/mod.rs | 4 +- crates/server/src/server_world.rs | 31 +++++++----- 7 files changed, 146 insertions(+), 116 deletions(-) diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs index 47b0d79ff..3e65ee14a 100644 --- a/crates/libanalysis/src/imp.rs +++ b/crates/libanalysis/src/imp.rs @@ -7,6 +7,7 @@ use std::{ collections::{HashSet, VecDeque}, }; +use relative_path::RelativePath; use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit}; use libsyntax2::{ TextUnit, TextRange, SmolStr, File, AstNode, @@ -21,6 +22,40 @@ use { roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, }; + +#[derive(Clone, Debug)] +pub(crate) struct FileResolverImp { + inner: Arc +} + +impl FileResolverImp { + pub(crate) fn new(inner: Arc) -> FileResolverImp { + FileResolverImp { inner } + } + pub(crate) fn file_stem(&self, file_id: FileId) -> String { + self.inner.file_stem(file_id) + } + pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option { + self.inner.resolve(file_id, path) + } +} + +impl Default for FileResolverImp { + fn default() -> FileResolverImp { + #[derive(Debug)] + struct DummyResolver; + impl FileResolver for DummyResolver { + fn file_stem(&self, _file_: FileId) -> String { + panic!("file resolver not set") + } + fn resolve(&self, _file_id: FileId, _path: &::relative_path::RelativePath) -> Option { + panic!("file resolver not set") + } + } + FileResolverImp { inner: Arc::new(DummyResolver) } + } +} + #[derive(Debug)] pub(crate) struct AnalysisHostImpl { data: Arc @@ -32,13 +67,9 @@ impl AnalysisHostImpl { data: Arc::new(WorldData::default()), } } - pub fn analysis( - &self, - file_resolver: Arc, - ) -> AnalysisImpl { + pub fn analysis(&self) -> AnalysisImpl { AnalysisImpl { needs_reindex: AtomicBool::new(false), - file_resolver, data: self.data.clone(), } } @@ -48,6 +79,11 @@ impl AnalysisHostImpl { data.root.update(file_id, text); } } + pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { + let data = self.data_mut(); + data.file_resolver = resolver.clone(); + data.root.set_file_resolver(resolver); + } pub fn set_crate_graph(&mut self, graph: CrateGraph) { let mut visited = HashSet::new(); for &file_id in graph.crate_roots.values() { @@ -67,7 +103,6 @@ impl AnalysisHostImpl { pub(crate) struct AnalysisImpl { needs_reindex: AtomicBool, - file_resolver: Arc, data: Arc, } @@ -81,7 +116,6 @@ impl Clone for AnalysisImpl { fn clone(&self) -> AnalysisImpl { AnalysisImpl { needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), - file_resolver: Arc::clone(&self.file_resolver), data: Arc::clone(&self.data), } } @@ -117,11 +151,7 @@ impl AnalysisImpl { let module_map = root.module_map(); let id = module_map.file2module(file_id); module_map - .parent_modules( - id, - &*self.file_resolver, - &|file_id| root.syntax(file_id), - ) + .parent_modules(id, &|file_id| root.syntax(file_id)) .into_iter() .map(|(id, name, node)| { let id = module_map.module2file(id); @@ -149,11 +179,7 @@ impl AnalysisImpl { } let mid = module_map.file2module(id); let parents = module_map - .parent_module_ids( - mid, - &*self.file_resolver, - &|file_id| self.file_syntax(file_id), - ) + .parent_module_ids(mid, &|file_id| self.file_syntax(file_id)) .into_iter() .map(|id| module_map.module2file(id)) .filter(|&id| visited.insert(id)); @@ -213,7 +239,6 @@ impl AnalysisImpl { module_map.problems( file_id, - &*self.file_resolver, &|file_id| self.file_syntax(file_id), |name_node, problem| { let diag = match problem { @@ -291,7 +316,6 @@ impl AnalysisImpl { module_map .child_module_by_name( id, name.as_str(), - &*self.file_resolver, &|file_id| self.file_syntax(file_id), ) .into_iter() @@ -306,8 +330,9 @@ impl AnalysisImpl { } } -#[derive(Clone, Default, Debug)] +#[derive(Default, Clone, Debug)] struct WorldData { + file_resolver: FileResolverImp, crate_graph: CrateGraph, root: WritableSourceRoot, libs: Vec>, diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index ff4fc709b..80cde079f 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -19,11 +19,12 @@ mod roots; use std::{ sync::Arc, collections::HashMap, + fmt::Debug, }; use relative_path::{RelativePath, RelativePathBuf}; use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; -use imp::{AnalysisImpl, AnalysisHostImpl}; +use imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp}; pub use libeditor::{ StructureNode, LineIndex, FileSymbol, @@ -42,9 +43,9 @@ pub struct CrateGraph { pub crate_roots: HashMap, } -pub trait FileResolver: Send + Sync + 'static { - fn file_stem(&self, id: FileId) -> String; - fn resolve(&self, id: FileId, path: &RelativePath) -> Option; +pub trait FileResolver: Debug + Send + Sync + 'static { + fn file_stem(&self, file_id: FileId) -> String; + fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option; } #[derive(Debug)] @@ -56,8 +57,8 @@ impl AnalysisHost { pub fn new() -> AnalysisHost { AnalysisHost { imp: AnalysisHostImpl::new() } } - pub fn analysis(&self, file_resolver: impl FileResolver) -> Analysis { - Analysis { imp: self.imp.analysis(Arc::new(file_resolver)) } + pub fn analysis(&self) -> Analysis { + Analysis { imp: self.imp.analysis() } } pub fn change_file(&mut self, file_id: FileId, text: Option) { self.change_files(::std::iter::once((file_id, text))); @@ -65,6 +66,9 @@ impl AnalysisHost { pub fn change_files(&mut self, mut changes: impl Iterator)>) { self.imp.change_files(&mut changes) } + pub fn set_file_resolver(&mut self, resolver: Arc) { + self.imp.set_file_resolver(FileResolverImp::new(resolver)); + } pub fn set_crate_graph(&mut self, graph: CrateGraph) { self.imp.set_crate_graph(graph) } @@ -223,8 +227,9 @@ pub struct LibraryData { } impl LibraryData { - pub fn prepare(files: Vec<(FileId, String)>) -> LibraryData { - let root = roots::ReadonlySourceRoot::new(files); + pub fn prepare(files: Vec<(FileId, String)>, file_resolver: Arc) -> LibraryData { + let file_resolver = FileResolverImp::new(file_resolver); + let root = roots::ReadonlySourceRoot::new(files, file_resolver); LibraryData { root } } } diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs index 1b225ecee..9acebd6e2 100644 --- a/crates/libanalysis/src/module_map.rs +++ b/crates/libanalysis/src/module_map.rs @@ -1,12 +1,11 @@ use relative_path::RelativePathBuf; - use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use libsyntax2::{ File, ast::{self, AstNode, NameOwner}, SyntaxNode, SmolStr, }; -use {FileId, FileResolver}; +use {FileId, imp::FileResolverImp}; type SyntaxProvider<'a> = dyn Fn(FileId) -> &'a File + 'a; @@ -32,6 +31,7 @@ impl Clone for ModuleMap { #[derive(Clone, Debug, Default)] struct State { + file_resolver: FileResolverImp, changes: Vec<(FileId, ChangeKind)>, links: Vec, } @@ -59,27 +59,25 @@ impl ModuleMap { pub fn new() -> ModuleMap { Default::default() } - - pub fn update_file(&mut self, file: FileId, change_kind: ChangeKind) { - self.state.get_mut().changes.push((file, change_kind)); + pub fn update_file(&mut self, file_id: FileId, change_kind: ChangeKind) { + self.state.get_mut().changes.push((file_id, change_kind)); + } + pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { + self.state.get_mut().file_resolver = 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<'a>( &self, parent_mod: ModuleId, child_mod: &str, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, ) -> Vec { - self.links(file_resolver, syntax_provider) + self.links(syntax_provider) .links .iter() .filter(|link| link.owner == parent_mod) @@ -92,11 +90,10 @@ impl ModuleMap { pub fn parent_modules( &self, m: ModuleId, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, ) -> Vec<(ModuleId, SmolStr, SyntaxNode)> { let mut res = Vec::new(); - self.for_each_parent_link(m, file_resolver, syntax_provider, |link| { + self.for_each_parent_link(m, syntax_provider, |link| { res.push( (link.owner, link.name().clone(), link.syntax.clone()) ) @@ -107,22 +104,20 @@ impl ModuleMap { pub fn parent_module_ids( &self, m: ModuleId, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, ) -> Vec { let mut res = Vec::new(); - self.for_each_parent_link(m, file_resolver, syntax_provider, |link| res.push(link.owner)); + self.for_each_parent_link(m, syntax_provider, |link| res.push(link.owner)); res } fn for_each_parent_link( &self, m: ModuleId, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, f: impl FnMut(&Link) ) { - self.links(file_resolver, syntax_provider) + self.links(syntax_provider) .links .iter() .filter(move |link| link.points_to.iter().any(|&it| it == m)) @@ -132,12 +127,11 @@ impl ModuleMap { 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); + let links = self.links(syntax_provider); links .links .iter() @@ -151,7 +145,6 @@ impl ModuleMap { fn links( &self, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, ) -> RwLockReadGuard { { @@ -162,7 +155,7 @@ impl ModuleMap { } let mut guard = self.state.write(); if !guard.changes.is_empty() { - guard.apply_changes(file_resolver, syntax_provider); + guard.apply_changes(syntax_provider); } assert!(guard.changes.is_empty()); RwLockWriteGuard::downgrade(guard) @@ -172,7 +165,6 @@ impl ModuleMap { impl State { pub fn apply_changes( &mut self, - file_resolver: &FileResolver, syntax_provider: &SyntaxProvider, ) { let mut reresolve = false; @@ -197,13 +189,14 @@ impl State { } ChangeKind::Update => { let file = syntax_provider(file_id); + let resolver = &self.file_resolver; self.links.extend( file .ast() .modules() .filter_map(|it| Link::new(mod_id, it)) .map(|mut link| { - link.resolve(file_resolver); + link.resolve(resolver); link }) ); @@ -212,7 +205,7 @@ impl State { } if reresolve { for link in self.links.iter_mut() { - link.resolve(file_resolver) + link.resolve(&self.file_resolver) } } } @@ -245,7 +238,7 @@ impl Link { .unwrap() } - fn resolve(&mut self, file_resolver: &FileResolver) { + fn resolve(&mut self, file_resolver: &FileResolverImp) { if !self.ast().has_semi() { self.problem = None; self.points_to = Vec::new(); diff --git a/crates/libanalysis/src/roots.rs b/crates/libanalysis/src/roots.rs index 6d1375f88..629a697c5 100644 --- a/crates/libanalysis/src/roots.rs +++ b/crates/libanalysis/src/roots.rs @@ -12,6 +12,7 @@ use libsyntax2::File; use { FileId, + imp::FileResolverImp, module_map::{ModuleMap, ChangeKind}, symbol_index::SymbolIndex, }; @@ -48,6 +49,9 @@ impl WritableSourceRoot { self.file_map.insert(file_id, Arc::new((file_data, Default::default()))); } } + pub fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { + self.module_map.set_file_resolver(file_resolver) + } pub fn reindex(&self) { let now = Instant::now(); self.file_map @@ -136,8 +140,9 @@ pub(crate) struct ReadonlySourceRoot { } impl ReadonlySourceRoot { - pub fn new(files: Vec<(FileId, String)>) -> ReadonlySourceRoot { + pub(crate) fn new(files: Vec<(FileId, String)>, file_resolver: FileResolverImp) -> ReadonlySourceRoot { let mut module_map = ModuleMap::new(); + module_map.set_file_resolver(file_resolver); let symbol_index = SymbolIndex::for_files( files.par_iter().map(|(file_id, text)| { (*file_id, File::parse(text)) diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs index ebc8037b3..00efe059c 100644 --- a/crates/libanalysis/tests/tests.rs +++ b/crates/libanalysis/tests/tests.rs @@ -3,21 +3,38 @@ extern crate relative_path; extern crate test_utils; use std::{ + sync::Arc, collections::HashMap, }; -use relative_path::{RelativePath}; -use libanalysis::{AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId}; +use relative_path::{RelativePath, RelativePathBuf}; +use libanalysis::{Analysis, AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId}; use test_utils::assert_eq_dbg; -struct FileMap(&'static [(u32, &'static str)]); +#[derive(Debug)] +struct FileMap(Vec<(FileId, RelativePathBuf)>); + +fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost { + let mut host = AnalysisHost::new(); + let mut file_map = Vec::new(); + for (id, &(path, contents)) in files.iter().enumerate() { + let file_id = FileId((id + 1) as u32); + assert!(path.starts_with('/')); + let path = RelativePathBuf::from_path(&path[1..]).unwrap(); + host.change_file(file_id, Some(contents.to_string())); + file_map.push((file_id, path)); + } + host.set_file_resolver(Arc::new(FileMap(file_map))); + host +} + +fn analysis(files: &'static [(&'static str, &'static str)]) -> Analysis { + analysis_host(files).analysis() +} impl FileMap { fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.0.iter().map(|&(id, path)| { - assert!(path.starts_with('/')); - (FileId(id), RelativePath::new(&path[1..])) - }) + self.0.iter().map(|(id, path)| (*id, path.as_relative_path())) } fn path(&self, id: FileId) -> &RelativePath { @@ -42,14 +59,10 @@ impl FileResolver for FileMap { #[test] fn test_resolve_module() { - let mut world = AnalysisHost::new(); - world.change_file(FileId(1), Some("mod foo;".to_string())); - world.change_file(FileId(2), Some("".to_string())); - - let snap = world.analysis(FileMap(&[ - (1, "/lib.rs"), - (2, "/foo.rs"), - ])); + let snap = analysis(&[ + ("/lib.rs", "mod foo;"), + ("/foo.rs", "") + ]); let (_handle, token) = JobHandle::new(); let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token); assert_eq_dbg( @@ -57,10 +70,10 @@ fn test_resolve_module() { &symbols, ); - let snap = world.analysis(FileMap(&[ - (1, "/lib.rs"), - (2, "/foo/mod.rs") - ])); + let snap = analysis(&[ + ("/lib.rs", "mod foo;"), + ("/foo/mod.rs", "") + ]); let symbols = snap.approximately_resolve_symbol(FileId(1), 4.into(), &token); assert_eq_dbg( r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, @@ -70,10 +83,7 @@ fn test_resolve_module() { #[test] fn test_unresolved_module_diagnostic() { - let mut world = AnalysisHost::new(); - world.change_file(FileId(1), Some("mod foo;".to_string())); - - let snap = world.analysis(FileMap(&[(1, "/lib.rs")])); + let snap = analysis(&[("/lib.rs", "mod foo;")]); let diagnostics = snap.diagnostics(FileId(1)); assert_eq_dbg( r#"[Diagnostic { @@ -90,10 +100,7 @@ fn test_unresolved_module_diagnostic() { #[test] fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { - let mut world = AnalysisHost::new(); - world.change_file(FileId(1), Some("mod foo {}".to_string())); - - let snap = world.analysis(FileMap(&[(1, "/lib.rs")])); + let snap = analysis(&[("/lib.rs", "mod foo {}")]); let diagnostics = snap.diagnostics(FileId(1)); assert_eq_dbg( r#"[]"#, @@ -103,14 +110,10 @@ fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { #[test] fn test_resolve_parent_module() { - let mut world = AnalysisHost::new(); - world.change_file(FileId(1), Some("mod foo;".to_string())); - world.change_file(FileId(2), Some("".to_string())); - - let snap = world.analysis(FileMap(&[ - (1, "/lib.rs"), - (2, "/foo.rs"), - ])); + let snap = analysis(&[ + ("/lib.rs", "mod foo;"), + ("/foo.rs", ""), + ]); let symbols = snap.parent_module(FileId(2)); assert_eq_dbg( r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#, @@ -120,14 +123,11 @@ fn test_resolve_parent_module() { #[test] fn test_resolve_crate_root() { - let mut world = AnalysisHost::new(); - world.change_file(FileId(1), Some("mod foo;".to_string())); - world.change_file(FileId(2), Some("".to_string())); - - let snap = world.analysis(FileMap(&[ - (1, "/lib.rs"), - (2, "/foo.rs"), - ])); + let mut host = analysis_host(&[ + ("/lib.rs", "mod foo;"), + ("/foo.rs", ""), + ]); + let snap = host.analysis(); assert!(snap.crate_for(FileId(2)).is_empty()); let crate_graph = CrateGraph { @@ -137,12 +137,9 @@ fn test_resolve_crate_root() { m }, }; - world.set_crate_graph(crate_graph); + host.set_crate_graph(crate_graph); + let snap = host.analysis(); - let snap = world.analysis(FileMap(&[ - (1, "/lib.rs"), - (2, "/foo.rs"), - ])); assert_eq!( snap.crate_for(FileId(2)), vec![CrateId(1)], diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index f1297ee48..f3b2744bf 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs @@ -135,12 +135,12 @@ fn main_loop_inner( if root == ws_root { state.apply_fs_changes(events); } else { - let files = state.events_to_files(events); + let (files, resolver) = state.events_to_files(events); let sender = libdata_sender.clone(); pool.spawn(move || { let start = ::std::time::Instant::now(); info!("indexing {} ... ", root.display()); - let data = LibraryData::prepare(files); + let data = LibraryData::prepare(files, resolver); info!("indexed {:?} {}", start.elapsed(), root.display()); sender.send(data); }); diff --git a/crates/server/src/server_world.rs b/crates/server/src/server_world.rs index 8ceec594f..ffa0e74b8 100644 --- a/crates/server/src/server_world.rs +++ b/crates/server/src/server_world.rs @@ -6,7 +6,7 @@ use std::{ }; use languageserver_types::Url; -use libanalysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData}; +use libanalysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; use { Result, @@ -64,17 +64,21 @@ impl ServerWorldState { self.analysis_host.change_files(changes); } - pub fn events_to_files(&mut self, events: Vec) -> Vec<(FileId, String)> { - let pm = &mut self.path_map; - events.into_iter() - .map(|event| { - let text = match event.kind { - FileEventKind::Add(text) => text, - }; - (event.path, text) - }) - .map(|(path, text)| (pm.get_or_insert(path, Root::Lib), text)) - .collect() + pub fn events_to_files(&mut self, events: Vec) -> (Vec<(FileId, String)>, Arc) { + let files = { + let pm = &mut self.path_map; + events.into_iter() + .map(|event| { + let text = match event.kind { + FileEventKind::Add(text) => text, + }; + (event.path, text) + }) + .map(|(path, text)| (pm.get_or_insert(path, Root::Lib), text)) + .collect() + }; + let resolver = Arc::new(self.path_map.clone()); + (files, resolver) } pub fn add_lib(&mut self, data: LibraryData) { self.analysis_host.add_library(data); @@ -82,6 +86,7 @@ impl ServerWorldState { pub fn add_mem_file(&mut self, path: PathBuf, text: String) -> FileId { let file_id = self.path_map.get_or_insert(path, Root::Workspace); + self.analysis_host.set_file_resolver(Arc::new(self.path_map.clone())); self.mem_map.insert(file_id, None); if self.path_map.get_root(file_id) != Root::Lib { self.analysis_host.change_file(file_id, Some(text)); @@ -135,7 +140,7 @@ impl ServerWorldState { pub fn snapshot(&self) -> ServerWorld { ServerWorld { workspaces: Arc::clone(&self.workspaces), - analysis: self.analysis_host.analysis(self.path_map.clone()), + analysis: self.analysis_host.analysis(), path_map: self.path_map.clone() } } -- cgit v1.2.3