mirror of
https://github.com/oxalica/nil.git
synced 2024-11-22 02:55:39 +03:00
parent
2091ef0e1d
commit
dbf49b3aab
@ -1,12 +1,15 @@
|
||||
use crate::def::{BindingValue, Expr, ExprId};
|
||||
use crate::def::{AstPtr, NameId};
|
||||
use crate::{DefDatabase, FileId, Module, ModuleSourceMap, NameKind};
|
||||
use smol_str::SmolStr;
|
||||
use syntax::ast::{self, AstNode};
|
||||
use syntax::rowan::WalkEvent;
|
||||
use syntax::{SyntaxNode, TextRange};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SymbolTree {
|
||||
pub name: SmolStr,
|
||||
// TODO: Avoid saving `NameId` in the public API.
|
||||
name_id: NameId,
|
||||
pub full_range: TextRange,
|
||||
pub focus_range: TextRange,
|
||||
pub kind: NameKind,
|
||||
@ -17,81 +20,119 @@ pub(crate) fn symbol_hierarchy(db: &dyn DefDatabase, file: FileId) -> Vec<Symbol
|
||||
let parse = db.parse(file);
|
||||
let module = db.module(file);
|
||||
let source_map = db.source_map(file);
|
||||
let mut symbols = Vec::new();
|
||||
let mut collector = Collector {
|
||||
module: &module,
|
||||
source_map: &source_map,
|
||||
root_node: parse.syntax_node(),
|
||||
symbols: Vec::new(),
|
||||
symbols: &mut symbols,
|
||||
};
|
||||
collector.collect_expr(module.entry_expr());
|
||||
let mut symbols = collector.symbols;
|
||||
sort_symbols(&mut symbols);
|
||||
collector.collect_node(&parse.syntax_node());
|
||||
symbols
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Collector<'a> {
|
||||
struct Collector<'a, 'b> {
|
||||
module: &'a Module,
|
||||
source_map: &'a ModuleSourceMap,
|
||||
root_node: SyntaxNode,
|
||||
symbols: Vec<SymbolTree>,
|
||||
symbols: &'b mut Vec<SymbolTree>,
|
||||
}
|
||||
|
||||
impl Collector<'_> {
|
||||
fn collect_expr(&mut self, expr_id: ExprId) {
|
||||
let e = &self.module[expr_id];
|
||||
let (bindings, body) = match e {
|
||||
Expr::LetIn(bindings, body) => (bindings, Some(*body)),
|
||||
Expr::Attrset(bindings) | Expr::LetAttrset(bindings) | Expr::RecAttrset(bindings) => {
|
||||
(bindings, None)
|
||||
}
|
||||
_ => return e.walk_child_exprs(|child| self.collect_expr(child)),
|
||||
};
|
||||
impl Collector<'_, '_> {
|
||||
fn push_symbol(&mut self, name_id: NameId, attr: &ast::Attr, full_range: TextRange) {
|
||||
self.symbols.push(SymbolTree {
|
||||
name: self.module[name_id].text.clone(),
|
||||
name_id,
|
||||
full_range,
|
||||
focus_range: attr.syntax().text_range(),
|
||||
kind: self.module[name_id].kind,
|
||||
children: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
for &(name, rhs) in bindings.statics.iter() {
|
||||
let prev_len = self.symbols.len();
|
||||
match rhs {
|
||||
BindingValue::InheritFrom(_) => {}
|
||||
BindingValue::Inherit(child) | BindingValue::Expr(child) => {
|
||||
self.collect_expr(child);
|
||||
// TODO: Rewrite this in non-recursive form?
|
||||
// Nested mutable borrowing is hard to impl without recursion yet.
|
||||
fn collect_node(&mut self, n: &SyntaxNode) {
|
||||
let mut iter = n.preorder();
|
||||
let mut last_is_path_value = false;
|
||||
while let Some(event) = iter.next() {
|
||||
let n = match event {
|
||||
WalkEvent::Enter(n) => n,
|
||||
WalkEvent::Leave(n) => {
|
||||
last_is_path_value = ast::AttrpathValue::can_cast(n.kind());
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let Some(binding) = ast::Binding::cast(n) else {
|
||||
continue;
|
||||
};
|
||||
match binding {
|
||||
ast::Binding::Inherit(i) => {
|
||||
for attr in i.attrs() {
|
||||
let ptr = AstPtr::new(attr.syntax());
|
||||
if let Some(name_id) = self.source_map.name_for_node(ptr) {
|
||||
self.push_symbol(name_id, &attr, i.syntax().text_range());
|
||||
}
|
||||
}
|
||||
// Continue traversing the from-expr. Attrs should be skipped automatically.
|
||||
}
|
||||
ast::Binding::AttrpathValue(path_value) => {
|
||||
let Some(path) = path_value.attrpath() else {
|
||||
continue;
|
||||
};
|
||||
iter.skip_subtree();
|
||||
self.collect_path_value(path_value, path.attrs(), last_is_path_value);
|
||||
}
|
||||
}
|
||||
(|| {
|
||||
let text = self.module[name].text.clone();
|
||||
let kind = self.module[name].kind;
|
||||
let name_node = self.source_map.nodes_for_name(name).next()?;
|
||||
let focus_range = name_node.text_range();
|
||||
let full_range = name_node
|
||||
.to_node(&self.root_node)
|
||||
.ancestors()
|
||||
.find_map(ast::Binding::cast)?
|
||||
.syntax()
|
||||
.text_range();
|
||||
let mut children = self.symbols.split_off(prev_len);
|
||||
sort_symbols(&mut children);
|
||||
self.symbols.push(SymbolTree {
|
||||
name: text,
|
||||
full_range,
|
||||
focus_range,
|
||||
kind,
|
||||
children,
|
||||
});
|
||||
Some(())
|
||||
})();
|
||||
}
|
||||
|
||||
bindings
|
||||
.dynamics
|
||||
.iter()
|
||||
.flat_map(|&(lhs, rhs)| [lhs, rhs])
|
||||
.chain(bindings.inherit_froms.iter().copied())
|
||||
.chain(body)
|
||||
.for_each(|e| self.collect_expr(e));
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_symbols(syms: &mut [SymbolTree]) {
|
||||
syms.sort_by_key(|sym| sym.full_range.start());
|
||||
fn collect_path_value(
|
||||
&mut self,
|
||||
binding: ast::AttrpathValue,
|
||||
mut attrs: ast::AstChildren<ast::Attr>,
|
||||
allow_merge_to_last: bool,
|
||||
) {
|
||||
let Some(attr) = attrs.next() else {
|
||||
if let Some(n) = binding.value() {
|
||||
self.collect_node(n.syntax());
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let ptr = AstPtr::new(attr.syntax());
|
||||
if let Some(name_id) = self.source_map.name_for_node(ptr) {
|
||||
let binding_end_pos = binding.syntax().text_range().end();
|
||||
|
||||
// Merge adjacent bindings to the same tree node.
|
||||
// Eg. `{ a.b = 1; a.c = 2; }`
|
||||
// ^-------a-------^
|
||||
let current_sym = if allow_merge_to_last
|
||||
&& self.symbols.last().map(|tree| tree.name_id) == Some(name_id)
|
||||
{
|
||||
let last_sym = self.symbols.last_mut().unwrap();
|
||||
last_sym.full_range = last_sym.full_range.cover_offset(binding_end_pos);
|
||||
last_sym
|
||||
} else {
|
||||
// `{ foo.bar = 1; }`
|
||||
// --- focus
|
||||
// ======== full
|
||||
let full_range = attr.syntax().text_range().cover_offset(binding_end_pos);
|
||||
self.push_symbol(name_id, &attr, full_range);
|
||||
self.symbols.last_mut().unwrap()
|
||||
};
|
||||
|
||||
Collector {
|
||||
module: self.module,
|
||||
source_map: self.source_map,
|
||||
symbols: &mut current_sym.children,
|
||||
}
|
||||
.collect_path_value(binding, attrs, allow_merge_to_last);
|
||||
} else {
|
||||
// Incomplete or dynamic attributes.
|
||||
self.collect_node(attr.syntax());
|
||||
self.collect_path_value(binding, attrs, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -139,7 +180,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attrset() {
|
||||
fn attrset_simple() {
|
||||
check(
|
||||
"{ a = 1; b = { c = 1; }; inherit d; inherit ({ e = 1; }) e; }",
|
||||
expect![[r#"
|
||||
@ -152,4 +193,32 @@ mod tests {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attrset_merge() {
|
||||
check(
|
||||
"{
|
||||
a.x.y = 1;
|
||||
# comment
|
||||
a.y.z = 2;
|
||||
another = 42;
|
||||
a.z = 3;
|
||||
inherit inhrit;
|
||||
a.w = 4;
|
||||
}",
|
||||
expect![[r#"
|
||||
a: PlainAttrset
|
||||
x: PlainAttrset
|
||||
y: PlainAttrset
|
||||
y: PlainAttrset
|
||||
z: PlainAttrset
|
||||
another: PlainAttrset
|
||||
a: PlainAttrset
|
||||
z: PlainAttrset
|
||||
inhrit: PlainAttrset
|
||||
a: PlainAttrset
|
||||
w: PlainAttrset
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user