fix: Preserve $ and \ in postfix format completions

`parse_format_exprs` doesn't escape these two anymore, so they have to be escaped as a separate step.
This commit is contained in:
DropDemBits 2024-03-07 16:55:09 -05:00
parent 7dadc64d1c
commit bc381837e3
No known key found for this signature in database
GPG Key ID: 7FE02A6C1EDFA075
2 changed files with 26 additions and 10 deletions

View File

@ -258,7 +258,7 @@ pub(crate) fn complete_postfix(
} }
fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
let text = if receiver_is_ambiguous_float_literal { let mut text = if receiver_is_ambiguous_float_literal {
let text = receiver.syntax().text(); let text = receiver.syntax().text();
let without_dot = ..text.len() - TextSize::of('.'); let without_dot = ..text.len() - TextSize::of('.');
text.slice(without_dot).to_string() text.slice(without_dot).to_string()
@ -267,12 +267,18 @@ fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal:
}; };
// The receiver texts should be interpreted as-is, as they are expected to be // The receiver texts should be interpreted as-is, as they are expected to be
// normal Rust expressions. We escape '\' and '$' so they don't get treated as // normal Rust expressions.
// snippet-specific constructs. escape_snippet_bits(&mut text);
// text
// Note that we don't need to escape the other characters that can be escaped, }
// because they wouldn't be treated as snippet-specific constructs without '$'.
text.replace('\\', "\\\\").replace('$', "\\$") /// Escapes `\` and `$` so that they don't get interpreted as snippet-specific constructs.
///
/// Note that we don't need to escape the other characters that can be escaped,
/// because they wouldn't be treated as snippet-specific constructs without '$'.
fn escape_snippet_bits(text: &mut String) {
stdx::replace(text, '\\', "\\\\");
stdx::replace(text, '$', "\\$");
} }
fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {

View File

@ -17,13 +17,15 @@
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] // image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
use ide_db::{ use ide_db::{
syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders}, syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg},
SnippetCap, SnippetCap,
}; };
use syntax::{ast, AstToken}; use syntax::{ast, AstToken};
use crate::{ use crate::{
completions::postfix::build_postfix_snippet_builder, context::CompletionContext, Completions, completions::postfix::{build_postfix_snippet_builder, escape_snippet_bits},
context::CompletionContext,
Completions,
}; };
/// Mapping ("postfix completion item" => "macro to use") /// Mapping ("postfix completion item" => "macro to use")
@ -51,7 +53,15 @@ pub(crate) fn add_format_like_completions(
None => return, None => return,
}; };
if let Ok((out, exprs)) = parse_format_exprs(receiver_text.text()) { if let Ok((mut out, mut exprs)) = parse_format_exprs(receiver_text.text()) {
// Escape any snippet bits in the out text and any of the exprs.
escape_snippet_bits(&mut out);
for arg in &mut exprs {
if let Arg::Ident(text) | Arg::Expr(text) = arg {
escape_snippet_bits(text)
}
}
let exprs = with_placeholders(exprs); let exprs = with_placeholders(exprs);
for (label, macro_name) in KINDS { for (label, macro_name) in KINDS {
let snippet = if exprs.is_empty() { let snippet = if exprs.is_empty() {