mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
Auto merge of #12636 - xuhongxu96:fix-12148, r=Veykril
complete raw identifier with "r#" prefix Fix #12148 Escape Names and Paths used in `insert_text`/`insert_snippet` while rendering the completion items.
This commit is contained in:
commit
994f3cf74d
12 changed files with 304 additions and 60 deletions
|
@ -20,6 +20,9 @@ pub struct ModPath {
|
|||
segments: Vec<Name>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct EscapedModPath<'a>(&'a ModPath);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
|
@ -97,10 +100,12 @@ impl ModPath {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModPath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
pub fn escaped(&self) -> EscapedModPath {
|
||||
EscapedModPath(self)
|
||||
}
|
||||
|
||||
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
|
||||
let mut first_segment = true;
|
||||
let mut add_segment = |s| -> fmt::Result {
|
||||
if !first_segment {
|
||||
|
@ -127,12 +132,28 @@ impl Display for ModPath {
|
|||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
segment.fmt(f)?;
|
||||
if escaped {
|
||||
segment.escaped().fmt(f)?
|
||||
} else {
|
||||
segment.fmt(f)?
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModPath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self._fmt(f, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for EscapedModPath<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0._fmt(f, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Name> for ModPath {
|
||||
fn from(name: Name) -> ModPath {
|
||||
ModPath::from_segments(PathKind::Plain, iter::once(name))
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use syntax::{ast, SmolStr};
|
||||
use syntax::{ast, SmolStr, SyntaxKind};
|
||||
|
||||
/// `Name` is a wrapper around string, which is used in hir for both references
|
||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||
|
@ -10,6 +10,10 @@ use syntax::{ast, SmolStr};
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Name(Repr);
|
||||
|
||||
/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct EscapedName<'a>(&'a Name);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Repr {
|
||||
Text(SmolStr),
|
||||
|
@ -25,6 +29,51 @@ impl fmt::Display for Name {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_raw_identifier(name: &str) -> bool {
|
||||
let is_keyword = SyntaxKind::from_keyword(name).is_some();
|
||||
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for EscapedName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.0 .0 {
|
||||
Repr::Text(text) => {
|
||||
if is_raw_identifier(text) {
|
||||
write!(f, "r#{}", &text)
|
||||
} else {
|
||||
fmt::Display::fmt(&text, f)
|
||||
}
|
||||
}
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EscapedName<'a> {
|
||||
pub fn is_escaped(&self) -> bool {
|
||||
match &self.0 .0 {
|
||||
Repr::Text(it) => is_raw_identifier(&it),
|
||||
Repr::TupleField(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the textual representation of this name as a [`SmolStr`].
|
||||
/// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in
|
||||
/// the general case.
|
||||
pub fn to_smol_str(&self) -> SmolStr {
|
||||
match &self.0 .0 {
|
||||
Repr::Text(it) => {
|
||||
if is_raw_identifier(&it) {
|
||||
SmolStr::from_iter(["r#", &it])
|
||||
} else {
|
||||
it.clone()
|
||||
}
|
||||
}
|
||||
Repr::TupleField(it) => SmolStr::new(&it.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Note: this is private to make creating name from random string hard.
|
||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||
|
@ -92,6 +141,10 @@ impl Name {
|
|||
Repr::TupleField(it) => SmolStr::new(&it.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn escaped(&self) -> EscapedName {
|
||||
EscapedName(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsName {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue