ra_ap_rust_analyzer/cli/
parse.rs1use ide::Edition;
3use ide_db::line_index::LineIndex;
4use serde::Serialize;
5use syntax::{AstNode, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken};
6
7use crate::cli::{flags, read_stdin};
8
9#[derive(Serialize)]
10struct JsonNode {
11 kind: String,
12 #[serde(rename = "type")]
13 node_type: &'static str,
14 start: [u32; 3],
15 end: [u32; 3],
16 #[serde(skip_serializing_if = "Option::is_none")]
17 text: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 children: Option<Vec<JsonNode>>,
20}
21
22fn pos(line_index: &LineIndex, offset: syntax::TextSize) -> [u32; 3] {
23 let offset_u32 = u32::from(offset);
24 let line_col = line_index.line_col(offset);
25 [offset_u32, line_col.line, line_col.col]
26}
27
28impl flags::Parse {
29 pub fn run(self) -> anyhow::Result<()> {
30 let _p = tracing::info_span!("flags::Parse::run").entered();
31 let text = read_stdin()?;
32 let line_index = LineIndex::new(&text);
33 let file = SourceFile::parse(&text, Edition::CURRENT).tree();
34
35 if !self.no_dump {
36 if self.json {
37 let json_tree = node_to_json(NodeOrToken::Node(file.syntax().clone()), &line_index);
38 println!("{}", serde_json::to_string(&json_tree)?);
39 } else {
40 println!("{:#?}", file.syntax());
41 }
42 }
43
44 std::mem::forget(file);
45 Ok(())
46 }
47}
48
49fn node_to_json(node: NodeOrToken<SyntaxNode, SyntaxToken>, line_index: &LineIndex) -> JsonNode {
50 let range = node.text_range();
51 let kind = format!("{:?}", node.kind());
52
53 match node {
54 NodeOrToken::Node(n) => {
55 let children: Vec<_> =
56 n.children_with_tokens().map(|it| node_to_json(it, line_index)).collect();
57 JsonNode {
58 kind,
59 node_type: "Node",
60 start: pos(line_index, range.start()),
61 end: pos(line_index, range.end()),
62 text: None,
63 children: Some(children),
64 }
65 }
66 NodeOrToken::Token(t) => JsonNode {
67 kind,
68 node_type: "Token",
69 start: pos(line_index, range.start()),
70 end: pos(line_index, range.end()),
71 text: Some(t.text().to_owned()),
72 children: None,
73 },
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::cli::flags;
81
82 #[test]
83 fn test_parse_json_output() {
84 let text = "fn main() {}".to_owned();
85 let flags = flags::Parse { json: true, no_dump: false };
86 let line_index = LineIndex::new(&text);
87
88 let file = SourceFile::parse(&text, Edition::CURRENT).tree();
89
90 let output = if flags.json {
91 let json_tree = node_to_json(NodeOrToken::Node(file.syntax().clone()), &line_index);
92 serde_json::to_string(&json_tree).unwrap()
93 } else {
94 format!("{:#?}", file.syntax())
95 };
96
97 assert!(output.contains(r#""kind":"SOURCE_FILE""#));
98 assert!(output.contains(r#""text":"main""#));
99 assert!(output.contains(r#""start":[0,0,0]"#));
100 }
101}