about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-assists/src/handlers/move_format_string_arg.rs6
-rw-r--r--crates/ide-db/src/syntax_helpers/format_string_exprs.rs60
2 files changed, 36 insertions, 30 deletions
diff --git a/crates/ide-assists/src/handlers/move_format_string_arg.rs b/crates/ide-assists/src/handlers/move_format_string_arg.rs
index aa710d2ce65..11db6ae7f7b 100644
--- a/crates/ide-assists/src/handlers/move_format_string_arg.rs
+++ b/crates/ide-assists/src/handlers/move_format_string_arg.rs
@@ -92,7 +92,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
                         NodeOrToken::Node(n) => {
                             format_to!(current_arg, "{n}");
                         },
-                        NodeOrToken::Token(t) if t.kind() == COMMA=> {
+                        NodeOrToken::Token(t) if t.kind() == COMMA => {
                             existing_args.push(current_arg.trim().into());
                             current_arg.clear();
                         },
@@ -238,14 +238,14 @@ fn main() {
             &add_macro_decl(
                 r#"
 fn main() {
-    print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
+    print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
 }
 "#,
             ),
             &add_macro_decl(
                 r#"
 fn main() {
-    print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
+    print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
 }
 "#,
             ),
diff --git a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index ac6c6e8feee..313346ee131 100644
--- a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -104,6 +104,11 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
                 extracted_expressions.push(Arg::Placeholder);
                 state = State::NotArg;
             }
+            (State::MaybeArg, ':') => {
+                output.push(chr);
+                extracted_expressions.push(Arg::Placeholder);
+                state = State::FormatOpts;
+            }
             (State::MaybeArg, _) => {
                 if matches!(chr, '\\' | '$') {
                     current_expr.push('\\');
@@ -118,44 +123,41 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
                     state = State::Expr;
                 }
             }
-            (State::Ident | State::Expr, '}') => {
-                if inexpr_open_count == 0 {
-                    output.push(chr);
-
-                    if matches!(state, State::Expr) {
-                        extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
-                    } else {
-                        extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
-                    }
-
-                    current_expr = String::new();
-                    state = State::NotArg;
-                } else {
-                    // We're closing one brace met before inside of the expression.
-                    current_expr.push(chr);
-                    inexpr_open_count -= 1;
-                }
-            }
             (State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => {
                 // path separator
                 state = State::Expr;
                 current_expr.push_str("::");
                 chars.next();
             }
-            (State::Ident | State::Expr, ':') => {
+            (State::Ident | State::Expr, ':' | '}') => {
                 if inexpr_open_count == 0 {
-                    // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
-                    output.push(chr);
+                    let trimmed = current_expr.trim();
 
-                    if matches!(state, State::Expr) {
-                        extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
+                    // if the expression consists of a single number, like "0" or "12", it can refer to
+                    // format args in the order they are specified.
+                    // see: https://doc.rust-lang.org/std/fmt/#positional-parameters
+                    if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) {
+                        output.push_str(trimmed);
+                    } else if matches!(state, State::Expr) {
+                        extracted_expressions.push(Arg::Expr(trimmed.into()));
                     } else {
-                        extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
+                        extracted_expressions.push(Arg::Ident(trimmed.into()));
                     }
 
-                    current_expr = String::new();
-                    state = State::FormatOpts;
-                } else {
+                    output.push(chr);
+                    current_expr.clear();
+                    state = if chr == ':' {
+                        State::FormatOpts
+                    } else if chr == '}' {
+                        State::NotArg
+                    } else {
+                        unreachable!()
+                    };
+                } else if chr == '}' {
+                    // We're closing one brace met before inside of the expression.
+                    current_expr.push(chr);
+                    inexpr_open_count -= 1;
+                } else if chr == ':' {
                     // We're inside of braced expression, assume that it's a struct field name/value delimiter.
                     current_expr.push(chr);
                 }
@@ -219,6 +221,10 @@ mod tests {
             ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
             ("{expr:?}", expect![["{:?}; expr"]]),
             ("{expr:1$}", expect![[r"{:1\$}; expr"]]),
+            ("{:1$}", expect![[r"{:1\$}; $1"]]),
+            ("{:>padding$}", expect![[r"{:>padding\$}; $1"]]),
+            ("{}, {}, {0}", expect![[r"{}, {}, {0}; $1, $2"]]),
+            ("{}, {}, {0:b}", expect![[r"{}, {}, {0:b}; $1, $2"]]),
             ("{$0}", expect![[r"{}; \$0"]]),
             ("{malformed", expect![["-"]]),
             ("malformed}", expect![["-"]]),