mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
fix(spell): skip backslash commands like \brief in comments (#985)
Comments and Markdown went through the string-escape rule, which drops only the letter after a backslash, so the Doxygen command `\brief` was reported as the typo "rief". Outside string literals the whole word after a backslash is skipped now (Doxygen / TeX commands, escapes quoted in comments); string literals still skip only the escape letter. Co-authored-by: fhrmrk <[email protected]>
This commit is contained in:
1 parent
839bd0cdb0
commit
bdeca5803b
2 files changed
+37
-6
No files matched your search
@@ -18,7 +18,8 @@ use std::time::SystemTime;
|
||||
|
||||
pub use words::{MIN_WORD_CHARS, WordSpan, words};
|
||||
|
||||
/// Where a region's words come from; strings and comments understand escapes.
|
||||
/// Where a region's words come from; strings understand escapes, comments and
|
||||
/// prose skip backslash commands (`\brief`).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RegionKind {
|
||||
Comment,
|
||||
|
||||
@@ -17,7 +17,11 @@ pub struct WordSpan {
|
||||
/// Words of `text[start..end]` in order, as absolute byte ranges.
|
||||
pub fn words(text: &str, start: usize, end: usize, kind: RegionKind, out: &mut Vec<WordSpan>) {
|
||||
let Some(slice) = text.get(start..end) else { return };
|
||||
let escapes = !matches!(kind, RegionKind::Name);
|
||||
let escapes = match kind {
|
||||
RegionKind::Name => Escapes::None,
|
||||
RegionKind::String => Escapes::Char,
|
||||
RegionKind::Comment | RegionKind::Prose => Escapes::Word,
|
||||
};
|
||||
let mut chunk_start = None;
|
||||
for (i, c) in slice.char_indices().chain(std::iter::once((slice.len(), ' '))) {
|
||||
if c.is_whitespace() {
|
||||
@@ -30,6 +34,18 @@ pub fn words(text: &str, start: usize, end: usize, kind: RegionKind, out: &mut V
|
||||
}
|
||||
}
|
||||
|
||||
/// What a `\` hides from the spell checker.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Escapes {
|
||||
/// Identifiers have no escapes.
|
||||
None,
|
||||
/// String literals: the escape letter (`\n`, `\t`), the rest of the word is text.
|
||||
Char,
|
||||
/// Comments and prose: the whole word, a Doxygen / TeX command (`\brief`,
|
||||
/// `\param`) or an escape quoted in a comment (`\n`).
|
||||
Word,
|
||||
}
|
||||
|
||||
/// URLs, e-mail addresses and the like are not prose.
|
||||
fn skip_chunk(chunk: &str) -> bool {
|
||||
if chunk.contains("://") || chunk.starts_with("www.") {
|
||||
@@ -41,7 +57,7 @@ fn skip_chunk(chunk: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn chunk(slice: &str, from: usize, to: usize, escapes: bool, base: usize, out: &mut Vec<WordSpan>) {
|
||||
fn chunk(slice: &str, from: usize, to: usize, escapes: Escapes, base: usize, out: &mut Vec<WordSpan>) {
|
||||
let text = &slice[from..to];
|
||||
if skip_chunk(text) {
|
||||
return;
|
||||
@@ -57,9 +73,15 @@ fn chunk(slice: &str, from: usize, to: usize, escapes: bool, base: usize, out: &
|
||||
if let Some(s) = run.take() {
|
||||
token(text, s, i, base + from, out);
|
||||
}
|
||||
// `\n`, `\t`, `é`: the escape letter is not part of the next word.
|
||||
if escapes && c == '\\' {
|
||||
chars.next();
|
||||
if c == '\\' {
|
||||
match escapes {
|
||||
Escapes::None => {}
|
||||
// `\n`, `\t`, `é`: the escape letter is not part of the next word.
|
||||
Escapes::Char => {
|
||||
chars.next();
|
||||
}
|
||||
Escapes::Word => while chars.next_if(|&(_, c)| c.is_alphanumeric() || c == '_').is_some() {},
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(s) = run {
|
||||
@@ -164,6 +186,14 @@ mod tests {
|
||||
assert_eq!(split("// 中文注释 naïve", RegionKind::Comment), ["naïve"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skips_backslash_commands_in_comments() {
|
||||
assert_eq!(split("/// \\brief Returns the sum.", RegionKind::Comment), ["Returns", "the", "sum"]);
|
||||
assert_eq!(split("@see \\ref Foo, \\param[in] count", RegionKind::Comment), ["see", "Foo", "count"]);
|
||||
assert_eq!(split("prints \\nthe line", RegionKind::Comment), ["prints", "line"]);
|
||||
assert_eq!(split("\\emph{word} text", RegionKind::Prose), ["word", "text"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_apostrophes_inside_words() {
|
||||
assert_eq!(split("'don't' shouldn't", RegionKind::Comment), ["don't", "shouldn't"]);
|
||||
|
||||
Reference in new issue
Block a user