1
1
mirror of https://github.com/oxalica/nil.git synced 2024-11-22 02:55:39 +03:00

Generate symbol tree syntactically without invalid nesting

Fixes #107
This commit is contained in:
oxalica 2023-10-09 08:56:12 +08:00
parent 2091ef0e1d
commit dbf49b3aab

View File

@ -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
"#]],
);
}
}