From 09ea0ca7e5fb5d3e123dc38927b158c798b689ad Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 29 Aug 2018 18:23:57 +0300 Subject: rename world -> analysis impl --- crates/libanalysis/src/api.rs | 4 +- crates/libanalysis/src/imp.rs | 291 ++++++++++++++++++++++++++++++++++++++++++ crates/libanalysis/src/lib.rs | 288 +---------------------------------------- 3 files changed, 298 insertions(+), 285 deletions(-) create mode 100644 crates/libanalysis/src/imp.rs diff --git a/crates/libanalysis/src/api.rs b/crates/libanalysis/src/api.rs index 8882268e6..6c06e0b32 100644 --- a/crates/libanalysis/src/api.rs +++ b/crates/libanalysis/src/api.rs @@ -1,7 +1,7 @@ use relative_path::RelativePathBuf; use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; use libeditor; -use {World, FileId, Query}; +use {imp::AnalysisImpl, FileId, Query}; pub use libeditor::{ LocalEdit, StructureNode, LineIndex, FileSymbol, @@ -49,7 +49,7 @@ pub struct Diagnostic { #[derive(Clone, Debug)] pub struct Analysis { - pub(crate) imp: World + pub(crate) imp: AnalysisImpl } impl Analysis { diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs new file mode 100644 index 000000000..76f0c0c87 --- /dev/null +++ b/crates/libanalysis/src/imp.rs @@ -0,0 +1,291 @@ +use std::{ + sync::{ + Arc, + atomic::{AtomicBool, Ordering::SeqCst}, + }, + fmt, + time::Instant, + collections::HashMap, + panic, +}; + +use libsyntax2::{ + TextUnit, TextRange, SmolStr, File, AstNode, + SyntaxKind::*, + ast::{self, NameOwner}, +}; +use rayon::prelude::*; +use once_cell::sync::OnceCell; +use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset}; + +use { + FileId, FileResolver, Query, Diagnostic, SourceChange, FileSystemEdit, + module_map::Problem, + symbol_index::FileSymbols, + module_map::ModuleMap, +}; + + +pub(crate) struct AnalysisImpl { + pub(crate) needs_reindex: AtomicBool, + pub(crate) file_resolver: Arc, + pub(crate) data: Arc, +} + +impl fmt::Debug for AnalysisImpl { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (&*self.data).fmt(f) + } +} + +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), + } + } +} + +impl AnalysisImpl { + pub fn file_syntax(&self, file_id: FileId) -> File { + self.file_data(file_id).syntax().clone() + } + + pub fn file_line_index(&self, id: FileId) -> LineIndex { + let data = self.file_data(id); + data + .lines + .get_or_init(|| LineIndex::new(&data.text)) + .clone() + } + + pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> { + self.reindex(); + self.data.file_map.iter() + .flat_map(move |(id, data)| { + let symbols = data.symbols(); + query.process(symbols).into_iter().map(move |s| (*id, s)) + }) + .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, + &*self.file_resolver, + &|file_id| self.file_syntax(file_id), + ) + .into_iter() + .map(|(id, name, node)| { + let id = module_map.module2file(id); + let sym = FileSymbol { + name, + node_range: node.range(), + kind: MODULE, + }; + (id, sym) + }) + .collect() + } + + pub fn approximately_resolve_symbol( + &self, + id: FileId, + offset: TextUnit, + ) -> Vec<(FileId, FileSymbol)> { + let file = self.file_syntax(id); + let syntax = file.syntax(); + if let Some(name_ref) = find_node_at_offset::(syntax, offset) { + return self.index_resolve(name_ref); + } + if let Some(name) = find_node_at_offset::(syntax, offset) { + if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { + if module.has_semi() { + let file_ids = self.resolve_module(id, module); + + let res = file_ids.into_iter().map(|id| { + let name = module.name() + .map(|n| n.text()) + .unwrap_or_else(|| SmolStr::new("")); + let symbol = FileSymbol { + name, + node_range: TextRange::offset_len(0.into(), 0.into()), + kind: MODULE, + }; + (id, symbol) + }).collect(); + + return res; + } + } + } + vec![] + } + + pub fn diagnostics(&self, file_id: FileId) -> Vec { + let syntax = self.file_syntax(file_id); + let mut res = libeditor::diagnostics(&syntax) + .into_iter() + .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) + .collect::>(); + + self.data.module_map.problems( + file_id, + &*self.file_resolver, + &|file_id| self.file_syntax(file_id), + |name_node, problem| { + let diag = match problem { + Problem::UnresolvedModule { candidate } => { + let create_file = FileSystemEdit::CreateFile { + anchor: file_id, + path: candidate.clone(), + }; + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, + }; + Diagnostic { + range: name_node.syntax().range(), + message: "unresolved module".to_string(), + fix: Some(fix), + } + } + Problem::NotDirOwner { move_to, candidate } => { + let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; + let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; + let fix = SourceChange { + label: "move file and create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![move_file, create_file], + cursor_position: None, + }; + Diagnostic { + range: name_node.syntax().range(), + message: "can't declare module at this location".to_string(), + fix: Some(fix), + } + } + }; + res.push(diag) + } + ); + res + } + + pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { + let file = self.file_syntax(file_id); + let actions = vec![ + ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), + ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), + ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), + ]; + let mut res = Vec::new(); + for (name, local_edit) in actions { + if let Some(local_edit) = local_edit { + res.push(SourceChange::from_local_edit( + file_id, name, local_edit + )) + } + } + res + } + + fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { + let name = name_ref.text(); + let mut query = Query::new(name.to_string()); + query.exact(); + query.limit(4); + self.world_symbols(query) + } + + fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec { + let name = match module.name() { + Some(name) => name.text(), + None => return Vec::new(), + }; + let module_map = &self.data.module_map; + let id = module_map.file2module(id); + module_map + .child_module_by_name( + id, name.as_str(), + &*self.file_resolver, + &|file_id| self.file_syntax(file_id), + ) + .into_iter() + .map(|id| module_map.module2file(id)) + .collect() + } + + fn reindex(&self) { + if self.needs_reindex.compare_and_swap(false, true, SeqCst) { + let now = Instant::now(); + let data = &*self.data; + data.file_map + .par_iter() + .for_each(|(_, data)| drop(data.symbols())); + info!("parallel indexing took {:?}", now.elapsed()); + } + } + + fn file_data(&self, file_id: FileId) -> Arc { + match self.data.file_map.get(&file_id) { + Some(data) => data.clone(), + None => panic!("unknown file: {:?}", file_id), + } + } +} + +#[derive(Default, Debug)] +pub(crate) struct WorldData { + pub(crate) file_map: HashMap>, + pub(crate) module_map: ModuleMap, +} + +#[derive(Debug)] +pub(crate) struct FileData { + pub(crate) text: String, + pub(crate) symbols: OnceCell, + pub(crate) syntax: OnceCell, + pub(crate) lines: OnceCell, +} + +impl FileData { + pub(crate) fn new(text: String) -> FileData { + FileData { + text, + symbols: OnceCell::new(), + syntax: OnceCell::new(), + lines: OnceCell::new(), + } + } + + fn syntax(&self) -> &File { + let text = &self.text; + let syntax = &self.syntax; + match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { + Ok(file) => file, + Err(err) => { + error!("Parser paniced on:\n------\n{}\n------\n", &self.text); + panic::resume_unwind(err) + } + } + } + + fn syntax_transient(&self) -> File { + self.syntax.get().map(|s| s.clone()) + .unwrap_or_else(|| File::parse(&self.text)) + } + + fn symbols(&self) -> &FileSymbols { + let syntax = self.syntax_transient(); + self.symbols + .get_or_init(|| FileSymbols::new(&syntax)) + } +} diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 5168814e4..027d7439b 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -1,4 +1,3 @@ -#[macro_use] extern crate failure; extern crate parking_lot; #[macro_use] @@ -13,33 +12,20 @@ extern crate relative_path; mod symbol_index; mod module_map; mod api; +mod imp; use std::{ - fmt, - panic, sync::{ Arc, - atomic::{AtomicBool, Ordering::SeqCst}, + atomic::{AtomicBool}, }, - collections::hash_map::HashMap, - time::Instant, }; use relative_path::RelativePath; -use once_cell::sync::OnceCell; -use rayon::prelude::*; - -use libsyntax2::{ - File, - TextUnit, TextRange, SmolStr, - ast::{self, AstNode, NameOwner}, - SyntaxKind::*, -}; -use libeditor::{LineIndex, FileSymbol, find_node_at_offset}; use self::{ - symbol_index::FileSymbols, - module_map::{ModuleMap, ChangeKind, Problem}, + module_map::{ChangeKind}, + imp::{WorldData, FileData}, }; pub use self::symbol_index::Query; pub use self::api::{ @@ -58,28 +44,6 @@ pub struct WorldState { data: Arc } -pub(crate) struct World { - needs_reindex: AtomicBool, - file_resolver: Arc, - data: Arc, -} - -impl fmt::Debug for World { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - (&*self.data).fmt(f) - } -} - -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)] pub struct FileId(pub u32); @@ -94,7 +58,7 @@ impl WorldState { &self, file_resolver: impl FileResolver, ) -> Analysis { - let imp = World { + let imp = imp::AnalysisImpl { needs_reindex: AtomicBool::new(false), file_resolver: Arc::new(file_resolver), data: self.data.clone() @@ -139,245 +103,3 @@ impl WorldState { Arc::get_mut(&mut self.data).unwrap() } } - -impl World { - pub fn file_syntax(&self, file_id: FileId) -> File { - self.file_data(file_id).syntax().clone() - } - - pub fn file_line_index(&self, id: FileId) -> LineIndex { - let data = self.file_data(id); - data - .lines - .get_or_init(|| LineIndex::new(&data.text)) - .clone() - } - - pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> { - self.reindex(); - self.data.file_map.iter() - .flat_map(move |(id, data)| { - let symbols = data.symbols(); - query.process(symbols).into_iter().map(move |s| (*id, s)) - }) - .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, - &*self.file_resolver, - &|file_id| self.file_syntax(file_id), - ) - .into_iter() - .map(|(id, name, node)| { - let id = module_map.module2file(id); - let sym = FileSymbol { - name, - node_range: node.range(), - kind: MODULE, - }; - (id, sym) - }) - .collect() - } - - pub fn approximately_resolve_symbol( - &self, - id: FileId, - offset: TextUnit, - ) -> Vec<(FileId, FileSymbol)> { - let file = self.file_syntax(id); - let syntax = file.syntax(); - if let Some(name_ref) = find_node_at_offset::(syntax, offset) { - return self.index_resolve(name_ref); - } - if let Some(name) = find_node_at_offset::(syntax, offset) { - if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { - if module.has_semi() { - let file_ids = self.resolve_module(id, module); - - let res = file_ids.into_iter().map(|id| { - let name = module.name() - .map(|n| n.text()) - .unwrap_or_else(|| SmolStr::new("")); - let symbol = FileSymbol { - name, - node_range: TextRange::offset_len(0.into(), 0.into()), - kind: MODULE, - }; - (id, symbol) - }).collect(); - - return res; - } - } - } - vec![] - } - - pub fn diagnostics(&self, file_id: FileId) -> Vec { - let syntax = self.file_syntax(file_id); - let mut res = libeditor::diagnostics(&syntax) - .into_iter() - .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) - .collect::>(); - - self.data.module_map.problems( - file_id, - &*self.file_resolver, - &|file_id| self.file_syntax(file_id), - |name_node, problem| { - let diag = match problem { - Problem::UnresolvedModule { candidate } => { - let create_file = FileSystemEdit::CreateFile { - anchor: file_id, - path: candidate.clone(), - }; - let fix = SourceChange { - label: "create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![create_file], - cursor_position: None, - }; - Diagnostic { - range: name_node.syntax().range(), - message: "unresolved module".to_string(), - fix: Some(fix), - } - } - Problem::NotDirOwner { move_to, candidate } => { - let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; - let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; - let fix = SourceChange { - label: "move file and create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![move_file, create_file], - cursor_position: None, - }; - Diagnostic { - range: name_node.syntax().range(), - message: "can't declare module at this location".to_string(), - fix: Some(fix), - } - } - }; - res.push(diag) - } - ); - res - } - - pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { - let file = self.file_syntax(file_id); - let actions = vec![ - ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), - ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), - ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), - ]; - let mut res = Vec::new(); - for (name, local_edit) in actions { - if let Some(local_edit) = local_edit { - res.push(SourceChange::from_local_edit( - file_id, name, local_edit - )) - } - } - res - } - - fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { - let name = name_ref.text(); - let mut query = Query::new(name.to_string()); - query.exact(); - query.limit(4); - self.world_symbols(query) - } - - fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec { - let name = match module.name() { - Some(name) => name.text(), - None => return Vec::new(), - }; - let module_map = &self.data.module_map; - let id = module_map.file2module(id); - module_map - .child_module_by_name( - id, name.as_str(), - &*self.file_resolver, - &|file_id| self.file_syntax(file_id), - ) - .into_iter() - .map(|id| module_map.module2file(id)) - .collect() - } - - fn reindex(&self) { - if self.needs_reindex.compare_and_swap(false, true, SeqCst) { - let now = Instant::now(); - let data = &*self.data; - data.file_map - .par_iter() - .for_each(|(_, data)| drop(data.symbols())); - info!("parallel indexing took {:?}", now.elapsed()); - } - } - - fn file_data(&self, file_id: FileId) -> Arc { - match self.data.file_map.get(&file_id) { - Some(data) => data.clone(), - None => panic!("unknown file: {:?}", file_id), - } - } -} - -#[derive(Default, Debug)] -struct WorldData { - file_map: HashMap>, - module_map: ModuleMap, -} - -#[derive(Debug)] -struct FileData { - text: String, - symbols: OnceCell, - syntax: OnceCell, - lines: OnceCell, -} - -impl FileData { - fn new(text: String) -> FileData { - FileData { - text, - symbols: OnceCell::new(), - syntax: OnceCell::new(), - lines: OnceCell::new(), - } - } - - fn syntax(&self) -> &File { - let text = &self.text; - let syntax = &self.syntax; - match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { - Ok(file) => file, - Err(err) => { - error!("Parser paniced on:\n------\n{}\n------\n", &self.text); - panic::resume_unwind(err) - } - } - } - - fn syntax_transient(&self) -> File { - self.syntax.get().map(|s| s.clone()) - .unwrap_or_else(|| File::parse(&self.text)) - } - - fn symbols(&self) -> &FileSymbols { - let syntax = self.syntax_transient(); - self.symbols - .get_or_init(|| FileSymbols::new(&syntax)) - } -} -- cgit v1.2.3