Skip to main content

ide_assists/handlers/
convert_char_literal.rs

1use syntax::{AstToken, ast};
2
3use crate::{AssistContext, AssistId, Assists, GroupLabel};
4
5// Assist: convert_char_literal
6//
7// Converts character literals between different representations. Currently supports normal character -> ASCII / Unicode escape.
8// ```
9// const _: char = 'a'$0;
10// ```
11// ->
12// ```
13// const _: char = '\x61';
14// ```
15pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
16    if !ctx.has_empty_selection() {
17        return None;
18    }
19
20    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
21    let literal = match literal.kind() {
22        ast::LiteralKind::Char(it) => it,
23        _ => return None,
24    };
25
26    let value = literal.value().ok()?;
27    let text = literal.syntax().text().to_owned();
28    let range = literal.syntax().text_range();
29    let group_id = GroupLabel("Convert char representation".into());
30
31    let mut add_assist = |converted: String| {
32        // Skip no-op assists (e.g. `'const C: char = '\\x61';'` already matches the ASCII form).
33        if converted == text {
34            return;
35        }
36        let label = format!("Convert {text} to {converted}");
37        acc.add_group(
38            &group_id,
39            AssistId::refactor_rewrite("convert_char_literal"),
40            label,
41            range,
42            |builder| builder.replace(range, converted),
43        );
44    };
45
46    if value.is_ascii() {
47        add_assist(format!("'\\x{:02x}'", value as u32));
48    }
49
50    add_assist(format!("'\\u{{{:x}}}'", value as u32));
51
52    Some(())
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::tests::check_assist_by_label;
58
59    use super::convert_char_literal;
60
61    #[test]
62    fn ascii_char_to_ascii_and_unicode() {
63        let before = "const _: char = 'a'$0;";
64        check_assist_by_label(
65            convert_char_literal,
66            before,
67            "const _: char = '\\x61';",
68            "Convert 'a' to '\\x61'",
69        );
70        check_assist_by_label(
71            convert_char_literal,
72            before,
73            "const _: char = '\\u{61}';",
74            "Convert 'a' to '\\u{61}'",
75        );
76    }
77
78    #[test]
79    fn non_ascii_char_only_unicode() {
80        check_assist_by_label(
81            convert_char_literal,
82            "const _: char = '😀'$0;",
83            "const _: char = '\\u{1f600}';",
84            "Convert '😀' to '\\u{1f600}'",
85        );
86    }
87
88    #[test]
89    fn ascii_escape_can_convert_to_unicode() {
90        check_assist_by_label(
91            convert_char_literal,
92            "const _: char = '\\x61'$0;",
93            "const _: char = '\\u{61}';",
94            "Convert '\\x61' to '\\u{61}'",
95        );
96    }
97}