From e6e1695084ae6b2101fe8a106e424cca7e6c1171 Mon Sep 17 00:00:00 2001 From: GarandPLG Date: Mon, 1 Dec 2025 00:43:23 +0100 Subject: [PATCH] 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. --- src/main.rs | 6 +-- src/nix.rs | 144 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 90 insertions(+), 60 deletions(-) diff --git a/src/main.rs b/src/main.rs index 696b5bc..1de2e75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,8 @@ fn main() { eprintln!(); } - let options: Vec<(String, bool)> = nix::collect_nix_options_with_path(&ast.syntax(), ""); - for (path, value) in options { - println!("{} = {};", path, value); + let options: Vec<(String, String, bool)> = nix::collect_nix_options(&ast.syntax()); + for (category, path, value) in options { + println!("{}: {} = {};", category, path, value); } } diff --git a/src/nix.rs b/src/nix.rs index cfb3f79..fd650ba 100644 --- a/src/nix.rs +++ b/src/nix.rs @@ -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)> { - let mut result: Vec<(String, bool)> = Vec::new(); +pub fn collect_nix_options(node: &SyntaxNode) -> Vec<(String, String, bool)> { + collect_nix_options_with_path(node, "", "") +} - for child in node.children_with_tokens() { - if let NodeOrToken::Node(child_node) = child { - match child_node.kind() { - SyntaxKind::NODE_ATTRPATH_VALUE => { - let mut attr_path: String = String::new(); - let mut value_node: Option = None; +fn collect_nix_options_with_path( + node: &SyntaxNode, + current_path: &str, + current_category: &str, +) -> Vec<(String, String, bool)> { + 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() { - 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) - }; + let children: Vec> = node.children_with_tokens().collect(); - result.extend(collect_nix_options_with_path( - &grand_node, - &new_path, - )); - } - _ => {} - } - } - } - - 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((full_path, bool_value)); - } - } - - _ => { - result.extend(collect_nix_options_with_path(&child_node, current_path)); + for i in 0..children.len() { + match &children[i] { + 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(); + category = content; } } + + NodeOrToken::Node(child_node) + if child_node.kind() == SyntaxKind::NODE_ATTRPATH_VALUE => + { + let mut attr_path: String = String::new(); + let mut value_node: Option = 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, + )); + } + + _ => {} } }