mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
SSR: Parse template as Rust code.
This is in preparation for a subsequent commit where we add special handling for paths in the template, allowing them to be qualified differently in different contexts.
This commit is contained in:
parent
1fce8b6ba3
commit
113abbeefe
5 changed files with 112 additions and 82 deletions
|
@ -15,12 +15,8 @@ use std::str::FromStr;
|
|||
pub(crate) struct ParsedRule {
|
||||
pub(crate) placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>,
|
||||
pub(crate) pattern: SyntaxNode,
|
||||
pub(crate) template: Option<SsrTemplate>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SsrTemplate {
|
||||
pub(crate) tokens: Vec<PatternElement>,
|
||||
pub(crate) template: Option<SyntaxNode>,
|
||||
pub(crate) index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -64,18 +60,23 @@ pub(crate) struct Token {
|
|||
impl ParsedRule {
|
||||
fn new(
|
||||
pattern: &RawPattern,
|
||||
template: Option<&SsrTemplate>,
|
||||
template: Option<&RawPattern>,
|
||||
) -> Result<Vec<ParsedRule>, SsrError> {
|
||||
let raw_pattern = pattern.as_rust_code();
|
||||
let raw_template = template.map(|t| t.as_rust_code());
|
||||
let raw_template = raw_template.as_ref().map(|s| s.as_str());
|
||||
let mut builder = RuleBuilder {
|
||||
placeholders_by_stand_in: pattern.placeholders_by_stand_in(),
|
||||
rules: Vec::new(),
|
||||
};
|
||||
builder.try_add(ast::Expr::parse(&raw_pattern), template);
|
||||
builder.try_add(ast::TypeRef::parse(&raw_pattern), template);
|
||||
builder.try_add(ast::ModuleItem::parse(&raw_pattern), template);
|
||||
builder.try_add(ast::Path::parse(&raw_pattern), template);
|
||||
builder.try_add(ast::Pat::parse(&raw_pattern), template);
|
||||
builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse));
|
||||
builder.try_add(ast::TypeRef::parse(&raw_pattern), raw_template.map(ast::TypeRef::parse));
|
||||
builder.try_add(
|
||||
ast::ModuleItem::parse(&raw_pattern),
|
||||
raw_template.map(ast::ModuleItem::parse),
|
||||
);
|
||||
builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse));
|
||||
builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse));
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
@ -86,12 +87,22 @@ struct RuleBuilder {
|
|||
}
|
||||
|
||||
impl RuleBuilder {
|
||||
fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<&SsrTemplate>) {
|
||||
match pattern {
|
||||
Ok(pattern) => self.rules.push(ParsedRule {
|
||||
fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<Result<T, ()>>) {
|
||||
match (pattern, template) {
|
||||
(Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule {
|
||||
placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),
|
||||
pattern: pattern.syntax().clone(),
|
||||
template: template.cloned(),
|
||||
template: Some(template.syntax().clone()),
|
||||
// For now we give the rule an index of 0. It's given a proper index when the rule
|
||||
// is added to the SsrMatcher. Using an Option<usize>, instead would be slightly
|
||||
// more correct, but we delete this field from ParsedRule in a subsequent commit.
|
||||
index: 0,
|
||||
}),
|
||||
(Ok(pattern), None) => self.rules.push(ParsedRule {
|
||||
placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),
|
||||
pattern: pattern.syntax().clone(),
|
||||
template: None,
|
||||
index: 0,
|
||||
}),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -99,7 +110,7 @@ impl RuleBuilder {
|
|||
|
||||
fn build(self) -> Result<Vec<ParsedRule>, SsrError> {
|
||||
if self.rules.is_empty() {
|
||||
bail!("Pattern is not a valid Rust expression, type, item, path or pattern");
|
||||
bail!("Not a valid Rust expression, type, item, path or pattern");
|
||||
}
|
||||
Ok(self.rules)
|
||||
}
|
||||
|
@ -179,21 +190,6 @@ impl FromStr for SsrPattern {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for SsrTemplate {
|
||||
type Err = SsrError;
|
||||
|
||||
fn from_str(pattern_str: &str) -> Result<SsrTemplate, SsrError> {
|
||||
let tokens = parse_pattern(pattern_str)?;
|
||||
// Validate that the template is a valid fragment of Rust code. We reuse the validation
|
||||
// logic for search patterns since the only thing that differs is the error message.
|
||||
if SsrPattern::from_str(pattern_str).is_err() {
|
||||
bail!("Replacement is not a valid Rust expression, type, item, path or pattern");
|
||||
}
|
||||
// Our actual template needs to preserve whitespace, so we can't reuse `tokens`.
|
||||
Ok(SsrTemplate { tokens })
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `pattern_str`, parsed as a search or replace pattern. If `remove_whitespace` is true,
|
||||
/// then any whitespace tokens will be removed, which we do for the search pattern, but not for the
|
||||
/// replace pattern.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue