use crate::types::*;
use super::node_text;
pub fn extract(tree: &tree_sitter::Tree, source: &[u8]) -> (Vec<Symbol>, Vec<Import>) {
let root = tree.root_node();
let mut symbols = Vec::new();
let mut cursor = root.walk();
for child in root.children(&mut cursor) {
if child.kind() == "object" {
extract_object_keys(&child, source, &mut symbols, true);
}
}
(symbols, Vec::new())
}
fn extract_object_keys(
node: &tree_sitter::Node,
source: &[u8],
symbols: &mut Vec<Symbol>,
top_level: bool,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "pair" {
let key_node = child.child_by_field_name("key");
let value_node = child.child_by_field_name("value");
if let Some(key) = key_node {
let key_text = node_text(&key, source);
let name = key_text.trim_matches('"').to_string();
let is_object_value = value_node
.map(|v| v.kind() == "object")
.unwrap_or(false);
if top_level {
if is_object_value {
let mut children = Vec::new();
if let Some(val) = value_node {
extract_object_keys(&val, source, &mut children, false);
}
symbols.push(Symbol {
name,
kind: SymbolKind::Module,
span: Span::from_node(&child),
signature: String::new(),
doc_comment: None,
parent: None,
children,
});
} else {
let value_text = value_node
.map(|v| node_text(&v, source).to_string())
.unwrap_or_default();
symbols.push(Symbol {
name,
kind: SymbolKind::Const,
span: Span::from_node(&child),
signature: value_text,
doc_comment: None,
parent: None,
children: Vec::new(),
});
}
} else if is_object_value {
symbols.push(Symbol {
name,
kind: SymbolKind::Module,
span: Span::from_node(&child),
signature: String::new(),
doc_comment: None,
parent: None,
children: Vec::new(),
});
} else {
let value_text = value_node
.map(|v| node_text(&v, source).to_string())
.unwrap_or_default();
symbols.push(Symbol {
name,
kind: SymbolKind::Const,
span: Span::from_node(&child),
signature: value_text,
doc_comment: None,
parent: None,
children: Vec::new(),
});
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_json(source: &str) -> (Vec<Symbol>, Vec<Import>) {
let mut parser = tree_sitter::Parser::new();
parser.set_language(&tree_sitter_json::LANGUAGE.into()).unwrap();
let tree = parser.parse(source, None).unwrap();
extract(&tree, source.as_bytes())
}
#[test]
fn test_json_extraction() {
let source = r#"{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"build": "cargo build",
"test": "cargo test"
},
"dependencies": {
"serde": {
"version": "1.0"
}
}
}"#;
let (symbols, imports) = parse_json(source);
assert!(imports.is_empty());
let consts: Vec<_> = symbols.iter().filter(|s| s.kind == SymbolKind::Const).collect();
assert!(consts.iter().any(|s| s.name == "name"));
assert!(consts.iter().any(|s| s.name == "version"));
let modules: Vec<_> = symbols.iter().filter(|s| s.kind == SymbolKind::Module).collect();
assert!(modules.iter().any(|s| s.name == "scripts"));
assert!(modules.iter().any(|s| s.name == "dependencies"));
let scripts = symbols.iter().find(|s| s.name == "scripts").unwrap();
assert_eq!(scripts.children.len(), 2);
assert!(scripts.children.iter().any(|c| c.name == "build"));
assert!(scripts.children.iter().any(|c| c.name == "test"));
let deps = symbols.iter().find(|s| s.name == "dependencies").unwrap();
assert_eq!(deps.children.len(), 1);
assert_eq!(deps.children[0].name, "serde");
assert_eq!(deps.children[0].kind, SymbolKind::Module);
}
}