1use std::ops::Range;
2use tree_sitter::{Query, QueryMatch};
3
4use crate::MigrationPatterns;
5
6pub const SETTINGS_PATTERNS: MigrationPatterns =
7 &[(FORMATTER_PATTERN, migrate_code_action_formatters)];
8
9const FORMATTER_PATTERN: &str = r#"
10 (object
11 (pair
12 key: (string (string_content) @formatter) (#any-of? @formatter "formatter" "format_on_save")
13 value: [
14 (array
15 (object
16 (pair
17 key: (string (string_content) @code-actions-key) (#eq? @code-actions-key "code_actions")
18 value: (object
19 ((pair) @code-action ","?)*
20 )
21 )
22 ) @code-actions-obj
23 ) @formatter-array
24 (object
25 (pair
26 key: (string (string_content) @code-actions-key) (#eq? @code-actions-key "code_actions")
27 value: (object
28 ((pair) @code-action ","?)*
29 )
30 )
31 ) @code-actions-obj
32 ]
33 )
34 )
35"#;
36
37pub fn migrate_code_action_formatters(
38 contents: &str,
39 mat: &QueryMatch,
40 query: &Query,
41) -> Option<(Range<usize>, String)> {
42 let code_actions_obj_ix = query.capture_index_for_name("code-actions-obj")?;
43 let code_actions_obj_node = mat.nodes_for_capture_index(code_actions_obj_ix).next()?;
44
45 let mut code_actions = vec![];
46
47 let code_actions_ix = query.capture_index_for_name("code-action")?;
48 for code_action_node in mat.nodes_for_capture_index(code_actions_ix) {
49 let Some(enabled) = code_action_node
50 .child_by_field_name("value")
51 .map(|n| n.kind() != "false")
52 else {
53 continue;
54 };
55 if !enabled {
56 continue;
57 }
58 let Some(name) = code_action_node
59 .child_by_field_name("key")
60 .and_then(|n| n.child(1))
61 .map(|n| &contents[n.byte_range()])
62 else {
63 continue;
64 };
65 code_actions.push(name);
66 }
67
68 let indent = query
69 .capture_index_for_name("formatter")
70 .and_then(|ix| mat.nodes_for_capture_index(ix).next())
71 .map(|node| node.start_position().column + 1)
72 .unwrap_or(2);
73
74 let mut code_actions_str = code_actions
75 .into_iter()
76 .map(|code_action| format!(r#"{{ "code_action": "{}" }}"#, code_action))
77 .collect::<Vec<_>>()
78 .join(&format!(",\n{}", " ".repeat(indent)));
79 let is_array = query
80 .capture_index_for_name("formatter-array")
81 .map(|ix| mat.nodes_for_capture_index(ix).count() > 0)
82 .unwrap_or(false);
83 if !is_array {
84 code_actions_str.insert_str(0, &" ".repeat(indent));
85 code_actions_str.insert_str(0, "[\n");
86 code_actions_str.push('\n');
87 code_actions_str.push_str(&" ".repeat(indent.saturating_sub(2)));
88 code_actions_str.push_str("]");
89 }
90 let mut replace_range = code_actions_obj_node.byte_range();
91 if is_array && code_actions_str.is_empty() {
92 let mut cursor = code_actions_obj_node.parent().unwrap().walk();
93 cursor.goto_first_child();
94 while cursor.node().id() != code_actions_obj_node.id() && cursor.goto_next_sibling() {}
95 while cursor.goto_next_sibling()
96 && (cursor.node().is_extra()
97 || cursor.node().is_missing()
98 || cursor.node().kind() == "comment")
99 {}
100 if cursor.node().kind() == "," {
101 // found comma, delete up to next node
102 while cursor.goto_next_sibling()
103 && (cursor.node().is_extra() || cursor.node().is_missing())
104 {}
105 replace_range.end = cursor.node().range().start_byte;
106 }
107 }
108 Some((replace_range, code_actions_str))
109}