Merge #193
193: Inline modules r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
de9bb9cfef
12 changed files with 200 additions and 89 deletions
|
@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{self, SyntaxDatabase},
|
db::{self, SyntaxDatabase},
|
||||||
descriptors::function::FnScopes,
|
descriptors::function::FnScopes,
|
||||||
descriptors::module::{ModuleId, ModuleScope, ModuleTree},
|
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
|
||||||
descriptors::DescriptorDatabase,
|
descriptors::DescriptorDatabase,
|
||||||
input::FilesDatabase,
|
input::FilesDatabase,
|
||||||
Cancelable, FileId,
|
Cancelable, FileId,
|
||||||
|
@ -35,7 +35,7 @@ pub(crate) fn resolve_based_completion(
|
||||||
let source_root_id = db.file_source_root(file_id);
|
let source_root_id = db.file_source_root(file_id);
|
||||||
let file = db.file_syntax(file_id);
|
let file = db.file_syntax(file_id);
|
||||||
let module_tree = db.module_tree(source_root_id)?;
|
let module_tree = db.module_tree(source_root_id)?;
|
||||||
let module_id = match module_tree.any_module_for_file(file_id) {
|
let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,16 +5,16 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode, FnDefNode},
|
ast::{self, AstNode, FnDefNode},
|
||||||
SmolStr, TextRange,
|
TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::SyntaxDatabase,
|
db::SyntaxDatabase,
|
||||||
descriptors::function::{resolve_local_name, FnId, FnScopes},
|
descriptors::function::{resolve_local_name, FnId, FnScopes},
|
||||||
descriptors::module::{ModuleId, ModuleScope, ModuleTree},
|
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
|
||||||
input::SourceRootId,
|
input::SourceRootId,
|
||||||
syntax_ptr::LocalSyntaxPtr,
|
syntax_ptr::LocalSyntaxPtr,
|
||||||
Cancelable, FileId,
|
Cancelable,
|
||||||
};
|
};
|
||||||
|
|
||||||
salsa::query_group! {
|
salsa::query_group! {
|
||||||
|
@ -23,7 +23,7 @@ salsa::query_group! {
|
||||||
type ModuleTreeQuery;
|
type ModuleTreeQuery;
|
||||||
use fn module::imp::module_tree;
|
use fn module::imp::module_tree;
|
||||||
}
|
}
|
||||||
fn submodules(file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> {
|
fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
|
||||||
type SubmodulesQuery;
|
type SubmodulesQuery;
|
||||||
use fn module::imp::submodules;
|
use fn module::imp::submodules;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,23 +19,64 @@ use super::{
|
||||||
ModuleTree, Problem,
|
ModuleTree, Problem,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn submodules(
|
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
||||||
db: &impl DescriptorDatabase,
|
pub(crate) enum Submodule {
|
||||||
file_id: FileId,
|
Declaration(SmolStr),
|
||||||
) -> Cancelable<Arc<Vec<SmolStr>>> {
|
Definition(SmolStr, ModuleSource),
|
||||||
db::check_canceled(db)?;
|
|
||||||
let file = db.file_syntax(file_id);
|
|
||||||
let root = file.ast();
|
|
||||||
let submodules = modules(root).map(|(name, _)| name).collect();
|
|
||||||
Ok(Arc::new(submodules))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn modules(root: ast::Root<'_>) -> impl Iterator<Item = (SmolStr, ast::Module<'_>)> {
|
impl Submodule {
|
||||||
root.modules().filter_map(|module| {
|
fn name(&self) -> &SmolStr {
|
||||||
let name = module.name()?.text();
|
match self {
|
||||||
if !module.has_semi() {
|
Submodule::Declaration(name) => name,
|
||||||
return None;
|
Submodule::Definition(name, _) => name,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn submodules(
|
||||||
|
db: &impl DescriptorDatabase,
|
||||||
|
source: ModuleSource,
|
||||||
|
) -> Cancelable<Arc<Vec<Submodule>>> {
|
||||||
|
db::check_canceled(db)?;
|
||||||
|
let file_id = source.file_id();
|
||||||
|
let submodules = match source.resolve(db) {
|
||||||
|
ModuleSourceNode::Root(it) => collect_submodules(file_id, it.ast()),
|
||||||
|
ModuleSourceNode::Inline(it) => it
|
||||||
|
.ast()
|
||||||
|
.item_list()
|
||||||
|
.map(|it| collect_submodules(file_id, it))
|
||||||
|
.unwrap_or_else(Vec::new),
|
||||||
|
};
|
||||||
|
return Ok(Arc::new(submodules));
|
||||||
|
|
||||||
|
fn collect_submodules<'a>(
|
||||||
|
file_id: FileId,
|
||||||
|
root: impl ast::ModuleItemOwner<'a>,
|
||||||
|
) -> Vec<Submodule> {
|
||||||
|
modules(root)
|
||||||
|
.map(|(name, m)| {
|
||||||
|
if m.has_semi() {
|
||||||
|
Submodule::Declaration(name)
|
||||||
|
} else {
|
||||||
|
let src = ModuleSource::new_inline(file_id, m);
|
||||||
|
Submodule::Definition(name, src)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn modules<'a>(
|
||||||
|
root: impl ast::ModuleItemOwner<'a>,
|
||||||
|
) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
|
||||||
|
root.items()
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
ast::ModuleItem::Module(m) => Some(m),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter_map(|module| {
|
||||||
|
let name = module.name()?.text();
|
||||||
Some((name, module))
|
Some((name, module))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -66,11 +107,6 @@ pub(crate) fn module_tree(
|
||||||
Ok(Arc::new(res))
|
Ok(Arc::new(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
|
||||||
pub struct Submodule {
|
|
||||||
pub name: SmolStr,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_module_tree<'a>(
|
fn create_module_tree<'a>(
|
||||||
db: &impl DescriptorDatabase,
|
db: &impl DescriptorDatabase,
|
||||||
source_root: SourceRootId,
|
source_root: SourceRootId,
|
||||||
|
@ -85,7 +121,8 @@ fn create_module_tree<'a>(
|
||||||
|
|
||||||
let source_root = db.source_root(source_root);
|
let source_root = db.source_root(source_root);
|
||||||
for &file_id in source_root.files.iter() {
|
for &file_id in source_root.files.iter() {
|
||||||
if visited.contains(&file_id) {
|
let source = ModuleSource::File(file_id);
|
||||||
|
if visited.contains(&source) {
|
||||||
continue; // TODO: use explicit crate_roots here
|
continue; // TODO: use explicit crate_roots here
|
||||||
}
|
}
|
||||||
assert!(!roots.contains_key(&file_id));
|
assert!(!roots.contains_key(&file_id));
|
||||||
|
@ -96,7 +133,7 @@ fn create_module_tree<'a>(
|
||||||
&mut visited,
|
&mut visited,
|
||||||
&mut roots,
|
&mut roots,
|
||||||
None,
|
None,
|
||||||
file_id,
|
source,
|
||||||
)?;
|
)?;
|
||||||
roots.insert(file_id, module_id);
|
roots.insert(file_id, module_id);
|
||||||
}
|
}
|
||||||
|
@ -107,26 +144,29 @@ fn build_subtree(
|
||||||
db: &impl DescriptorDatabase,
|
db: &impl DescriptorDatabase,
|
||||||
source_root: &SourceRoot,
|
source_root: &SourceRoot,
|
||||||
tree: &mut ModuleTree,
|
tree: &mut ModuleTree,
|
||||||
visited: &mut FxHashSet<FileId>,
|
visited: &mut FxHashSet<ModuleSource>,
|
||||||
roots: &mut FxHashMap<FileId, ModuleId>,
|
roots: &mut FxHashMap<FileId, ModuleId>,
|
||||||
parent: Option<LinkId>,
|
parent: Option<LinkId>,
|
||||||
file_id: FileId,
|
source: ModuleSource,
|
||||||
) -> Cancelable<ModuleId> {
|
) -> Cancelable<ModuleId> {
|
||||||
visited.insert(file_id);
|
visited.insert(source);
|
||||||
let id = tree.push_mod(ModuleData {
|
let id = tree.push_mod(ModuleData {
|
||||||
source: ModuleSource::File(file_id),
|
source,
|
||||||
parent,
|
parent,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
});
|
});
|
||||||
for name in db.submodules(file_id)?.iter() {
|
for sub in db.submodules(source)?.iter() {
|
||||||
let (points_to, problem) = resolve_submodule(file_id, name, &source_root.file_resolver);
|
|
||||||
let link = tree.push_link(LinkData {
|
let link = tree.push_link(LinkData {
|
||||||
name: name.clone(),
|
name: sub.name().clone(),
|
||||||
owner: id,
|
owner: id,
|
||||||
points_to: Vec::new(),
|
points_to: Vec::new(),
|
||||||
problem: None,
|
problem: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (points_to, problem) = match sub {
|
||||||
|
Submodule::Declaration(name) => {
|
||||||
|
let (points_to, problem) =
|
||||||
|
resolve_submodule(source, &name, &source_root.file_resolver);
|
||||||
let points_to = points_to
|
let points_to = points_to
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|file_id| match roots.remove(&file_id) {
|
.map(|file_id| match roots.remove(&file_id) {
|
||||||
|
@ -134,9 +174,33 @@ fn build_subtree(
|
||||||
tree.module_mut(module_id).parent = Some(link);
|
tree.module_mut(module_id).parent = Some(link);
|
||||||
Ok(module_id)
|
Ok(module_id)
|
||||||
}
|
}
|
||||||
None => build_subtree(db, source_root, tree, visited, roots, Some(link), file_id),
|
None => build_subtree(
|
||||||
|
db,
|
||||||
|
source_root,
|
||||||
|
tree,
|
||||||
|
visited,
|
||||||
|
roots,
|
||||||
|
Some(link),
|
||||||
|
ModuleSource::File(file_id),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.collect::<Cancelable<Vec<_>>>()?;
|
.collect::<Cancelable<Vec<_>>>()?;
|
||||||
|
(points_to, problem)
|
||||||
|
}
|
||||||
|
Submodule::Definition(_name, submodule_source) => {
|
||||||
|
let points_to = build_subtree(
|
||||||
|
db,
|
||||||
|
source_root,
|
||||||
|
tree,
|
||||||
|
visited,
|
||||||
|
roots,
|
||||||
|
Some(link),
|
||||||
|
*submodule_source,
|
||||||
|
)?;
|
||||||
|
(vec![points_to], None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tree.link_mut(link).points_to = points_to;
|
tree.link_mut(link).points_to = points_to;
|
||||||
tree.link_mut(link).problem = problem;
|
tree.link_mut(link).problem = problem;
|
||||||
}
|
}
|
||||||
|
@ -144,10 +208,17 @@ fn build_subtree(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_submodule(
|
fn resolve_submodule(
|
||||||
file_id: FileId,
|
source: ModuleSource,
|
||||||
name: &SmolStr,
|
name: &SmolStr,
|
||||||
file_resolver: &FileResolverImp,
|
file_resolver: &FileResolverImp,
|
||||||
) -> (Vec<FileId>, Option<Problem>) {
|
) -> (Vec<FileId>, Option<Problem>) {
|
||||||
|
let file_id = match source {
|
||||||
|
ModuleSource::File(it) => it,
|
||||||
|
ModuleSource::Inline(..) => {
|
||||||
|
// TODO
|
||||||
|
return (Vec::new(), None);
|
||||||
|
}
|
||||||
|
};
|
||||||
let mod_name = file_resolver.file_stem(file_id);
|
let mod_name = file_resolver.file_stem(file_id);
|
||||||
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,17 @@ pub(crate) struct ModuleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleTree {
|
impl ModuleTree {
|
||||||
pub(crate) fn modules_for_file(&self, file_id: FileId) -> Vec<ModuleId> {
|
pub(crate) fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
|
||||||
self.mods
|
self.mods
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_idx, it)| it.source.is_file(file_id))
|
.filter(|(_idx, it)| it.source == source)
|
||||||
.map(|(idx, _)| ModuleId(idx as u32))
|
.map(|(idx, _)| ModuleId(idx as u32))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn any_module_for_file(&self, file_id: FileId) -> Option<ModuleId> {
|
pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
|
||||||
self.modules_for_file(file_id).pop()
|
self.modules_for_source(source).pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +142,7 @@ impl LinkId {
|
||||||
.1;
|
.1;
|
||||||
ast.into()
|
ast.into()
|
||||||
}
|
}
|
||||||
ModuleSourceNode::Inline(..) => {
|
ModuleSourceNode::Inline(it) => it,
|
||||||
unimplemented!("https://github.com/rust-analyzer/rust-analyzer/issues/181")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +155,12 @@ struct ModuleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleSource {
|
impl ModuleSource {
|
||||||
|
pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
|
||||||
|
assert!(!module.has_semi());
|
||||||
|
let ptr = SyntaxPtr::new(file_id, module.syntax());
|
||||||
|
ModuleSource::Inline(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn as_file(self) -> Option<FileId> {
|
pub(crate) fn as_file(self) -> Option<FileId> {
|
||||||
match self {
|
match self {
|
||||||
ModuleSource::File(f) => Some(f),
|
ModuleSource::File(f) => Some(f),
|
||||||
|
@ -164,6 +168,13 @@ impl ModuleSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn file_id(self) -> FileId {
|
||||||
|
match self {
|
||||||
|
ModuleSource::File(f) => f,
|
||||||
|
ModuleSource::Inline(ptr) => ptr.file_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
|
fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
|
||||||
match self {
|
match self {
|
||||||
ModuleSource::File(file_id) => {
|
ModuleSource::File(file_id) => {
|
||||||
|
@ -178,10 +189,6 @@ impl ModuleSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_file(self, file_id: FileId) -> bool {
|
|
||||||
self.as_file() == Some(file_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Hash, Debug, PartialEq, Eq)]
|
#[derive(Hash, Debug, PartialEq, Eq)]
|
||||||
|
|
|
@ -220,27 +220,32 @@ impl AnalysisImpl {
|
||||||
let source_root = self.db.file_source_root(file_id);
|
let source_root = self.db.file_source_root(file_id);
|
||||||
self.db.module_tree(source_root)
|
self.db.module_tree(source_root)
|
||||||
}
|
}
|
||||||
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
pub fn parent_module(
|
||||||
|
&self,
|
||||||
|
file_id: FileId,
|
||||||
|
offset: TextUnit,
|
||||||
|
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||||
let module_tree = self.module_tree(file_id)?;
|
let module_tree = self.module_tree(file_id)?;
|
||||||
|
let file = self.db.file_syntax(file_id);
|
||||||
|
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), offset) {
|
||||||
|
Some(m) if !m.has_semi() => ModuleSource::new_inline(file_id, m),
|
||||||
|
_ => ModuleSource::File(file_id),
|
||||||
|
};
|
||||||
|
|
||||||
let res = module_tree
|
let res = module_tree
|
||||||
.modules_for_file(file_id)
|
.modules_for_source(module_source)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|module_id| {
|
.filter_map(|module_id| {
|
||||||
let link = module_id.parent_link(&module_tree)?;
|
let link = module_id.parent_link(&module_tree)?;
|
||||||
let file_id = match link.owner(&module_tree).source(&module_tree) {
|
let file_id = link.owner(&module_tree).source(&module_tree).file_id();
|
||||||
ModuleSource::File(file_id) => file_id,
|
|
||||||
ModuleSource::Inline(..) => {
|
|
||||||
//TODO: https://github.com/rust-analyzer/rust-analyzer/issues/181
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let decl = link.bind_source(&module_tree, &*self.db);
|
let decl = link.bind_source(&module_tree, &*self.db);
|
||||||
let decl = decl.ast();
|
let decl = decl.ast();
|
||||||
|
|
||||||
|
let decl_name = decl.name().unwrap();
|
||||||
|
|
||||||
let sym = FileSymbol {
|
let sym = FileSymbol {
|
||||||
name: decl.name().unwrap().text(),
|
name: decl_name.text(),
|
||||||
node_range: decl.syntax().range(),
|
node_range: decl_name.syntax().range(),
|
||||||
kind: MODULE,
|
kind: MODULE,
|
||||||
};
|
};
|
||||||
Some((file_id, sym))
|
Some((file_id, sym))
|
||||||
|
@ -252,7 +257,7 @@ impl AnalysisImpl {
|
||||||
let module_tree = self.module_tree(file_id)?;
|
let module_tree = self.module_tree(file_id)?;
|
||||||
let crate_graph = self.db.crate_graph();
|
let crate_graph = self.db.crate_graph();
|
||||||
let res = module_tree
|
let res = module_tree
|
||||||
.modules_for_file(file_id)
|
.modules_for_source(ModuleSource::File(file_id))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|it| it.root(&module_tree))
|
.map(|it| it.root(&module_tree))
|
||||||
.filter_map(|it| it.source(&module_tree).as_file())
|
.filter_map(|it| it.source(&module_tree).as_file())
|
||||||
|
@ -376,7 +381,7 @@ impl AnalysisImpl {
|
||||||
fix: None,
|
fix: None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if let Some(m) = module_tree.any_module_for_file(file_id) {
|
if let Some(m) = module_tree.any_module_for_source(ModuleSource::File(file_id)) {
|
||||||
for (name_node, problem) in m.problems(&module_tree, &*self.db) {
|
for (name_node, problem) in m.problems(&module_tree, &*self.db) {
|
||||||
let diag = match problem {
|
let diag = match problem {
|
||||||
Problem::UnresolvedModule { candidate } => {
|
Problem::UnresolvedModule { candidate } => {
|
||||||
|
@ -539,7 +544,7 @@ impl AnalysisImpl {
|
||||||
Some(name) => name.text(),
|
Some(name) => name.text(),
|
||||||
None => return Vec::new(),
|
None => return Vec::new(),
|
||||||
};
|
};
|
||||||
let module_id = match module_tree.any_module_for_file(file_id) {
|
let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
None => return Vec::new(),
|
None => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -263,8 +263,12 @@ impl Analysis {
|
||||||
) -> Cancelable<Vec<(FileId, TextRange)>> {
|
) -> Cancelable<Vec<(FileId, TextRange)>> {
|
||||||
Ok(self.imp.find_all_refs(file_id, offset))
|
Ok(self.imp.find_all_refs(file_id, offset))
|
||||||
}
|
}
|
||||||
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
pub fn parent_module(
|
||||||
self.imp.parent_module(file_id)
|
&self,
|
||||||
|
file_id: FileId,
|
||||||
|
offset: TextUnit,
|
||||||
|
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||||
|
self.imp.parent_module(file_id, offset)
|
||||||
}
|
}
|
||||||
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
|
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
|
||||||
self.imp.crate_for(file_id)
|
self.imp.crate_for(file_id)
|
||||||
|
|
|
@ -22,6 +22,10 @@ impl SyntaxPtr {
|
||||||
let local = LocalSyntaxPtr::new(node);
|
let local = LocalSyntaxPtr::new(node);
|
||||||
SyntaxPtr { file_id, local }
|
SyntaxPtr { file_id, local }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn file_id(self) -> FileId {
|
||||||
|
self.file_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A pionter to a syntax node inside a file.
|
/// A pionter to a syntax node inside a file.
|
||||||
|
|
|
@ -92,9 +92,28 @@ fn test_resolve_parent_module() {
|
||||||
<|>// empty
|
<|>// empty
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
let symbols = analysis.parent_module(pos.file_id).unwrap();
|
let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
|
||||||
assert_eq_dbg(
|
assert_eq_dbg(
|
||||||
r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
|
r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#,
|
||||||
|
&symbols,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resolve_parent_module_for_inline() {
|
||||||
|
let (analysis, pos) = analysis_and_position(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo {
|
||||||
|
mod bar {
|
||||||
|
mod baz { <|> }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
|
||||||
|
assert_eq_dbg(
|
||||||
|
r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#,
|
||||||
&symbols,
|
&symbols,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,11 +218,13 @@ pub fn handle_goto_definition(
|
||||||
|
|
||||||
pub fn handle_parent_module(
|
pub fn handle_parent_module(
|
||||||
world: ServerWorld,
|
world: ServerWorld,
|
||||||
params: TextDocumentIdentifier,
|
params: req::TextDocumentPositionParams,
|
||||||
) -> Result<Vec<Location>> {
|
) -> Result<Vec<Location>> {
|
||||||
let file_id = params.try_conv_with(&world)?;
|
let file_id = params.text_document.try_conv_with(&world)?;
|
||||||
|
let line_index = world.analysis().file_line_index(file_id);
|
||||||
|
let offset = params.position.conv_with(&line_index);
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for (file_id, symbol) in world.analysis().parent_module(file_id)? {
|
for (file_id, symbol) in world.analysis().parent_module(file_id, offset)? {
|
||||||
let line_index = world.analysis().file_line_index(file_id);
|
let line_index = world.analysis().file_line_index(file_id);
|
||||||
let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
|
let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
|
||||||
res.push(location);
|
res.push(location);
|
||||||
|
|
|
@ -93,7 +93,7 @@ pub struct Decoration {
|
||||||
pub enum ParentModule {}
|
pub enum ParentModule {}
|
||||||
|
|
||||||
impl Request for ParentModule {
|
impl Request for ParentModule {
|
||||||
type Params = TextDocumentIdentifier;
|
type Params = TextDocumentPositionParams;
|
||||||
type Result = Vec<Location>;
|
type Result = Vec<Location>;
|
||||||
const METHOD: &'static str = "m/parentModule";
|
const METHOD: &'static str = "m/parentModule";
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,6 @@ import {
|
||||||
SourceChange
|
SourceChange
|
||||||
} from './apply_source_change';
|
} from './apply_source_change';
|
||||||
|
|
||||||
interface OnEnterParams {
|
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
|
||||||
position: lc.Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function handle(event: { text: string }): Promise<boolean> {
|
export async function handle(event: { text: string }): Promise<boolean> {
|
||||||
const editor = vscode.window.activeTextEditor;
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
@ -20,7 +16,7 @@ export async function handle(event: { text: string }): Promise<boolean> {
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const request: OnEnterParams = {
|
const request: lc.TextDocumentPositionParams = {
|
||||||
textDocument: { uri: editor.document.uri.toString() },
|
textDocument: { uri: editor.document.uri.toString() },
|
||||||
position: Server.client.code2ProtocolConverter.asPosition(
|
position: Server.client.code2ProtocolConverter.asPosition(
|
||||||
editor.selection.active
|
editor.selection.active
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import { Location, TextDocumentIdentifier } from 'vscode-languageclient';
|
import * as lc from 'vscode-languageclient';
|
||||||
import { Server } from '../server';
|
import { Server } from '../server';
|
||||||
|
|
||||||
export async function handle() {
|
export async function handle() {
|
||||||
|
@ -8,10 +8,13 @@ export async function handle() {
|
||||||
if (editor == null || editor.document.languageId !== 'rust') {
|
if (editor == null || editor.document.languageId !== 'rust') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const request: TextDocumentIdentifier = {
|
const request: lc.TextDocumentPositionParams = {
|
||||||
uri: editor.document.uri.toString()
|
textDocument: { uri: editor.document.uri.toString() },
|
||||||
|
position: Server.client.code2ProtocolConverter.asPosition(
|
||||||
|
editor.selection.active
|
||||||
|
)
|
||||||
};
|
};
|
||||||
const response = await Server.client.sendRequest<Location[]>(
|
const response = await Server.client.sendRequest<lc.Location[]>(
|
||||||
'm/parentModule',
|
'm/parentModule',
|
||||||
request
|
request
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue