1use syntax::{AstToken, ast, ast::Radix};
2
3use crate::{AssistContext, AssistId, Assists, GroupLabel};
4
5pub(crate) fn convert_integer_literal(
17 acc: &mut Assists,
18 ctx: &AssistContext<'_, '_>,
19) -> Option<()> {
20 if !ctx.has_empty_selection() {
21 return None;
22 }
23 let literal = ctx.find_node_at_offset::<ast::Literal>()?;
24 let literal = match literal.kind() {
25 ast::LiteralKind::IntNumber(it) => it,
26 _ => return None,
27 };
28 let radix = literal.radix();
29 let value = literal.value().ok()?;
30 let suffix = literal.suffix();
31
32 let range = literal.syntax().text_range();
33 let group_id = GroupLabel("Convert integer base".into());
34
35 for &target_radix in Radix::ALL {
36 if target_radix == radix {
37 continue;
38 }
39
40 let mut converted = match target_radix {
41 Radix::Binary => format!("0b{value:b}"),
42 Radix::Octal => format!("0o{value:o}"),
43 Radix::Decimal => value.to_string(),
44 Radix::Hexadecimal => format!("0x{value:X}"),
45 };
46
47 if let Some(suffix) = suffix {
49 converted.push_str(suffix);
50 }
51
52 let label = format!("Convert {literal} to {converted}");
53
54 acc.add_group(
55 &group_id,
56 AssistId::refactor_rewrite("convert_integer_literal"),
57 label,
58 range,
59 |builder| builder.replace(range, converted),
60 );
61 }
62
63 Some(())
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
69
70 use super::*;
71
72 #[test]
73 fn binary_target() {
74 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010$0;", "0b1010");
75 }
76
77 #[test]
78 fn octal_target() {
79 check_assist_target(convert_integer_literal, "const _: i32 = 0o12$0;", "0o12");
80 }
81
82 #[test]
83 fn decimal_target() {
84 check_assist_target(convert_integer_literal, "const _: i32 = 10$0;", "10");
85 }
86
87 #[test]
88 fn hexadecimal_target() {
89 check_assist_target(convert_integer_literal, "const _: i32 = 0xA$0;", "0xA");
90 }
91
92 #[test]
93 fn binary_target_with_underscores() {
94 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10$0;", "0b10_10");
95 }
96
97 #[test]
98 fn octal_target_with_underscores() {
99 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2$0;", "0o1_2");
100 }
101
102 #[test]
103 fn decimal_target_with_underscores() {
104 check_assist_target(convert_integer_literal, "const _: i32 = 1_0$0;", "1_0");
105 }
106
107 #[test]
108 fn hexadecimal_target_with_underscores() {
109 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A$0;", "0x_A");
110 }
111
112 #[test]
113 fn convert_decimal_integer() {
114 let before = "const _: i32 = 1000$0;";
115
116 check_assist_by_label(
117 convert_integer_literal,
118 before,
119 "const _: i32 = 0b1111101000;",
120 "Convert 1000 to 0b1111101000",
121 );
122
123 check_assist_by_label(
124 convert_integer_literal,
125 before,
126 "const _: i32 = 0o1750;",
127 "Convert 1000 to 0o1750",
128 );
129
130 check_assist_by_label(
131 convert_integer_literal,
132 before,
133 "const _: i32 = 0x3E8;",
134 "Convert 1000 to 0x3E8",
135 );
136 }
137
138 #[test]
139 fn convert_hexadecimal_integer() {
140 let before = "const _: i32 = 0xFF$0;";
141
142 check_assist_by_label(
143 convert_integer_literal,
144 before,
145 "const _: i32 = 0b11111111;",
146 "Convert 0xFF to 0b11111111",
147 );
148
149 check_assist_by_label(
150 convert_integer_literal,
151 before,
152 "const _: i32 = 0o377;",
153 "Convert 0xFF to 0o377",
154 );
155
156 check_assist_by_label(
157 convert_integer_literal,
158 before,
159 "const _: i32 = 255;",
160 "Convert 0xFF to 255",
161 );
162 }
163
164 #[test]
165 fn convert_binary_integer() {
166 let before = "const _: i32 = 0b11111111$0;";
167
168 check_assist_by_label(
169 convert_integer_literal,
170 before,
171 "const _: i32 = 0o377;",
172 "Convert 0b11111111 to 0o377",
173 );
174
175 check_assist_by_label(
176 convert_integer_literal,
177 before,
178 "const _: i32 = 255;",
179 "Convert 0b11111111 to 255",
180 );
181
182 check_assist_by_label(
183 convert_integer_literal,
184 before,
185 "const _: i32 = 0xFF;",
186 "Convert 0b11111111 to 0xFF",
187 );
188 }
189
190 #[test]
191 fn convert_octal_integer() {
192 let before = "const _: i32 = 0o377$0;";
193
194 check_assist_by_label(
195 convert_integer_literal,
196 before,
197 "const _: i32 = 0b11111111;",
198 "Convert 0o377 to 0b11111111",
199 );
200
201 check_assist_by_label(
202 convert_integer_literal,
203 before,
204 "const _: i32 = 255;",
205 "Convert 0o377 to 255",
206 );
207
208 check_assist_by_label(
209 convert_integer_literal,
210 before,
211 "const _: i32 = 0xFF;",
212 "Convert 0o377 to 0xFF",
213 );
214 }
215
216 #[test]
217 fn convert_integer_with_underscores() {
218 let before = "const _: i32 = 1_00_0$0;";
219
220 check_assist_by_label(
221 convert_integer_literal,
222 before,
223 "const _: i32 = 0b1111101000;",
224 "Convert 1_00_0 to 0b1111101000",
225 );
226
227 check_assist_by_label(
228 convert_integer_literal,
229 before,
230 "const _: i32 = 0o1750;",
231 "Convert 1_00_0 to 0o1750",
232 );
233
234 check_assist_by_label(
235 convert_integer_literal,
236 before,
237 "const _: i32 = 0x3E8;",
238 "Convert 1_00_0 to 0x3E8",
239 );
240 }
241
242 #[test]
243 fn convert_integer_with_suffix() {
244 let before = "const _: i32 = 1000i32$0;";
245
246 check_assist_by_label(
247 convert_integer_literal,
248 before,
249 "const _: i32 = 0b1111101000i32;",
250 "Convert 1000i32 to 0b1111101000i32",
251 );
252
253 check_assist_by_label(
254 convert_integer_literal,
255 before,
256 "const _: i32 = 0o1750i32;",
257 "Convert 1000i32 to 0o1750i32",
258 );
259
260 check_assist_by_label(
261 convert_integer_literal,
262 before,
263 "const _: i32 = 0x3E8i32;",
264 "Convert 1000i32 to 0x3E8i32",
265 );
266 }
267
268 #[test]
269 fn convert_overflowing_literal() {
270 let before = "const _: i32 =
271 111111111111111111111111111111111111111111111111111111111111111111111111$0;";
272 check_assist_not_applicable(convert_integer_literal, before);
273 }
274
275 #[test]
276 fn convert_non_empty_selection_literal() {
277 check_assist_not_applicable(convert_integer_literal, "const _: i32 = $00b1010$0;");
278 }
279}