Merge #6111
6111: Add assist for converting the base of integer literals. r=SomeoneToIgnore a=vlakreeh
This PR adds an assist similar to Intellij's [convert number to](https://i.imgur.com/JH6wstP.png). It also does a small refactor to [assists/src/tests.rs](fc34403018/crates/assists/src/tests.rs) to add the ability to specify the resolved assist for a specific action within an assist group.
## Demo

Co-authored-by: vlakreeh <zeb@zebulon.dev>
This commit is contained in:
701
crates/assists/src/handlers/convert_integer_literal.rs
Normal file
701
crates/assists/src/handlers/convert_integer_literal.rs
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
use syntax::{ast, AstNode, SmolStr};
|
||||||
|
|
||||||
|
use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
|
||||||
|
|
||||||
|
// Assist: convert_integer_literal
|
||||||
|
//
|
||||||
|
// Converts the base of integer literals to other bases.
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// const _: i32 = 10<|>;
|
||||||
|
// ```
|
||||||
|
// ->
|
||||||
|
// ```
|
||||||
|
// const _: i32 = 0b1010;
|
||||||
|
// ```
|
||||||
|
pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
|
let literal = ctx.find_node_at_offset::<ast::Literal>()?;
|
||||||
|
let range = literal.syntax().text_range();
|
||||||
|
let group_id = GroupLabel("Convert integer base".into());
|
||||||
|
|
||||||
|
let suffix = match literal.kind() {
|
||||||
|
ast::LiteralKind::IntNumber { suffix } => suffix,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let suffix_len = suffix.as_ref().map(|s| s.len()).unwrap_or(0);
|
||||||
|
let raw_literal_text = literal.syntax().to_string();
|
||||||
|
|
||||||
|
// Gets the literal's text without the type suffix and without underscores.
|
||||||
|
let literal_text = raw_literal_text
|
||||||
|
.chars()
|
||||||
|
.take(raw_literal_text.len() - suffix_len)
|
||||||
|
.filter(|c| *c != '_')
|
||||||
|
.collect::<SmolStr>();
|
||||||
|
let literal_base = IntegerLiteralBase::identify(&literal_text)?;
|
||||||
|
|
||||||
|
for base in IntegerLiteralBase::bases() {
|
||||||
|
if *base == literal_base {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut converted = literal_base.convert(&literal_text, base);
|
||||||
|
|
||||||
|
let label = if let Some(suffix) = &suffix {
|
||||||
|
format!("Convert {} ({}) to {}", &literal_text, suffix, &converted)
|
||||||
|
} else {
|
||||||
|
format!("Convert {} to {}", &literal_text, &converted)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Appends the type suffix back into the new literal if it exists.
|
||||||
|
if let Some(suffix) = &suffix {
|
||||||
|
converted.push_str(&suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.add_group(
|
||||||
|
&group_id,
|
||||||
|
AssistId("convert_integer_literal", AssistKind::RefactorInline),
|
||||||
|
label,
|
||||||
|
range,
|
||||||
|
|builder| builder.replace(range, converted),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum IntegerLiteralBase {
|
||||||
|
Binary,
|
||||||
|
Octal,
|
||||||
|
Decimal,
|
||||||
|
Hexadecimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntegerLiteralBase {
|
||||||
|
fn identify(literal_text: &str) -> Option<Self> {
|
||||||
|
// We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible.
|
||||||
|
if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) {
|
||||||
|
return Some(Self::Decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = match &literal_text[..2] {
|
||||||
|
"0b" => Self::Binary,
|
||||||
|
"0o" => Self::Octal,
|
||||||
|
"0x" => Self::Hexadecimal,
|
||||||
|
_ => Self::Decimal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks that all characters after the base prefix are all valid digits for that base.
|
||||||
|
if literal_text[base.prefix_len()..].chars().all(|c| c.is_digit(base.base())) {
|
||||||
|
Some(base)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(&self, literal_text: &str, to: &IntegerLiteralBase) -> String {
|
||||||
|
let digits = &literal_text[self.prefix_len()..];
|
||||||
|
let value = u128::from_str_radix(digits, self.base()).unwrap();
|
||||||
|
|
||||||
|
match to {
|
||||||
|
Self::Binary => format!("0b{:b}", value),
|
||||||
|
Self::Octal => format!("0o{:o}", value),
|
||||||
|
Self::Decimal => value.to_string(),
|
||||||
|
Self::Hexadecimal => format!("0x{:X}", value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn base(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Binary => 2,
|
||||||
|
Self::Octal => 8,
|
||||||
|
Self::Decimal => 10,
|
||||||
|
Self::Hexadecimal => 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn prefix_len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Decimal => 0,
|
||||||
|
_ => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn bases() -> &'static [IntegerLiteralBase] {
|
||||||
|
&[
|
||||||
|
IntegerLiteralBase::Binary,
|
||||||
|
IntegerLiteralBase::Octal,
|
||||||
|
IntegerLiteralBase::Decimal,
|
||||||
|
IntegerLiteralBase::Hexadecimal,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::tests::{check_assist_by_label, check_assist_target};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn binary_target() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0b1010<|>;", "0b1010");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn octal_target() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0o12<|>;", "0o12");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decimal_target() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 10<|>;", "10");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hexadecimal_target() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0xA<|>;", "0xA");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn binary_target_with_underscores() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10<|>;", "0b10_10");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn octal_target_with_underscores() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2<|>;", "0o1_2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decimal_target_with_underscores() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 1_0<|>;", "1_0");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hexadecimal_target_with_underscores() {
|
||||||
|
check_assist_target(convert_integer_literal, "const _: i32 = 0x_A<|>;", "0x_A");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_decimal_integer() {
|
||||||
|
let before = "const _: i32 = 1000<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1111101000;",
|
||||||
|
"Convert 1000 to 0b1111101000",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o1750;",
|
||||||
|
"Convert 1000 to 0o1750",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0x3E8;",
|
||||||
|
"Convert 1000 to 0x3E8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal numbers under 3 digits have a special case where they return early because we can't fit a
|
||||||
|
// other base's prefix, so we have a separate test for that.
|
||||||
|
#[test]
|
||||||
|
fn convert_small_decimal_integer() {
|
||||||
|
let before = "const _: i32 = 10<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1010;",
|
||||||
|
"Convert 10 to 0b1010",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o12;",
|
||||||
|
"Convert 10 to 0o12",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xA;",
|
||||||
|
"Convert 10 to 0xA",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_hexadecimal_integer() {
|
||||||
|
let before = "const _: i32 = 0xFF<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111;",
|
||||||
|
"Convert 0xFF to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377;",
|
||||||
|
"Convert 0xFF to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0xFF to 255",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_binary_integer() {
|
||||||
|
let before = "const _: i32 = 0b11111111<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377;",
|
||||||
|
"Convert 0b11111111 to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0b11111111 to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFF;",
|
||||||
|
"Convert 0b11111111 to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_octal_integer() {
|
||||||
|
let before = "const _: i32 = 0o377<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111;",
|
||||||
|
"Convert 0o377 to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0o377 to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFF;",
|
||||||
|
"Convert 0o377 to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_decimal_integer_with_underscores() {
|
||||||
|
let before = "const _: i32 = 1_00_0<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1111101000;",
|
||||||
|
"Convert 1000 to 0b1111101000",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o1750;",
|
||||||
|
"Convert 1000 to 0o1750",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0x3E8;",
|
||||||
|
"Convert 1000 to 0x3E8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_small_decimal_integer_with_underscores() {
|
||||||
|
let before = "const _: i32 = 1_0<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1010;",
|
||||||
|
"Convert 10 to 0b1010",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o12;",
|
||||||
|
"Convert 10 to 0o12",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xA;",
|
||||||
|
"Convert 10 to 0xA",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_hexadecimal_integer_with_underscores() {
|
||||||
|
let before = "const _: i32 = 0x_F_F<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111;",
|
||||||
|
"Convert 0xFF to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377;",
|
||||||
|
"Convert 0xFF to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0xFF to 255",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_binary_integer_with_underscores() {
|
||||||
|
let before = "const _: i32 = 0b1111_1111<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377;",
|
||||||
|
"Convert 0b11111111 to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0b11111111 to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFF;",
|
||||||
|
"Convert 0b11111111 to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_octal_integer_with_underscores() {
|
||||||
|
let before = "const _: i32 = 0o3_77<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111;",
|
||||||
|
"Convert 0o377 to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255;",
|
||||||
|
"Convert 0o377 to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFF;",
|
||||||
|
"Convert 0o377 to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_decimal_integer_with_suffix() {
|
||||||
|
let before = "const _: i32 = 1000i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1111101000i32;",
|
||||||
|
"Convert 1000 (i32) to 0b1111101000",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o1750i32;",
|
||||||
|
"Convert 1000 (i32) to 0o1750",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0x3E8i32;",
|
||||||
|
"Convert 1000 (i32) to 0x3E8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_small_decimal_integer_with_suffix() {
|
||||||
|
let before = "const _: i32 = 10i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1010i32;",
|
||||||
|
"Convert 10 (i32) to 0b1010",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o12i32;",
|
||||||
|
"Convert 10 (i32) to 0o12",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xAi32;",
|
||||||
|
"Convert 10 (i32) to 0xA",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_hexadecimal_integer_with_suffix() {
|
||||||
|
let before = "const _: i32 = 0xFFi32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111i32;",
|
||||||
|
"Convert 0xFF (i32) to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377i32;",
|
||||||
|
"Convert 0xFF (i32) to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0xFF (i32) to 255",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_binary_integer_with_suffix() {
|
||||||
|
let before = "const _: i32 = 0b11111111i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377i32;",
|
||||||
|
"Convert 0b11111111 (i32) to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0b11111111 (i32) to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFFi32;",
|
||||||
|
"Convert 0b11111111 (i32) to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_octal_integer_with_suffix() {
|
||||||
|
let before = "const _: i32 = 0o377i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111i32;",
|
||||||
|
"Convert 0o377 (i32) to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0o377 (i32) to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFFi32;",
|
||||||
|
"Convert 0o377 (i32) to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_decimal_integer_with_underscores_and_suffix() {
|
||||||
|
let before = "const _: i32 = 1_00_0i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1111101000i32;",
|
||||||
|
"Convert 1000 (i32) to 0b1111101000",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o1750i32;",
|
||||||
|
"Convert 1000 (i32) to 0o1750",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0x3E8i32;",
|
||||||
|
"Convert 1000 (i32) to 0x3E8",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_small_decimal_integer_with_underscores_and_suffix() {
|
||||||
|
let before = "const _: i32 = 1_0i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b1010i32;",
|
||||||
|
"Convert 10 (i32) to 0b1010",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o12i32;",
|
||||||
|
"Convert 10 (i32) to 0o12",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xAi32;",
|
||||||
|
"Convert 10 (i32) to 0xA",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_hexadecimal_integer_with_underscores_and_suffix() {
|
||||||
|
let before = "const _: i32 = 0x_F_Fi32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111i32;",
|
||||||
|
"Convert 0xFF (i32) to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377i32;",
|
||||||
|
"Convert 0xFF (i32) to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0xFF (i32) to 255",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_binary_integer_with_underscores_and_suffix() {
|
||||||
|
let before = "const _: i32 = 0b1111_1111i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0o377i32;",
|
||||||
|
"Convert 0b11111111 (i32) to 0o377",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0b11111111 (i32) to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFFi32;",
|
||||||
|
"Convert 0b11111111 (i32) to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert_octal_integer_with_underscores_and_suffix() {
|
||||||
|
let before = "const _: i32 = 0o3_77i32<|>;";
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0b11111111i32;",
|
||||||
|
"Convert 0o377 (i32) to 0b11111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 255i32;",
|
||||||
|
"Convert 0o377 (i32) to 255",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
convert_integer_literal,
|
||||||
|
before,
|
||||||
|
"const _: i32 = 0xFFi32;",
|
||||||
|
"Convert 0o377 (i32) to 0xFF",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,6 +128,7 @@ mod handlers {
|
|||||||
mod auto_import;
|
mod auto_import;
|
||||||
mod change_return_type_to_result;
|
mod change_return_type_to_result;
|
||||||
mod change_visibility;
|
mod change_visibility;
|
||||||
|
mod convert_integer_literal;
|
||||||
mod early_return;
|
mod early_return;
|
||||||
mod expand_glob_import;
|
mod expand_glob_import;
|
||||||
mod extract_struct_from_enum_variant;
|
mod extract_struct_from_enum_variant;
|
||||||
@@ -172,6 +173,7 @@ mod handlers {
|
|||||||
auto_import::auto_import,
|
auto_import::auto_import,
|
||||||
change_return_type_to_result::change_return_type_to_result,
|
change_return_type_to_result::change_return_type_to_result,
|
||||||
change_visibility::change_visibility,
|
change_visibility::change_visibility,
|
||||||
|
convert_integer_literal::convert_integer_literal,
|
||||||
early_return::convert_to_guarded_return,
|
early_return::convert_to_guarded_return,
|
||||||
expand_glob_import::expand_glob_import,
|
expand_glob_import::expand_glob_import,
|
||||||
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
|
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
|
||||||
|
|||||||
@@ -15,18 +15,30 @@ pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
|
|||||||
|
|
||||||
pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
|
pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||||
let ra_fixture_after = trim_indent(ra_fixture_after);
|
let ra_fixture_after = trim_indent(ra_fixture_after);
|
||||||
check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after));
|
check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no way to choose what assist within a group you want to test against,
|
||||||
|
// so this is here to allow you choose.
|
||||||
|
pub(crate) fn check_assist_by_label(
|
||||||
|
assist: Handler,
|
||||||
|
ra_fixture_before: &str,
|
||||||
|
ra_fixture_after: &str,
|
||||||
|
label: &str,
|
||||||
|
) {
|
||||||
|
let ra_fixture_after = trim_indent(ra_fixture_after);
|
||||||
|
check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), Some(label));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: instead of having a separate function here, maybe use
|
// FIXME: instead of having a separate function here, maybe use
|
||||||
// `extract_ranges` and mark the target as `<target> </target>` in the
|
// `extract_ranges` and mark the target as `<target> </target>` in the
|
||||||
// fixture?
|
// fixture?
|
||||||
pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
|
pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
|
||||||
check(assist, ra_fixture, ExpectedResult::Target(target));
|
check(assist, ra_fixture, ExpectedResult::Target(target), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
|
pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
|
||||||
check(assist, ra_fixture, ExpectedResult::NotApplicable);
|
check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_doc_test(assist_id: &str, before: &str, after: &str) {
|
fn check_doc_test(assist_id: &str, before: &str, after: &str) {
|
||||||
@@ -65,7 +77,7 @@ enum ExpectedResult<'a> {
|
|||||||
Target(&'a str),
|
Target(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check(handler: Handler, before: &str, expected: ExpectedResult) {
|
fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: Option<&str>) {
|
||||||
let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
|
let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
|
||||||
let text_without_caret = db.file_text(file_with_caret_id).to_string();
|
let text_without_caret = db.file_text(file_with_caret_id).to_string();
|
||||||
|
|
||||||
@@ -77,7 +89,12 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
|
|||||||
let mut acc = Assists::new_resolved(&ctx);
|
let mut acc = Assists::new_resolved(&ctx);
|
||||||
handler(&mut acc, &ctx);
|
handler(&mut acc, &ctx);
|
||||||
let mut res = acc.finish_resolved();
|
let mut res = acc.finish_resolved();
|
||||||
let assist = res.pop();
|
|
||||||
|
let assist = match assist_label {
|
||||||
|
Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label),
|
||||||
|
None => res.pop(),
|
||||||
|
};
|
||||||
|
|
||||||
match (assist, expected) {
|
match (assist, expected) {
|
||||||
(Some(assist), ExpectedResult::After(after)) => {
|
(Some(assist), ExpectedResult::After(after)) => {
|
||||||
let mut source_change = assist.source_change;
|
let mut source_change = assist.source_change;
|
||||||
|
|||||||
@@ -203,6 +203,19 @@ pub(crate) fn frobnicate() {}
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn doctest_convert_integer_literal() {
|
||||||
|
check_doc_test(
|
||||||
|
"convert_integer_literal",
|
||||||
|
r#####"
|
||||||
|
const _: i32 = 10<|>;
|
||||||
|
"#####,
|
||||||
|
r#####"
|
||||||
|
const _: i32 = 0b1010;
|
||||||
|
"#####,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn doctest_convert_to_guarded_return() {
|
fn doctest_convert_to_guarded_return() {
|
||||||
check_doc_test(
|
check_doc_test(
|
||||||
|
|||||||
Reference in New Issue
Block a user