use super::{ExprLinter, Lint, LintKind};
use crate::expr::Expr;
use crate::expr::FixedPhrase;
use crate::expr::LongestMatchOf;
use crate::expr::SimilarToPhrase;
use crate::linting::Suggestion;
use crate::linting::expr_linter::Chunk;
use crate::weir::weir_expr_to_expr;
use crate::{Token, TokenStringExt};
pub struct MapPhraseLinter {
description: String,
expr: Box<dyn Expr>,
correct_forms: Vec<String>,
message: String,
lint_kind: LintKind,
}
impl MapPhraseLinter {
pub fn new(
expr: Box<dyn Expr>,
correct_forms: impl IntoIterator<Item = impl ToString>,
message: impl ToString,
description: impl ToString,
lint_kind: Option<LintKind>,
) -> Self {
Self {
description: description.to_string(),
expr,
correct_forms: correct_forms.into_iter().map(|f| f.to_string()).collect(),
message: message.to_string(),
lint_kind: lint_kind.unwrap_or(LintKind::Miscellaneous),
}
}
pub fn new_similar_to_phrase(phrase: &'static str, detectable_distance: u8) -> Self {
Self::new(
Box::new(SimilarToPhrase::from_phrase(phrase, detectable_distance)),
[phrase],
format!("Did you mean the phrase `{phrase}`?"),
format!("Looks for slight improper modifications to the phrase `{phrase}`."),
None,
)
}
pub fn new_fixed_phrases(
phrase: impl IntoIterator<Item = impl AsRef<str>>,
correct_forms: impl IntoIterator<Item = impl ToString>,
message: impl ToString,
description: impl ToString,
lint_kind: Option<LintKind>,
) -> Self {
let patterns = LongestMatchOf::new(
phrase
.into_iter()
.map(|p| {
let expr: Box<dyn Expr> = Box::new(weir_expr_to_expr(p.as_ref()).unwrap());
expr
})
.collect(),
);
Self::new(
Box::new(patterns),
correct_forms,
message,
description,
lint_kind,
)
}
pub fn new_fixed_phrase(
phrase: impl AsRef<str>,
correct_forms: impl IntoIterator<Item = impl ToString>,
message: impl ToString,
description: impl ToString,
lint_kind: Option<LintKind>,
) -> Self {
Self::new(
Box::new(FixedPhrase::from_phrase(phrase.as_ref())),
correct_forms,
message,
description,
lint_kind,
)
}
pub fn new_closed_compound(
phrases: impl IntoIterator<Item = impl AsRef<str>>,
correct_form: impl ToString,
) -> Self {
let message = format!(
"Did you mean the closed compound `{}`?",
correct_form.to_string()
);
let description = format!(
"Looks for incorrect spacing inside the closed compound `{}`.",
correct_form.to_string()
);
Self::new_fixed_phrases(
phrases,
[correct_form],
message,
description,
Some(LintKind::Miscellaneous),
)
}
}
impl ExprLinter for MapPhraseLinter {
type Unit = Chunk;
fn expr(&self) -> &dyn Expr {
self.expr.as_ref()
}
fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
let span = matched_tokens.span()?;
let matched_text = span.get_content(source);
Some(Lint {
span,
lint_kind: self.lint_kind,
suggestions: self
.correct_forms
.iter()
.map(|correct_form| {
Suggestion::replace_with_match_case(
correct_form.chars().collect(),
matched_text,
)
})
.collect(),
message: self.message.to_string(),
priority: 31,
})
}
fn description(&self) -> &str {
self.description.as_str()
}
}