diff options
-rw-r--r-- | crates/assists/src/handlers/convert_integer_literal.rs | 23 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 63 |
2 files changed, 38 insertions, 48 deletions
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs index 6d477c045..5957834d3 100644 --- a/crates/assists/src/handlers/convert_integer_literal.rs +++ b/crates/assists/src/handlers/convert_integer_literal.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, ast::Radix, AstNode}; | 1 | use syntax::{ast, ast::Radix, AstToken}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; |
4 | 4 | ||
@@ -14,15 +14,13 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | |||
14 | // const _: i32 = 0b1010; | 14 | // const _: i32 = 0b1010; |
15 | // ``` | 15 | // ``` |
16 | pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 16 | pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
17 | let literal = ctx.find_node_at_offset::<ast::Literal>()?; | 17 | let literal = ctx.find_node_at_offset::<ast::Literal>()?.as_int_number()?; |
18 | let (radix, value) = literal.as_int_number()?.value()?; | 18 | let radix = literal.radix(); |
19 | let value = literal.value()?; | ||
20 | let suffix = literal.suffix(); | ||
19 | 21 | ||
20 | let range = literal.syntax().text_range(); | 22 | let range = literal.syntax().text_range(); |
21 | let group_id = GroupLabel("Convert integer base".into()); | 23 | let group_id = GroupLabel("Convert integer base".into()); |
22 | let suffix = match literal.kind() { | ||
23 | ast::LiteralKind::IntNumber { suffix } => suffix, | ||
24 | _ => return None, | ||
25 | }; | ||
26 | 24 | ||
27 | for &target_radix in Radix::ALL { | 25 | for &target_radix in Radix::ALL { |
28 | if target_radix == radix { | 26 | if target_radix == radix { |
@@ -36,16 +34,11 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> | |||
36 | Radix::Hexadecimal => format!("0x{:X}", value), | 34 | Radix::Hexadecimal => format!("0x{:X}", value), |
37 | }; | 35 | }; |
38 | 36 | ||
39 | let label = format!( | 37 | let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default()); |
40 | "Convert {} to {}{}", | ||
41 | literal, | ||
42 | converted, | ||
43 | suffix.as_deref().unwrap_or_default() | ||
44 | ); | ||
45 | 38 | ||
46 | // Appends the type suffix back into the new literal if it exists. | 39 | // Appends the type suffix back into the new literal if it exists. |
47 | if let Some(suffix) = &suffix { | 40 | if let Some(suffix) = suffix { |
48 | converted.push_str(&suffix); | 41 | converted.push_str(suffix); |
49 | } | 42 | } |
50 | 43 | ||
51 | acc.add_group( | 44 | acc.add_group( |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 5623799b4..8d3fad5a6 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -166,6 +166,7 @@ impl HasStringValue for ast::String { | |||
166 | } | 166 | } |
167 | } | 167 | } |
168 | 168 | ||
169 | // FIXME: merge `ast::RawString` and `ast::String`. | ||
169 | impl HasStringValue for ast::RawString { | 170 | impl HasStringValue for ast::RawString { |
170 | fn value(&self) -> Option<Cow<'_, str>> { | 171 | fn value(&self) -> Option<Cow<'_, str>> { |
171 | let text = self.text().as_str(); | 172 | let text = self.text().as_str(); |
@@ -544,29 +545,46 @@ impl ast::IntNumber { | |||
544 | "i8", "i16", "i32", "i64", "i128", "isize", | 545 | "i8", "i16", "i32", "i64", "i128", "isize", |
545 | ]; | 546 | ]; |
546 | 547 | ||
547 | // FIXME: should probably introduce string token type? | 548 | pub fn radix(&self) -> Radix { |
548 | // https://github.com/rust-analyzer/rust-analyzer/issues/6308 | 549 | match self.text().get(..2).unwrap_or_default() { |
549 | pub fn value(&self) -> Option<(Radix, u128)> { | 550 | "0b" => Radix::Binary, |
551 | "0o" => Radix::Octal, | ||
552 | "0x" => Radix::Hexadecimal, | ||
553 | _ => Radix::Decimal, | ||
554 | } | ||
555 | } | ||
556 | |||
557 | pub fn value(&self) -> Option<u128> { | ||
550 | let token = self.syntax(); | 558 | let token = self.syntax(); |
551 | 559 | ||
552 | let mut text = token.text().as_str(); | 560 | let mut text = token.text().as_str(); |
553 | for suffix in ast::IntNumber::SUFFIXES { | 561 | if let Some(suffix) = self.suffix() { |
554 | if let Some(without_suffix) = text.strip_suffix(suffix) { | 562 | text = &text[..text.len() - suffix.len()] |
555 | text = without_suffix; | ||
556 | break; | ||
557 | } | ||
558 | } | 563 | } |
559 | 564 | ||
565 | let radix = self.radix(); | ||
566 | text = &text[radix.prefix_len()..]; | ||
567 | |||
560 | let buf; | 568 | let buf; |
561 | if text.contains("_") { | 569 | if text.contains("_") { |
562 | buf = text.replace('_', ""); | 570 | buf = text.replace('_', ""); |
563 | text = buf.as_str(); | 571 | text = buf.as_str(); |
564 | }; | 572 | }; |
565 | 573 | ||
566 | let radix = Radix::identify(text)?; | 574 | let value = u128::from_str_radix(text, radix as u32).ok()?; |
567 | let digits = &text[radix.prefix_len()..]; | 575 | Some(value) |
568 | let value = u128::from_str_radix(digits, radix as u32).ok()?; | 576 | } |
569 | Some((radix, value)) | 577 | |
578 | pub fn suffix(&self) -> Option<&str> { | ||
579 | let text = self.text(); | ||
580 | // FIXME: don't check a fixed set of suffixes, `1_0_1___lol` is valid | ||
581 | // syntax, suffix is `lol`. | ||
582 | ast::IntNumber::SUFFIXES.iter().find_map(|suffix| { | ||
583 | if text.ends_with(suffix) { | ||
584 | return Some(&text[text.len() - suffix.len()..]); | ||
585 | } | ||
586 | None | ||
587 | }) | ||
570 | } | 588 | } |
571 | } | 589 | } |
572 | 590 | ||
@@ -586,27 +604,6 @@ impl Radix { | |||
586 | pub const ALL: &'static [Radix] = | 604 | pub const ALL: &'static [Radix] = |
587 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | 605 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; |
588 | 606 | ||
589 | fn identify(literal_text: &str) -> Option<Self> { | ||
590 | // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible. | ||
591 | if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) { | ||
592 | return Some(Self::Decimal); | ||
593 | } | ||
594 | |||
595 | let res = match &literal_text[..2] { | ||
596 | "0b" => Radix::Binary, | ||
597 | "0o" => Radix::Octal, | ||
598 | "0x" => Radix::Hexadecimal, | ||
599 | _ => Radix::Decimal, | ||
600 | }; | ||
601 | |||
602 | // Checks that all characters after the base prefix are all valid digits for that base. | ||
603 | if literal_text[res.prefix_len()..].chars().all(|c| c.is_digit(res as u32)) { | ||
604 | Some(res) | ||
605 | } else { | ||
606 | None | ||
607 | } | ||
608 | } | ||
609 | |||
610 | const fn prefix_len(&self) -> usize { | 607 | const fn prefix_len(&self) -> usize { |
611 | match self { | 608 | match self { |
612 | Self::Decimal => 0, | 609 | Self::Decimal => 0, |