mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
Auto merge of #16782 - DropDemBits:format-string-exprs-comments, r=Veykril
fix: Preserve $ and \ in postfix format completions `parse_format_exprs` doesn't escape these two as of #16781, so they have to be escaped as a separate step.
This commit is contained in:
commit
00a0125372
3 changed files with 41 additions and 31 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -11,15 +11,12 @@ pub enum Arg {
|
||||||
Expr(String),
|
Expr(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Add placeholders like `$1` and `$2` in place of [`Arg::Placeholder`],
|
||||||
Add placeholders like `$1` and `$2` in place of [`Arg::Placeholder`],
|
/// and unwraps the [`Arg::Ident`] and [`Arg::Expr`] enums.
|
||||||
and unwraps the [`Arg::Ident`] and [`Arg::Expr`] enums.
|
/// ```rust
|
||||||
```rust
|
/// # use ide_db::syntax_helpers::format_string_exprs::*;
|
||||||
# use ide_db::syntax_helpers::format_string_exprs::*;
|
/// assert_eq!(with_placeholders(vec![Arg::Ident("ident".to_owned()), Arg::Placeholder, Arg::Expr("expr + 2".to_owned())]), vec!["ident".to_owned(), "$1".to_owned(), "expr + 2".to_owned()])
|
||||||
assert_eq!(with_placeholders(vec![Arg::Ident("ident".to_owned()), Arg::Placeholder, Arg::Expr("expr + 2".to_owned())]), vec!["ident".to_owned(), "$1".to_owned(), "expr + 2".to_owned()])
|
/// ```
|
||||||
```
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
|
pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
|
||||||
let mut placeholder_id = 1;
|
let mut placeholder_id = 1;
|
||||||
args.into_iter()
|
args.into_iter()
|
||||||
|
@ -34,18 +31,15 @@ pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Parser for a format-like string. It is more allowing in terms of string contents,
|
||||||
Parser for a format-like string. It is more allowing in terms of string contents,
|
/// as we expect variable placeholders to be filled with expressions.
|
||||||
as we expect variable placeholders to be filled with expressions.
|
///
|
||||||
|
/// Splits a format string that may contain expressions
|
||||||
Built for completions and assists, and escapes `\` and `$` in output.
|
/// like
|
||||||
(See the comments on `get_receiver_text()` for detail.)
|
/// ```rust
|
||||||
Splits a format string that may contain expressions
|
/// # use ide_db::syntax_helpers::format_string_exprs::*;
|
||||||
like
|
/// assert_eq!(parse_format_exprs("{ident} {} {expr + 42} ").unwrap(), ("{ident} {} {} ".to_owned(), vec![Arg::Placeholder, Arg::Expr("expr + 42".to_owned())]));
|
||||||
```rust
|
/// ```
|
||||||
assert_eq!(parse("{ident} {} {expr + 42} ").unwrap(), ("{} {} {}", vec![Arg::Ident("ident"), Arg::Placeholder, Arg::Expr("expr + 42")]));
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
|
pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
enum State {
|
enum State {
|
||||||
|
|
Loading…
Reference in a new issue