1use jsona::{
4 dom::{node::DomNode, Keys, Node},
5 rowan::{Direction, TextSize, TokenAtOffset, WalkEvent},
6 syntax::{SyntaxKind, SyntaxNode, SyntaxToken},
7};
8
9#[derive(Debug)]
10pub struct Query {
11 pub offset: TextSize,
13 pub before: Option<PositionInfo>,
15 pub after: Option<PositionInfo>,
17 pub scope: ScopeKind,
19 pub node_at_offset: TextSize,
21 pub key: Option<SyntaxToken>,
23 pub value: Option<SyntaxNode>,
25 pub add_value: bool,
27}
28
29impl Query {
30 pub fn at(root: &Node, offset: TextSize, is_completion: bool) -> Self {
31 let syntax = root.node_syntax().cloned().unwrap().into_node().unwrap();
32 let before = offset
33 .checked_sub(TextSize::from(1))
34 .and_then(|offset| Self::position_info_at(&syntax, offset));
35 let after = if offset >= syntax.text_range().end() {
36 None
37 } else {
38 Self::position_info_at(&syntax, offset)
39 };
40
41 let mut kind = ScopeKind::Unknown;
42 let mut node_at_offset = offset;
43 let mut key = None;
44 let mut value = None;
45 let mut add_value = true;
46 if let Some(token) = before
47 .as_ref()
48 .and_then(|t| Query::prev_none_ws_comment(t.syntax.clone()))
49 {
50 let mut fallback = || {
51 if let Some(node) = token.parent_ancestors().find(|v| {
52 matches!(
53 v.kind(),
54 SyntaxKind::KEY
55 | SyntaxKind::SCALAR
56 | SyntaxKind::OBJECT
57 | SyntaxKind::ARRAY
58 )
59 }) {
60 node_at_offset = node.text_range().start();
61 match &node.kind() {
62 SyntaxKind::KEY => {
63 key = node
64 .children_with_tokens()
65 .find(|v| v.kind().is_key())
66 .and_then(|v| v.as_token().cloned());
67 add_value = !node
68 .siblings_with_tokens(Direction::Next)
69 .any(|v| v.kind() == SyntaxKind::COLON);
70 if is_completion {
71 if let Some(offset) = node
72 .parent()
73 .and_then(|v| v.parent())
74 .and_then(|v| v.text_range().start().checked_add(1.into()))
75 {
76 node_at_offset = offset;
77 }
78 }
79 kind = ScopeKind::PropertyKey;
80 }
81 SyntaxKind::SCALAR => {
82 kind = ScopeKind::Value;
83 value = Some(node);
84 }
85 SyntaxKind::OBJECT => kind = ScopeKind::Object,
86 SyntaxKind::ARRAY => kind = ScopeKind::Array,
87 _ => {}
88 };
89 }
90 };
91 match token.kind() {
92 SyntaxKind::ANNOTATION_KEY => {
93 let exist_value = token
94 .siblings_with_tokens(Direction::Next)
95 .any(|v| v.kind() == SyntaxKind::ANNOTATION_VALUE);
96 if !exist_value && !token.text_range().contains_inclusive(offset) {
97 fallback()
99 } else {
100 add_value = !exist_value;
101 kind = ScopeKind::AnnotationKey;
102 key = Some(token);
103 }
104 }
105 SyntaxKind::COLON => {
106 node_at_offset = token.text_range().start();
107 kind = ScopeKind::Value;
108 value = token.next_sibling_or_token().and_then(|v| {
109 if v.kind() == SyntaxKind::VALUE {
110 v.as_node()
111 .unwrap()
112 .children()
113 .find(|v| v.kind() == SyntaxKind::SCALAR)
114 } else {
115 None
116 }
117 });
118 }
119 SyntaxKind::PARENTHESES_START => {
120 kind = ScopeKind::Value;
121 value = token.next_sibling_or_token().and_then(|v| {
122 if v.kind() == SyntaxKind::ANNOTATION_VALUE {
123 v.as_node()
124 .unwrap()
125 .children()
126 .find(|v| v.kind() == SyntaxKind::VALUE)
127 .and_then(|v| v.children().find(|v| v.kind() == SyntaxKind::SCALAR))
128 } else {
129 None
130 }
131 });
132 }
133 SyntaxKind::BRACE_START => {
134 kind = ScopeKind::Object;
135 }
136 SyntaxKind::BRACKET_START => {
137 kind = ScopeKind::Array;
138 }
139 SyntaxKind::BRACE_END | SyntaxKind::BRACKET_END => {}
140 _ => fallback(),
141 };
142 }
143
144 Query {
145 offset,
146 before,
147 after,
148 scope: kind,
149 node_at_offset,
150 add_value,
151 key,
152 value,
153 }
154 }
155
156 pub fn node_at(root: &Node, offset: TextSize) -> Option<(Keys, Node)> {
157 if !root
158 .node_text_range()
159 .map(|v| v.contains(offset))
160 .unwrap_or_default()
161 {
162 return None;
163 }
164 node_at_impl(root, offset, Keys::default())
165 }
166
167 pub fn index_at(&self) -> Option<usize> {
168 self.before
169 .as_ref()
170 .and_then(|v| {
171 v.syntax
172 .parent_ancestors()
173 .find(|v| v.kind() == SyntaxKind::ARRAY)
174 })
175 .map(|v| {
176 let mut index = 0;
177 for child in v.children() {
178 if child.kind() == SyntaxKind::VALUE {
179 index += 1;
180 if child.text_range().contains(self.offset) {
181 break;
182 }
183 }
184 }
185 index
186 })
187 }
188
189 pub fn space_and_comma(&self) -> (&'static str, &'static str) {
190 let mut add_comma = true;
191 let mut add_space = false;
192 if self.scope != ScopeKind::AnnotationKey {
193 if let Some(token) = self.before.as_ref().map(|v| &v.syntax) {
194 if let Some(parent) = token.parent_ancestors().find(|v| {
195 matches!(
196 v.kind(),
197 SyntaxKind::ANNOTATION_VALUE | SyntaxKind::OBJECT | SyntaxKind::ARRAY
198 )
199 }) {
200 let mut found = false;
201 let mut exist_new_line = false;
202 let mut prev_token = None;
203 let mut next_token = None;
204 for event in parent.preorder_with_tokens() {
205 if let WalkEvent::Enter(ele) = event {
206 if let Some(t) = ele.as_token() {
207 if t.text().contains(|p| p == '\r' || p == '\n') {
208 exist_new_line = true;
209 }
210 if found && !t.kind().is_ws_or_comment() {
211 next_token = Some(t.clone());
212 break;
213 }
214 if !found {
215 if t.text_range().contains_inclusive(self.offset) {
216 found = true;
217 } else {
218 prev_token = Some(t.clone())
219 }
220 }
221 }
222 }
223 }
224
225 if let Some(t) = next_token {
226 if matches!(
227 t.kind(),
228 SyntaxKind::COMMA
229 | SyntaxKind::BRACE_END
230 | SyntaxKind::BRACKET_END
231 | SyntaxKind::PARENTHESES_END
232 ) {
233 add_comma = false;
234 }
235 }
236 if exist_new_line {
237 match self.scope {
238 ScopeKind::Array => {}
239 ScopeKind::Value => {
240 if !(self
241 .before
242 .as_ref()
243 .map(|v| v.syntax.kind().is_ws_or_comment())
244 .unwrap_or_default()
245 || prev_token
246 .map(|v| v.kind().is_ws_or_comment())
247 .unwrap_or_default())
248 {
249 add_space = true
250 }
251 }
252 _ => {
253 add_space = true;
254 }
255 }
256 }
257 }
258 }
259 }
260 let space = if add_space { " " } else { "" };
261 let comma = if add_comma { "," } else { "" };
262 (space, comma)
263 }
264
265 fn position_info_at(syntax: &SyntaxNode, offset: TextSize) -> Option<PositionInfo> {
266 let syntax = match syntax.token_at_offset(offset) {
267 TokenAtOffset::None => return None,
268 TokenAtOffset::Single(s) => s,
269 TokenAtOffset::Between(_, right) => right,
270 };
271
272 Some(PositionInfo { syntax })
273 }
274
275 fn prev_none_ws_comment(token: SyntaxToken) -> Option<SyntaxToken> {
276 if token.kind().is_ws_or_comment() {
277 token.prev_token().and_then(Query::prev_none_ws_comment)
278 } else {
279 Some(token)
280 }
281 }
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum ScopeKind {
286 Unknown,
287 Object,
288 Array,
289 AnnotationKey,
290 PropertyKey,
291 Value,
292}
293
294#[derive(Debug, Clone)]
295pub struct PositionInfo {
296 pub syntax: SyntaxToken,
298}
299
300fn node_at_impl(node: &Node, offset: TextSize, keys: Keys) -> Option<(Keys, Node)> {
301 if let Some(annotations) = node.annotations() {
302 let map = annotations.value().read();
303 for (key, value) in map.iter() {
304 if map
305 .syntax(key)
306 .map(|v| v.text_range().contains(offset))
307 .unwrap_or_default()
308 {
309 return node_at_impl(value, offset, keys.join(key.clone()));
310 }
311 }
312 }
313 match node {
314 Node::Array(arr) => {
315 for (index, value) in arr.value().read().iter().enumerate() {
316 if value
317 .node_syntax()
318 .map(|v| v.text_range().contains(offset))
319 .unwrap_or_default()
320 {
321 return node_at_impl(value, offset, keys.join(index));
322 }
323 }
324 }
325 Node::Object(obj) => {
326 let map = obj.value().read();
327 for (key, value) in map.iter() {
328 if map
329 .syntax(key)
330 .map(|v| v.text_range().contains(offset))
331 .unwrap_or_default()
332 {
333 if value.syntax().is_none()
334 && key
335 .syntax()
336 .map(|v| v.text_range().contains(offset))
337 .unwrap_or_default()
338 {
339 return Some((keys, node.clone()));
340 }
341 return node_at_impl(value, offset, keys.join(key.clone()));
342 }
343 }
344 }
345 _ => {}
346 }
347 Some((keys, node.clone()))
348}