diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 9429f8f552..19b64fece1 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -16,7 +16,7 @@ use rayon::prelude::*; use std::{ fmt, - path::Path, + path::{Path, PathBuf}, sync::{ Arc, atomic::{AtomicUsize, Ordering::SeqCst}, @@ -26,8 +26,9 @@ use std::{ }; use libsyntax2::{ - TextUnit, - ast::{self, AstNode}, + TextUnit, TextRange, SyntaxRoot, + ast::{self, AstNode, NameOwner}, + SyntaxKind::*, }; use libeditor::{LineIndex, FileSymbol, find_node}; @@ -119,33 +120,58 @@ impl World { Ok(index.clone()) } - pub fn world_symbols<'a>(&'a self, mut query: Query) -> impl Iterator + 'a { + 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 approximately_resolve_symbol<'a>( - &'a self, + pub fn approximately_resolve_symbol( + &self, id: FileId, offset: TextUnit, - ) -> Result> { + ) -> Result> { let file = self.file_syntax(id)?; - let syntax = file.syntax(); - let syntax = syntax.as_ref(); - let name_ref = find_node::>(syntax, offset); - let name = match name_ref { - None => return Ok(vec![]), - Some(name_ref) => name_ref.text(), - }; + let syntax = file.syntax_ref(); + if let Some(name_ref) = find_node::>(syntax, offset) { + return Ok(self.index_resolve(name_ref)); + } + if let Some(name) = find_node::>(syntax, offset) { + if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { + if module.has_semi() { + return Ok(self.resolve_module(id, module)); + } + } + } + Ok(vec![]) + } + fn index_resolve(&self, name_ref: ast::NameRef<&SyntaxRoot>) -> Vec<(FileId, FileSymbol)> { + let name = name_ref.text(); let mut query = Query::new(name.to_string()); query.exact(); query.limit(4); - Ok(self.world_symbols(query).collect()) + self.world_symbols(query) + } + + fn resolve_module(&self, id: FileId, module: ast::Module<&SyntaxRoot>) -> Vec<(FileId, FileSymbol)> { + let name = match module.name() { + Some(name) => name.text(), + None => return Vec::new(), + }; + let id = match self.resolve_relative_path(id, &PathBuf::from(format!("../{}.rs", name))) { + Some(id) => id, + None => return Vec::new(), + }; + vec![(id, FileSymbol { + name: name.clone(), + node_range: TextRange::offset_len(0.into(), 0.into()), + kind: MODULE, + })] } fn resolve_relative_path(&self, id: FileId, path: &Path) -> Option { diff --git a/crates/libanalysis/src/symbol_index.rs b/crates/libanalysis/src/symbol_index.rs index 35141cfdc9..3c3252956b 100644 --- a/crates/libanalysis/src/symbol_index.rs +++ b/crates/libanalysis/src/symbol_index.rs @@ -62,10 +62,10 @@ impl Query { self.limit = limit } - pub(crate) fn process<'a>( + pub(crate) fn process( &mut self, - file: &'a FileSymbols, - ) -> Vec<&'a FileSymbol> { + file: &FileSymbols, + ) -> Vec { fn is_type(kind: SyntaxKind) -> bool { match kind { STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_DEF => true, @@ -87,7 +87,7 @@ impl Query { if self.exact && symbol.name != self.query { continue; } - res.push(symbol); + res.push(symbol.clone()); self.limit -= 1; } res diff --git a/crates/libeditor/src/symbols.rs b/crates/libeditor/src/symbols.rs index 2585d9579e..ce870430e2 100644 --- a/crates/libeditor/src/symbols.rs +++ b/crates/libeditor/src/symbols.rs @@ -9,7 +9,7 @@ use libsyntax2::{ }; use TextRange; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StructureNode { pub parent: Option, pub label: String, @@ -18,7 +18,7 @@ pub struct StructureNode { pub kind: SyntaxKind, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FileSymbol { pub name: SmolStr, pub node_range: TextRange, diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index d53b12ab86..9b9200f996 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -118,3 +118,12 @@ impl ImplItem { (first, second) } } + +impl Module { + pub fn has_semi(&self) -> bool { + match self.syntax_ref().last_child() { + None => false, + Some(node) => node.kind() == SEMI, + } + } +} diff --git a/crates/libsyntax2/src/yellow/syntax.rs b/crates/libsyntax2/src/yellow/syntax.rs index b264e008ae..bb390751a2 100644 --- a/crates/libsyntax2/src/yellow/syntax.rs +++ b/crates/libsyntax2/src/yellow/syntax.rs @@ -89,7 +89,15 @@ impl SyntaxNode { } pub fn first_child(&self) -> Option> { - self.children().next() + let red = self.red().get_child(0)?; + Some(SyntaxNode { root: self.root.clone(), red }) + } + + pub fn last_child(&self) -> Option> { + let n = self.red().n_children(); + let n = n.checked_sub(1)?; + let red = self.red().get_child(n)?; + Some(SyntaxNode { root: self.root.clone(), red }) } pub fn next_sibling(&self) -> Option> { diff --git a/crates/server/src/path_map.rs b/crates/server/src/path_map.rs index e198e165de..d2b811a3ba 100644 --- a/crates/server/src/path_map.rs +++ b/crates/server/src/path_map.rs @@ -1,4 +1,4 @@ -use std::path::{PathBuf, Path}; +use std::path::{PathBuf, Path, Component}; use im; use libanalysis::{FileId}; @@ -36,6 +36,7 @@ impl PathMap { pub fn resolve(&self, id: FileId, relpath: &Path) -> Option { let path = self.get_path(id).join(relpath); + let path = normalize(&path); self.get_id(&path) } @@ -50,3 +51,47 @@ impl PathMap { id } } + +fn normalize(path: &Path) -> PathBuf { + let mut components = path.components().peekable(); + let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { + components.next(); + PathBuf::from(c.as_os_str()) + } else { + PathBuf::new() + }; + + for component in components { + match component { + Component::Prefix(..) => unreachable!(), + Component::RootDir => { + ret.push(component.as_os_str()); + } + Component::CurDir => {} + Component::ParentDir => { + ret.pop(); + } + Component::Normal(c) => { + ret.push(c); + } + } + } + ret +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_resolve() { + let mut m = PathMap::new(); + let id1 = m.get_or_insert(PathBuf::from("/foo")); + let id2 = m.get_or_insert(PathBuf::from("/foo/bar.rs")); + assert_eq!( + m.resolve(id1, &PathBuf::from("bar.rs")), + Some(id2), + ) + } +} +