store file rsovler

This commit is contained in:
Aleksey Kladov 2018-09-10 12:57:40 +03:00
parent 4f64709666
commit 505895a25f
7 changed files with 146 additions and 116 deletions

View File

@ -7,6 +7,7 @@
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 @@
roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot},
};
#[derive(Clone, Debug)]
pub(crate) struct FileResolverImp {
inner: Arc<FileResolver>
}
impl FileResolverImp {
pub(crate) fn new(inner: Arc<FileResolver>) -> 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<FileId> {
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<FileId> {
panic!("file resolver not set")
}
}
FileResolverImp { inner: Arc::new(DummyResolver) }
}
}
#[derive(Debug)]
pub(crate) struct AnalysisHostImpl {
data: Arc<WorldData>
@ -32,13 +67,9 @@ pub fn new() -> AnalysisHostImpl {
data: Arc::new(WorldData::default()),
}
}
pub fn analysis(
&self,
file_resolver: Arc<dyn FileResolver>,
) -> AnalysisImpl {
pub fn analysis(&self) -> AnalysisImpl {
AnalysisImpl {
needs_reindex: AtomicBool::new(false),
file_resolver,
data: self.data.clone(),
}
}
@ -48,6 +79,11 @@ pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<S
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 @@ fn data_mut(&mut self) -> &mut WorldData {
pub(crate) struct AnalysisImpl {
needs_reindex: AtomicBool,
file_resolver: Arc<dyn FileResolver>,
data: Arc<WorldData>,
}
@ -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 @@ pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> {
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 @@ pub fn crate_for(&self, file_id: FileId) -> Vec<CrateId> {
}
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 @@ pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
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 @@ fn resolve_module(&self, module_map: &ModuleMap, file_id: FileId, module: ast::M
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 @@ fn reindex(&self) {
}
}
#[derive(Clone, Default, Debug)]
#[derive(Default, Clone, Debug)]
struct WorldData {
file_resolver: FileResolverImp,
crate_graph: CrateGraph,
root: WritableSourceRoot,
libs: Vec<Arc<ReadonlySourceRoot>>,

View File

@ -19,11 +19,12 @@
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<CrateId, FileId>,
}
pub trait FileResolver: Send + Sync + 'static {
fn file_stem(&self, id: FileId) -> String;
fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
pub trait FileResolver: Debug + Send + Sync + 'static {
fn file_stem(&self, file_id: FileId) -> String;
fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>;
}
#[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<String>) {
self.change_files(::std::iter::once((file_id, text)));
@ -65,6 +66,9 @@ pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
pub fn change_files(&mut self, mut changes: impl Iterator<Item=(FileId, Option<String>)>) {
self.imp.change_files(&mut changes)
}
pub fn set_file_resolver(&mut self, resolver: Arc<FileResolver>) {
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<FileResolver>) -> LibraryData {
let file_resolver = FileResolverImp::new(file_resolver);
let root = roots::ReadonlySourceRoot::new(files, file_resolver);
LibraryData { root }
}
}

View File

@ -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 @@ fn clone(&self) -> ModuleMap {
#[derive(Clone, Debug, Default)]
struct State {
file_resolver: FileResolverImp,
changes: Vec<(FileId, ChangeKind)>,
links: Vec<Link>,
}
@ -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<ModuleId> {
self.links(file_resolver, syntax_provider)
self.links(syntax_provider)
.links
.iter()
.filter(|link| link.owner == parent_mod)
@ -92,11 +90,10 @@ pub fn child_module_by_name<'a>(
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 @@ pub fn parent_modules(
pub fn parent_module_ids(
&self,
m: ModuleId,
file_resolver: &FileResolver,
syntax_provider: &SyntaxProvider,
) -> Vec<ModuleId> {
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 @@ fn for_each_parent_link(
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 @@ pub fn problems(
fn links(
&self,
file_resolver: &FileResolver,
syntax_provider: &SyntaxProvider,
) -> RwLockReadGuard<State> {
{
@ -162,7 +155,7 @@ fn links(
}
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 @@ fn links(
impl State {
pub fn apply_changes(
&mut self,
file_resolver: &FileResolver,
syntax_provider: &SyntaxProvider,
) {
let mut reresolve = false;
@ -197,13 +189,14 @@ pub fn apply_changes(
}
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 @@ pub fn apply_changes(
}
if reresolve {
for link in self.links.iter_mut() {
link.resolve(file_resolver)
link.resolve(&self.file_resolver)
}
}
}
@ -245,7 +238,7 @@ fn ast(&self) -> ast::Module {
.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();

View File

@ -12,6 +12,7 @@
use {
FileId,
imp::FileResolverImp,
module_map::{ModuleMap, ChangeKind},
symbol_index::SymbolIndex,
};
@ -48,6 +49,9 @@ pub fn update(&mut self, file_id: FileId, text: Option<String>) {
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))

View File

@ -3,21 +3,38 @@
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<Item=(FileId, &'a RelativePath)> + '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 @@ fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> {
#[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)],

View File

@ -135,12 +135,12 @@ enum Event {
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);
});

View File

@ -6,7 +6,7 @@
};
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 @@ pub fn apply_fs_changes(&mut self, events: Vec<FileEvent>) {
self.analysis_host.change_files(changes);
}
pub fn events_to_files(&mut self, events: Vec<FileEvent>) -> 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<FileEvent>) -> (Vec<(FileId, String)>, Arc<FileResolver>) {
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 @@ pub fn add_lib(&mut self, data: LibraryData) {
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 @@ pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) {
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()
}
}