diff --git a/lt-core/src/document.rs b/lt-core/src/document.rs index b6bbdfdd..5b5be858 100644 --- a/lt-core/src/document.rs +++ b/lt-core/src/document.rs @@ -28,7 +28,7 @@ impl Document { doc } - fn iter_quote_indices(&self) -> impl Iterator + '_ { + pub fn iter_quote_indices(&self) -> impl Iterator + '_ { self.tokens.iter().enumerate().filter_map(|(idx, token)| { if let TokenKind::Punctuation(Punctuation::Quote(_)) = &token.kind { Some(idx) @@ -38,6 +38,10 @@ impl Document { }) } + pub fn iter_quotes(&self) -> impl Iterator + '_ { + self.iter_quote_indices().map(|idx| self.tokens[idx]) + } + /// Searches for quotation marks and fills the [`Punctuation::Quote::twin_loc`] field. /// This is on a best effort basis. /// @@ -92,7 +96,7 @@ impl Document { let first_sentence = self .sentence_terminators() .next() - .map(|first_term| &self.tokens[1..=first_term]); + .map(|first_term| &self.tokens[0..=first_term]); let rest = self .sentence_terminators() diff --git a/lt-core/src/linting/lint.rs b/lt-core/src/linting/lint.rs index a1225c52..c90eb062 100644 --- a/lt-core/src/linting/lint.rs +++ b/lt-core/src/linting/lint.rs @@ -15,6 +15,7 @@ pub enum LintKind { Spelling, Capitalization, UnmatchedQuote, + WrongQuotes, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/lt-core/src/linting/mod.rs b/lt-core/src/linting/mod.rs index c5edd19e..268dd59f 100644 --- a/lt-core/src/linting/mod.rs +++ b/lt-core/src/linting/mod.rs @@ -2,6 +2,7 @@ mod lint; mod sentence_capitalization; mod spell_check; mod unclosed_quotes; +mod wrong_quotes; pub use lint::{Lint, LintKind, Suggestion}; @@ -12,10 +13,11 @@ use self::lint::Linter; pub fn all_linters(document: &Document) -> Vec { let mut lints = Vec::new(); - let linters: [Linter; 3] = [ + let linters: [Linter; 4] = [ spell_check::spell_check, sentence_capitalization::sentence_capitalization_lint, unclosed_quotes::unclosed_quotes, + wrong_quotes::wrong_quotes, ]; for linter in linters { diff --git a/lt-core/src/linting/wrong_quotes.rs b/lt-core/src/linting/wrong_quotes.rs new file mode 100644 index 00000000..80bb9027 --- /dev/null +++ b/lt-core/src/linting/wrong_quotes.rs @@ -0,0 +1,33 @@ +use crate::{ + document::Document, parsing::Quote, Lint, LintKind, Punctuation, Suggestion, Token, TokenKind, +}; + +pub fn wrong_quotes(document: &Document) -> Vec { + document + .iter_quote_indices() + .zip(document.iter_quotes()) + .filter_map(|(quote_idx, quote_token)| lint_quote(document, quote_idx, quote_token)) + .collect() +} + +fn lint_quote(document: &Document, quote_idx: usize, quote_token: Token) -> Option { + let quote = quote_token.kind.as_quote().unwrap(); + + let twin_loc = quote.twin_loc?; + let is_left = twin_loc > quote_idx; + + let quote_char = *document.get_span_content(quote_token.span).first()?; + + let should_be = if is_left { '“' } else { '”' }; + + if quote_char != should_be { + Some(Lint { + span: quote_token.span, + lint_kind: LintKind::WrongQuotes, + suggestions: vec![Suggestion::ReplaceWith(vec![should_be])], + message: "Use the better-formatted quote character.".to_string(), + }) + } else { + None + } +}