Add support for category comments in Nix option collection

The nix module now parses `/* */` comments as categories and associates
them with subsequent options. The output format has been updated to
include the category prefix before the option path. The internal state
is maintained during traversal to ensure correct category propagation
through nested structures.
This commit is contained in:
2025-12-01 00:43:23 +01:00
parent cb35269308
commit e6e1695084
2 changed files with 90 additions and 60 deletions

View File

@@ -17,8 +17,8 @@ fn main() {
eprintln!(); eprintln!();
} }
let options: Vec<(String, bool)> = nix::collect_nix_options_with_path(&ast.syntax(), ""); let options: Vec<(String, String, bool)> = nix::collect_nix_options(&ast.syntax());
for (path, value) in options { for (category, path, value) in options {
println!("{} = {};", path, value); println!("{}: {} = {};", category, path, value);
} }
} }

View File

@@ -1,66 +1,96 @@
use rnix::{NodeOrToken, SyntaxKind, SyntaxNode}; use rnix::{NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken};
pub fn collect_nix_options_with_path(node: &SyntaxNode, current_path: &str) -> Vec<(String, bool)> { pub fn collect_nix_options(node: &SyntaxNode) -> Vec<(String, String, bool)> {
let mut result: Vec<(String, bool)> = Vec::new(); collect_nix_options_with_path(node, "", "")
}
for child in node.children_with_tokens() { fn collect_nix_options_with_path(
if let NodeOrToken::Node(child_node) = child { node: &SyntaxNode,
match child_node.kind() { current_path: &str,
SyntaxKind::NODE_ATTRPATH_VALUE => { current_category: &str,
let mut attr_path: String = String::new(); ) -> Vec<(String, String, bool)> {
let mut value_node: Option<SyntaxNode> = None; let mut result: Vec<(String, String, bool)> = Vec::new();
let mut category: String = current_category.to_string();
for grand in child_node.children_with_tokens() { let children: Vec<NodeOrToken<SyntaxNode, SyntaxToken>> = node.children_with_tokens().collect();
if let NodeOrToken::Node(grand_node) = grand {
match grand_node.kind() {
SyntaxKind::NODE_ATTRPATH => {
attr_path = grand_node.text().to_string();
}
SyntaxKind::NODE_IDENT | SyntaxKind::NODE_LITERAL => {
let text: String = grand_node.text().to_string();
if text == "true" || text == "false" {
value_node = Some(grand_node);
}
}
SyntaxKind::NODE_ATTR_SET => {
let new_path: String = if current_path.is_empty() {
attr_path.clone()
} else {
format!("{}.{}", current_path, attr_path)
};
result.extend(collect_nix_options_with_path( for i in 0..children.len() {
&grand_node, match &children[i] {
&new_path, NodeOrToken::Token(token) if token.kind() == SyntaxKind::TOKEN_COMMENT => {
)); let text: &str = token.text();
} if text.starts_with("/*") && text.ends_with("*/") {
_ => {} let content: String = text
} .trim_start_matches("/*")
} .trim_end_matches("*/")
} .trim()
.to_string();
if let Some(value_node) = value_node { category = content;
let full_path: String = if current_path.is_empty() {
attr_path
} else {
format!("{}.{}", current_path, attr_path)
};
let value_text: String = value_node.text().to_string();
let mut bool_value: bool = false;
if value_text == "true" {
bool_value = true;
}
result.push((full_path, bool_value));
}
}
_ => {
result.extend(collect_nix_options_with_path(&child_node, current_path));
} }
} }
NodeOrToken::Node(child_node)
if child_node.kind() == SyntaxKind::NODE_ATTRPATH_VALUE =>
{
let mut attr_path: String = String::new();
let mut value_node: Option<SyntaxNode> = None;
for grand in child_node.children_with_tokens() {
if let NodeOrToken::Node(grand_node) = grand {
match grand_node.kind() {
SyntaxKind::NODE_ATTRPATH => {
attr_path = grand_node.text().to_string();
}
SyntaxKind::NODE_IDENT | SyntaxKind::NODE_LITERAL => {
let text: String = grand_node.text().to_string();
if text == "true" || text == "false" {
value_node = Some(grand_node);
}
}
SyntaxKind::NODE_ATTR_SET => {
let new_path: String = if current_path.is_empty() {
attr_path.clone()
} else {
format!("{}.{}", current_path, attr_path)
};
result.extend(collect_nix_options_with_path(
&grand_node,
&new_path,
&category,
));
}
_ => {}
}
}
}
if let Some(value_node) = value_node {
let full_path: String = if current_path.is_empty() {
attr_path
} else {
format!("{}.{}", current_path, attr_path)
};
let value_text: String = value_node.text().to_string();
let mut bool_value: bool = false;
if value_text == "true" {
bool_value = true;
}
result.push((category.clone(), full_path, bool_value));
}
}
NodeOrToken::Node(child_node) => {
result.extend(collect_nix_options_with_path(
child_node,
current_path,
&category,
));
}
_ => {}
} }
} }