1use super::*;
2
3#[derive(Debug, PartialEq)]
4pub struct Setting {
5 pub kind: SettingKind,
6 pub name: TextNode,
7 pub range: lsp::Range,
8}
9
10impl Setting {
11 #[must_use]
12 pub fn from_node(node: &Node, document: &Document) -> Option<Self> {
13 let range = node.get_range(document);
14
15 let name_node = node.child(1)?;
16
17 let name = TextNode {
18 value: document.get_node_text(&name_node),
19 range: name_node.get_range(document),
20 };
21
22 let mut cursor = node.walk();
23
24 let right_children = node
25 .children_by_field_name("right", &mut cursor)
26 .collect::<Vec<_>>();
27
28 let has_bracket = right_children.iter().any(|child| child.kind() == "[");
29
30 let boolean_child = right_children
31 .iter()
32 .find(|child| child.kind() == "boolean");
33
34 let string_child =
35 right_children.iter().find(|child| child.kind() == "string");
36
37 let expression_child = right_children
38 .iter()
39 .find(|child| child.kind() == "expression");
40
41 let kind = if has_bracket {
42 SettingKind::Array
43 } else if let Some(boolean) = boolean_child {
44 SettingKind::Boolean(document.get_node_text(boolean) == "true")
45 } else if string_child.is_some() || expression_child.is_some() {
46 SettingKind::String
47 } else if right_children.is_empty() {
48 SettingKind::Boolean(true)
49 } else {
50 return None;
51 };
52
53 Some(Setting { kind, name, range })
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use {super::*, indoc::indoc, pretty_assertions::assert_eq};
60
61 #[test]
62 fn parse_boolean_with_value() {
63 assert_eq!(
64 Document::from("set foo := true\n").settings(),
65 vec![Setting {
66 name: TextNode {
67 value: "foo".into(),
68 range: lsp::Range::at(0, 4, 0, 7),
69 },
70 kind: SettingKind::Boolean(true),
71 range: lsp::Range::at(0, 0, 1, 0),
72 }],
73 );
74 }
75
76 #[test]
77 fn parse_boolean_false() {
78 assert_eq!(
79 Document::from("set foo := false\n").settings(),
80 vec![Setting {
81 name: TextNode {
82 value: "foo".into(),
83 range: lsp::Range::at(0, 4, 0, 7),
84 },
85 kind: SettingKind::Boolean(false),
86 range: lsp::Range::at(0, 0, 1, 0),
87 }],
88 );
89 }
90
91 #[test]
92 fn parse_boolean_without_value() {
93 assert_eq!(
94 Document::from("set export\n").settings(),
95 vec![Setting {
96 name: TextNode {
97 value: "export".into(),
98 range: lsp::Range::at(0, 4, 0, 10),
99 },
100 kind: SettingKind::Boolean(true),
101 range: lsp::Range::at(0, 0, 1, 0),
102 }],
103 );
104 }
105
106 #[test]
107 fn parse_array() {
108 assert_eq!(
109 Document::from("set shell := [\"zsh\", \"-cu\"]\n").settings(),
110 vec![Setting {
111 name: TextNode {
112 value: "shell".into(),
113 range: lsp::Range::at(0, 4, 0, 9),
114 },
115 kind: SettingKind::Array,
116 range: lsp::Range::at(0, 0, 1, 0),
117 }],
118 );
119 }
120
121 #[test]
122 fn parse_string() {
123 assert_eq!(
124 Document::from("set foo := \"bar\"\n").settings(),
125 vec![Setting {
126 name: TextNode {
127 value: "foo".into(),
128 range: lsp::Range::at(0, 4, 0, 7),
129 },
130 kind: SettingKind::String,
131 range: lsp::Range::at(0, 0, 1, 0),
132 }],
133 );
134 }
135
136 #[test]
137 fn parse_string_containing_walrus() {
138 assert_eq!(
139 Document::from("set foo := \"bar := baz\"\n").settings(),
140 vec![Setting {
141 name: TextNode {
142 value: "foo".into(),
143 range: lsp::Range::at(0, 4, 0, 7),
144 },
145 kind: SettingKind::String,
146 range: lsp::Range::at(0, 0, 1, 0),
147 }],
148 );
149 }
150
151 #[test]
152 fn parse_expression() {
153 assert_eq!(
154 Document::from("set foo := \"bar\" / baz\n").settings(),
155 vec![Setting {
156 name: TextNode {
157 value: "foo".into(),
158 range: lsp::Range::at(0, 4, 0, 7),
159 },
160 kind: SettingKind::String,
161 range: lsp::Range::at(0, 0, 1, 0),
162 }],
163 );
164 }
165
166 #[test]
167 fn parse_multiple_settings() {
168 assert_eq!(
169 Document::from(indoc! {"
170 set foo := true
171 set bar := \"baz\"
172 set shell := [\"zsh\", \"-cu\"]
173 "})
174 .settings(),
175 vec![
176 Setting {
177 name: TextNode {
178 value: "foo".into(),
179 range: lsp::Range::at(0, 4, 0, 7),
180 },
181 kind: SettingKind::Boolean(true),
182 range: lsp::Range::at(0, 0, 1, 0),
183 },
184 Setting {
185 name: TextNode {
186 value: "bar".into(),
187 range: lsp::Range::at(1, 4, 1, 7),
188 },
189 kind: SettingKind::String,
190 range: lsp::Range::at(1, 0, 2, 0),
191 },
192 Setting {
193 name: TextNode {
194 value: "shell".into(),
195 range: lsp::Range::at(2, 4, 2, 9),
196 },
197 kind: SettingKind::Array,
198 range: lsp::Range::at(2, 0, 3, 0),
199 },
200 ],
201 );
202 }
203}