feat(core): allow multiple replacement strategies

This commit is contained in:
Elijah Potter 2025-12-17 12:22:04 -07:00
parent 3828a33e31
commit 622becd520
12 changed files with 53 additions and 16 deletions

View file

@ -6,4 +6,3 @@ declare kind "Usage"
declare becomes ["kelvins", "kelvin"]
test "degrees kelvin" "kelvins"
test "°K" "K"

View file

@ -5,3 +5,4 @@ declare description "Corrects use of `°K` to `K`."
declare kind "Usage"
declare becomes "K"
test "°K" "K"

View file

@ -7,4 +7,4 @@ declare becomes ["don't want", "do not want"]
test "I don't wan to pay for this." "I don't want to pay for this."
test "Don't Wan that option." "Don't Want that option."
test "I don't want to leave." "I don't wantt to leave."
test "I don't want to leave." "I don't want to leave."

View file

@ -7,4 +7,4 @@ declare becomes "excellent"
test "Her results were very good this semester." "Her results were excellent this semester."
test "The performance was excellent, drawing praise from all critics." "The performance was excellent, drawing praise from all critics."
test "He radiated a sense of very goodness in his charitable acts." "He radiated a sense of excellentness in his charitable acts."
test "He radiated a sense of very goodness in his charitable acts." "He radiated a sense of very goodness in his charitable acts."

View file

@ -4,6 +4,7 @@ declare message "The period of economic prosperity is called the `Gilded Age`."
declare description "If referring to the period of economic prosperity, the correct term is `Gilded Age`."
declare kind "Eggcorn"
declare becomes "Gilded Age"
declare strategy "Exact"
test "It is especially a reflection of the socio-economic patterns in the Guilded Age." "It is especially a reflection of the socio-economic patterns in the Gilded Age."
test "It is especially a reflection of the socio-economic patterns in the guilded age." "It is especially a reflection of the socio-economic patterns in the Gilded Age."

View file

@ -6,5 +6,3 @@ declare kind "Spelling"
declare becomes "in lieu of"
test "Controller Emulation in lue of Direct Controller binding" "Controller Emulation in lieu of Direct Controller binding"
test "in of itself" "in and of itself"
test "This is not entirely unexpected in of itself, as Git and GitHub Desktop both generally prove fairly bad at delineating context intelligently..." "This is not entirely unexpected in and of itself, as Git and GitHub Desktop both generally prove fairly bad at delineating context intelligently..."

View file

@ -5,3 +5,5 @@ declare description "Corrects nonstandard `in of itself` to standard `in itself`
declare kind "Usage"
declare becomes ["in itself", "in and of itself"]
test "in of itself" "in and of itself"
test "This is not entirely unexpected in of itself, as Git and GitHub Desktop both generally prove fairly bad at delineating context intelligently..." "This is not entirely unexpected in and of itself, as Git and GitHub Desktop both generally prove fairly bad at delineating context intelligently..."

View file

@ -1,10 +1,10 @@
set main (on second though)
set main <([on, my] second though), (second though)>
declare message "Idiomatic expression: use `on second thought` instead of `on second though`"
declare description "Replaces the nonstandard `on second though` with the common idiom `on second thought` to indicate reconsideration."
declare kind "Typo"
declare becomes "on second thought"
declare becomes "second thought"
test "I was going to buy it, but on second though, maybe I'll wait." "I was going to buy it, but on second thought, maybe I'll wait."
test "She considered driving home, but on second thought, she decided to walk." "She considered driving home, but on second thoughtt, she decided to walk."
test "My second though is that I'd prefer something else entirely." "My second though is that I'd prefer something else entirely."
test "She considered driving home, but on second thought, she decided to walk." "She considered driving home, but on second thought, she decided to walk."
test "My second though is that I'd prefer something else entirely." "My second thought is that I'd prefer something else entirely."

View file

@ -4,7 +4,8 @@ declare message "`IIRC` already means 'if I recall correctly', so adding 'if' or
declare description "Flags redundant use of 'if' or 'correctly' with `IIRC`, since `IIRC` already stands for 'if I recall correctly'."
declare kind "Redundancy"
declare becomes "IIRC"
declare strategy "Exact"
test "This is due to the fact that if IIRC up to 2 processes mpirun will bind to core and then it will be socket." "This is due to the fact that IIRC up to 2 processes mpirun will bind to core and then it will be socket."
test "if iirc getting it to work with the SQLite storage engine was turning into a whole project and we decided to punt it" "iirc getting it to work with the SQLite storage engine was turning into a whole project and we decided to punt it"
test "if iirc getting it to work with the SQLite storage engine was turning into a whole project and we decided to punt it" "IIRC getting it to work with the SQLite storage engine was turning into a whole project and we decided to punt it"
test "IIRC correctly, someone on the Home Assistant forums went as far as discovering that RS-485 was being used." "IIRC, someone on the Home Assistant forums went as far as discovering that RS-485 was being used."

View file

@ -5,5 +5,5 @@ declare description "Corrects `ticking time clock` to `ticking time bomb` for id
declare kind "Usage"
declare becomes ["ticking time bomb", "ticking clock"]
test "One element that can help up the stakes (and tension!) is a “ticking time clock.”" "One element that can help up the stakes (and tension!) is a “ticking time bomb.”"
test "One element that can help up the stakes (and tension!) is a \“ticking time clock.\”" "One element that can help up the stakes (and tension!) is a \“ticking time bomb.\”"
test "The opportunity itself has a ticking time clock as all great opportunities do." "The opportunity itself has a ticking clock as all great opportunities do."

View file

@ -16,6 +16,8 @@ pub enum Error {
ExpectedVariableUndefined,
#[error("Invalid LintKind")]
InvalidLintKind,
#[error("Invalid Replacement Strategy")]
InvalidReplacementStrategy,
#[error("Expected a variable type other than the one provided.")]
ExpectedDifferentVariableType,
}

View file

@ -3,9 +3,13 @@ mod error;
mod optimize;
mod parsing;
use std::str::FromStr;
use crate::parsers::PlainEnglish;
pub use error::Error;
use is_macro::Is;
use parsing::{parse_expr_str, parse_str};
use strum_macros::{AsRefStr, EnumString};
use crate::expr::Expr;
use crate::linting::{Chunk, ExprLinter, Lint, LintKind, Linter, Suggestion};
@ -18,6 +22,12 @@ pub fn weir_expr_to_expr(weir_code: &str) -> Result<Box<dyn Expr>, Error> {
Ok(ast.to_expr())
}
#[derive(Debug, Is, EnumString, AsRefStr)]
enum ReplacementStrategy {
MatchCase,
Exact,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TestResult {
expected: String,
@ -28,6 +38,7 @@ pub struct WeirLinter {
expr: Box<dyn Expr>,
description: String,
message: String,
strategy: ReplacementStrategy,
replacements: Vec<String>,
lint_kind: LintKind,
ast: Ast,
@ -42,6 +53,7 @@ impl WeirLinter {
let message_name = "message";
let lint_kind_name = "kind";
let replacement_name = "becomes";
let replacement_strat_name = "strategy";
let expr = ast
.get_expr(main_expr_name)
@ -82,8 +94,19 @@ impl WeirLinter {
}
};
let lint_kind_var = ast.get_variable_value(lint_kind_name);
let replacement_strat_var = ast.get_variable_value(replacement_strat_name);
let replacement_strat = if let Some(replacement_strat) = replacement_strat_var {
let str = replacement_strat
.as_string()
.ok_or(Error::ExpectedDifferentVariableType)?;
ReplacementStrategy::from_str(str)
.ok()
.ok_or(Error::InvalidReplacementStrategy)?
} else {
ReplacementStrategy::MatchCase
};
let lint_kind_var = ast.get_variable_value(lint_kind_name);
let lint_kind = if let Some(lint_kind) = lint_kind_var {
let str = lint_kind
.as_string()
@ -94,6 +117,7 @@ impl WeirLinter {
};
let linter = WeirLinter {
strategy: replacement_strat,
ast,
expr,
lint_kind,
@ -183,14 +207,23 @@ impl ExprLinter for WeirLinter {
let span = matched_tokens.span()?;
let orig = span.get_content(source);
Some(Lint {
span,
lint_kind: self.lint_kind,
suggestions: self
let suggestions = match self.strategy {
ReplacementStrategy::MatchCase => self
.replacements
.iter()
.map(|s| Suggestion::replace_with_match_case(s.chars().collect(), orig))
.collect(),
ReplacementStrategy::Exact => self
.replacements
.iter()
.map(|r| Suggestion::ReplaceWith(r.chars().collect()))
.collect(),
};
Some(Lint {
span,
lint_kind: self.lint_kind,
suggestions,
message: self.message.to_owned(),
priority: 127,
})