mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
Fully add inline modules to module tree
This commit is contained in:
parent
44d8919384
commit
6bbcfca7ae
9 changed files with 99 additions and 47 deletions
|
@ -1,7 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, ModuleItemOwner, NameOwner, AstNode},
|
||||
ast::{self, ModuleItemOwner, NameOwner},
|
||||
SmolStr,
|
||||
};
|
||||
use relative_path::RelativePathBuf;
|
||||
|
@ -12,7 +12,6 @@ use crate::{
|
|||
descriptors::DescriptorDatabase,
|
||||
input::{SourceRoot, SourceRootId},
|
||||
Cancelable, FileId, FileResolverImp,
|
||||
syntax_ptr::SyntaxPtr,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -23,7 +22,7 @@ use super::{
|
|||
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
||||
pub(crate) enum Submodule {
|
||||
Declaration(SmolStr),
|
||||
Definition(SmolStr, SyntaxPtr),
|
||||
Definition(SmolStr, ModuleSource),
|
||||
}
|
||||
|
||||
impl Submodule {
|
||||
|
@ -60,7 +59,8 @@ pub(crate) fn submodules(
|
|||
if m.has_semi() {
|
||||
Submodule::Declaration(name)
|
||||
} else {
|
||||
Submodule::Definition(name, SyntaxPtr::new(file_id, m.syntax()))
|
||||
let src = ModuleSource::new_inline(file_id, m);
|
||||
Submodule::Definition(name, src)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
@ -121,7 +121,8 @@ fn create_module_tree<'a>(
|
|||
|
||||
let source_root = db.source_root(source_root);
|
||||
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
|
||||
}
|
||||
assert!(!roots.contains_key(&file_id));
|
||||
|
@ -132,7 +133,7 @@ fn create_module_tree<'a>(
|
|||
&mut visited,
|
||||
&mut roots,
|
||||
None,
|
||||
file_id,
|
||||
source,
|
||||
)?;
|
||||
roots.insert(file_id, module_id);
|
||||
}
|
||||
|
@ -143,18 +144,18 @@ fn build_subtree(
|
|||
db: &impl DescriptorDatabase,
|
||||
source_root: &SourceRoot,
|
||||
tree: &mut ModuleTree,
|
||||
visited: &mut FxHashSet<FileId>,
|
||||
visited: &mut FxHashSet<ModuleSource>,
|
||||
roots: &mut FxHashMap<FileId, ModuleId>,
|
||||
parent: Option<LinkId>,
|
||||
file_id: FileId,
|
||||
source: ModuleSource,
|
||||
) -> Cancelable<ModuleId> {
|
||||
visited.insert(file_id);
|
||||
visited.insert(source);
|
||||
let id = tree.push_mod(ModuleData {
|
||||
source: ModuleSource::File(file_id),
|
||||
source,
|
||||
parent,
|
||||
children: Vec::new(),
|
||||
});
|
||||
for sub in db.submodules(ModuleSource::File(file_id))?.iter() {
|
||||
for sub in db.submodules(source)?.iter() {
|
||||
let link = tree.push_link(LinkData {
|
||||
name: sub.name().clone(),
|
||||
owner: id,
|
||||
|
@ -165,7 +166,7 @@ fn build_subtree(
|
|||
let (points_to, problem) = match sub {
|
||||
Submodule::Declaration(name) => {
|
||||
let (points_to, problem) =
|
||||
resolve_submodule(file_id, &name, &source_root.file_resolver);
|
||||
resolve_submodule(source, &name, &source_root.file_resolver);
|
||||
let points_to = points_to
|
||||
.into_iter()
|
||||
.map(|file_id| match roots.remove(&file_id) {
|
||||
|
@ -180,13 +181,24 @@ fn build_subtree(
|
|||
visited,
|
||||
roots,
|
||||
Some(link),
|
||||
file_id,
|
||||
ModuleSource::File(file_id),
|
||||
),
|
||||
})
|
||||
.collect::<Cancelable<Vec<_>>>()?;
|
||||
(points_to, problem)
|
||||
}
|
||||
Submodule::Definition(..) => continue,
|
||||
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;
|
||||
|
@ -196,10 +208,17 @@ fn build_subtree(
|
|||
}
|
||||
|
||||
fn resolve_submodule(
|
||||
file_id: FileId,
|
||||
source: ModuleSource,
|
||||
name: &SmolStr,
|
||||
file_resolver: &FileResolverImp,
|
||||
) -> (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 is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
|
||||
|
||||
|
|
|
@ -142,9 +142,7 @@ impl LinkId {
|
|||
.1;
|
||||
ast.into()
|
||||
}
|
||||
ModuleSourceNode::Inline(..) => {
|
||||
unimplemented!("https://github.com/rust-analyzer/rust-analyzer/issues/181")
|
||||
}
|
||||
ModuleSourceNode::Inline(it) => it,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +155,12 @@ struct ModuleData {
|
|||
}
|
||||
|
||||
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> {
|
||||
match self {
|
||||
ModuleSource::File(f) => Some(f),
|
||||
|
@ -164,7 +168,7 @@ impl ModuleSource {
|
|||
}
|
||||
}
|
||||
|
||||
fn file_id(self) -> FileId {
|
||||
pub(crate) fn file_id(self) -> FileId {
|
||||
match self {
|
||||
ModuleSource::File(f) => f,
|
||||
ModuleSource::Inline(ptr) => ptr.file_id(),
|
||||
|
|
|
@ -220,27 +220,32 @@ impl AnalysisImpl {
|
|||
let source_root = self.db.file_source_root(file_id);
|
||||
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 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
|
||||
.modules_for_source(ModuleSource::File(file_id))
|
||||
.modules_for_source(module_source)
|
||||
.into_iter()
|
||||
.filter_map(|module_id| {
|
||||
let link = module_id.parent_link(&module_tree)?;
|
||||
let file_id = match link.owner(&module_tree).source(&module_tree) {
|
||||
ModuleSource::File(file_id) => file_id,
|
||||
ModuleSource::Inline(..) => {
|
||||
//TODO: https://github.com/rust-analyzer/rust-analyzer/issues/181
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let file_id = link.owner(&module_tree).source(&module_tree).file_id();
|
||||
let decl = link.bind_source(&module_tree, &*self.db);
|
||||
let decl = decl.ast();
|
||||
|
||||
let decl_name = decl.name().unwrap();
|
||||
|
||||
let sym = FileSymbol {
|
||||
name: decl.name().unwrap().text(),
|
||||
node_range: decl.syntax().range(),
|
||||
name: decl_name.text(),
|
||||
node_range: decl_name.syntax().range(),
|
||||
kind: MODULE,
|
||||
};
|
||||
Some((file_id, sym))
|
||||
|
|
|
@ -263,8 +263,12 @@ impl Analysis {
|
|||
) -> Cancelable<Vec<(FileId, TextRange)>> {
|
||||
Ok(self.imp.find_all_refs(file_id, offset))
|
||||
}
|
||||
pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||
self.imp.parent_module(file_id)
|
||||
pub fn parent_module(
|
||||
&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>> {
|
||||
self.imp.crate_for(file_id)
|
||||
|
|
|
@ -92,9 +92,28 @@ fn test_resolve_parent_module() {
|
|||
<|>// empty
|
||||
",
|
||||
);
|
||||
let symbols = analysis.parent_module(pos.file_id).unwrap();
|
||||
let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -218,11 +218,13 @@ pub fn handle_goto_definition(
|
|||
|
||||
pub fn handle_parent_module(
|
||||
world: ServerWorld,
|
||||
params: TextDocumentIdentifier,
|
||||
params: req::TextDocumentPositionParams,
|
||||
) -> 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();
|
||||
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 location = to_location(file_id, symbol.node_range, &world, &line_index)?;
|
||||
res.push(location);
|
||||
|
|
|
@ -93,7 +93,7 @@ pub struct Decoration {
|
|||
pub enum ParentModule {}
|
||||
|
||||
impl Request for ParentModule {
|
||||
type Params = TextDocumentIdentifier;
|
||||
type Params = TextDocumentPositionParams;
|
||||
type Result = Vec<Location>;
|
||||
const METHOD: &'static str = "m/parentModule";
|
||||
}
|
||||
|
|
|
@ -6,10 +6,6 @@ import {
|
|||
SourceChange
|
||||
} from './apply_source_change';
|
||||
|
||||
interface OnEnterParams {
|
||||
textDocument: lc.TextDocumentIdentifier;
|
||||
position: lc.Position;
|
||||
}
|
||||
|
||||
export async function handle(event: { text: string }): Promise<boolean> {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
@ -20,7 +16,7 @@ export async function handle(event: { text: string }): Promise<boolean> {
|
|||
) {
|
||||
return false;
|
||||
}
|
||||
const request: OnEnterParams = {
|
||||
const request: lc.TextDocumentPositionParams = {
|
||||
textDocument: { uri: editor.document.uri.toString() },
|
||||
position: Server.client.code2ProtocolConverter.asPosition(
|
||||
editor.selection.active
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
import { Location, TextDocumentIdentifier } from 'vscode-languageclient';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import { Server } from '../server';
|
||||
|
||||
export async function handle() {
|
||||
|
@ -8,10 +8,13 @@ export async function handle() {
|
|||
if (editor == null || editor.document.languageId !== 'rust') {
|
||||
return;
|
||||
}
|
||||
const request: TextDocumentIdentifier = {
|
||||
uri: editor.document.uri.toString()
|
||||
const request: lc.TextDocumentPositionParams = {
|
||||
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',
|
||||
request
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue