1use collections::HashMap;
2use convert_case::{Case, Casing};
3use std::{ops::Range, sync::LazyLock};
4use tree_sitter::{Query, QueryMatch};
5
6use crate::MigrationPatterns;
7use crate::patterns::KEYMAP_ACTION_ARRAY_ARGUMENT_AS_OBJECT_PATTERN;
8
9pub const KEYMAP_PATTERNS: MigrationPatterns = &[(
10 KEYMAP_ACTION_ARRAY_ARGUMENT_AS_OBJECT_PATTERN,
11 action_argument_snake_case,
12)];
13
14fn to_snake_case(text: &str) -> String {
15 text.to_case(Case::Snake)
16}
17
18fn action_argument_snake_case(
19 contents: &str,
20 mat: &QueryMatch,
21 query: &Query,
22) -> Option<(Range<usize>, String)> {
23 let array_ix = query.capture_index_for_name("array")?;
24 let action_name_ix = query.capture_index_for_name("action_name")?;
25 let argument_key_ix = query.capture_index_for_name("argument_key")?;
26 let argument_value_ix = query.capture_index_for_name("argument_value")?;
27 let action_name = contents.get(
28 mat.nodes_for_capture_index(action_name_ix)
29 .next()?
30 .byte_range(),
31 )?;
32
33 let replacement_key = ACTION_ARGUMENT_SNAKE_CASE_REPLACE.get(action_name)?;
34 let argument_key = contents.get(
35 mat.nodes_for_capture_index(argument_key_ix)
36 .next()?
37 .byte_range(),
38 )?;
39
40 if argument_key != *replacement_key {
41 return None;
42 }
43
44 let argument_value_node = mat.nodes_for_capture_index(argument_value_ix).next()?;
45 let argument_value = contents.get(argument_value_node.byte_range())?;
46
47 let new_key = to_snake_case(argument_key);
48 let new_value = if argument_value_node.kind() == "string" {
49 format!("\"{}\"", to_snake_case(argument_value.trim_matches('"')))
50 } else {
51 argument_value.to_string()
52 };
53
54 let range_to_replace = mat.nodes_for_capture_index(array_ix).next()?.byte_range();
55 let replacement = format!(
56 "[\"{}\", {{ \"{}\": {} }}]",
57 action_name, new_key, new_value
58 );
59
60 Some((range_to_replace, replacement))
61}
62
63static ACTION_ARGUMENT_SNAKE_CASE_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
64 HashMap::from_iter([
65 ("vim::NextWordStart", "ignorePunctuation"),
66 ("vim::NextWordEnd", "ignorePunctuation"),
67 ("vim::PreviousWordStart", "ignorePunctuation"),
68 ("vim::PreviousWordEnd", "ignorePunctuation"),
69 ("vim::MoveToNext", "partialWord"),
70 ("vim::MoveToPrev", "partialWord"),
71 ("vim::Down", "displayLines"),
72 ("vim::Up", "displayLines"),
73 ("vim::EndOfLine", "displayLines"),
74 ("vim::StartOfLine", "displayLines"),
75 ("vim::FirstNonWhitespace", "displayLines"),
76 ("pane::CloseActiveItem", "saveIntent"),
77 ("vim::Paste", "preserveClipboard"),
78 ("vim::Word", "ignorePunctuation"),
79 ("vim::Subword", "ignorePunctuation"),
80 ("vim::IndentObj", "includeBelow"),
81 ])
82});