Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-assists/src/handlers/convert_char_literal.rs97
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs13
3 files changed, 112 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/convert_char_literal.rs b/crates/ide-assists/src/handlers/convert_char_literal.rs
new file mode 100644
index 0000000000..0a50ba86ba
--- /dev/null
+++ b/crates/ide-assists/src/handlers/convert_char_literal.rs
@@ -0,0 +1,97 @@
+use syntax::{AstToken, ast};
+
+use crate::{AssistContext, AssistId, Assists, GroupLabel};
+
+// Assist: convert_char_literal
+//
+// Converts character literals between different representations. Currently supports normal character -> ASCII / Unicode escape.
+// ```
+// const _: char = 'a'$0;
+// ```
+// ->
+// ```
+// const _: char = '\x61';
+// ```
+pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ if !ctx.has_empty_selection() {
+ return None;
+ }
+
+ let literal = ctx.find_node_at_offset::<ast::Literal>()?;
+ let literal = match literal.kind() {
+ ast::LiteralKind::Char(it) => it,
+ _ => return None,
+ };
+
+ let value = literal.value().ok()?;
+ let text = literal.syntax().text().to_owned();
+ let range = literal.syntax().text_range();
+ let group_id = GroupLabel("Convert char representation".into());
+
+ let mut add_assist = |converted: String| {
+ // Skip no-op assists (e.g. `'const C: char = '\\x61';'` already matches the ASCII form).
+ if converted == text {
+ return;
+ }
+ let label = format!("Convert {text} to {converted}");
+ acc.add_group(
+ &group_id,
+ AssistId::refactor_rewrite("convert_char_literal"),
+ label,
+ range,
+ |builder| builder.replace(range, converted),
+ );
+ };
+
+ if value.is_ascii() {
+ add_assist(format!("'\\x{:02x}'", value as u32));
+ }
+
+ add_assist(format!("'\\u{{{:x}}}'", value as u32));
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_assist_by_label;
+
+ use super::convert_char_literal;
+
+ #[test]
+ fn ascii_char_to_ascii_and_unicode() {
+ let before = "const _: char = 'a'$0;";
+ check_assist_by_label(
+ convert_char_literal,
+ before,
+ "const _: char = '\\x61';",
+ "Convert 'a' to '\\x61'",
+ );
+ check_assist_by_label(
+ convert_char_literal,
+ before,
+ "const _: char = '\\u{61}';",
+ "Convert 'a' to '\\u{61}'",
+ );
+ }
+
+ #[test]
+ fn non_ascii_char_only_unicode() {
+ check_assist_by_label(
+ convert_char_literal,
+ "const _: char = '😀'$0;",
+ "const _: char = '\\u{1f600}';",
+ "Convert '😀' to '\\u{1f600}'",
+ );
+ }
+
+ #[test]
+ fn ascii_escape_can_convert_to_unicode() {
+ check_assist_by_label(
+ convert_char_literal,
+ "const _: char = '\\x61'$0;",
+ "const _: char = '\\u{61}';",
+ "Convert '\\x61' to '\\u{61}'",
+ );
+ }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 4b4aa94279..ca468905fb 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -119,6 +119,7 @@ mod handlers {
mod change_visibility;
mod convert_bool_then;
mod convert_bool_to_enum;
+ mod convert_char_literal;
mod convert_closure_to_fn;
mod convert_comment_block;
mod convert_comment_from_or_to_doc;
@@ -256,6 +257,7 @@ mod handlers {
convert_bool_then::convert_bool_then_to_if,
convert_bool_then::convert_if_to_bool_then,
convert_bool_to_enum::convert_bool_to_enum,
+ convert_char_literal::convert_char_literal,
convert_closure_to_fn::convert_closure_to_fn,
convert_comment_block::convert_comment_block,
convert_comment_from_or_to_doc::convert_comment_from_or_to_doc,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 160b31af0a..7eef257b95 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -425,6 +425,19 @@ fn main() {
}
#[test]
+fn doctest_convert_char_literal() {
+ check_doc_test(
+ "convert_char_literal",
+ r#####"
+const _: char = 'a'$0;
+"#####,
+ r#####"
+const _: char = '\x61';
+"#####,
+ )
+}
+
+#[test]
fn doctest_convert_closure_to_fn() {
check_doc_test(
"convert_closure_to_fn",