Merge PR #4109: Proofing: web addresses after a no-break space are not marked as misspelled

This commit is contained in:
aiongg
2026-10-24 20:38:49 +00:00
2 changed files with 16 additions and 1 deletions
+1
View File
@@ -142,6 +142,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
- 2026-10-22: Mail merge (#3028): Preview Results has First Record and Last Record buttons and a box with the previewed record number; type a number and press Enter to go there (past either end stops at the first or last record). Finish & Merge is now a menu. Edit Individual Documents… and Print Documents… ask for all records, the current record, or a From and To range by record number, and Print Documents opens Print for the merged document. Scripts get `mailings.first`, `mailings.last`, `mailings.goToRecord`, `mailings.printDocuments` and a `records` choice on `mailings.finish`; a range with no records in it is refused instead of leaving an empty document.
- 2026-10-22: Re-routing a group connector keeps its free end at its displayed rotated and flipped position, including after nonuniform group resizing, while the glued end follows its existing connection-site path. Missing target shapes and invalid connection sites keep their transformed endpoints too. Fallback routes that cannot fit the renderer's 4000 pt child-size cap leave the original connector unchanged. Model and engine regressions cover starts, ends, unit/uniform/nonuniform scale, settled reroutes and safe handling of degenerate dimensions and display-size limits; no new native Word comparison.
- 2026-10-23: Lists: an empty paragraph whose mark is hidden takes no room and draws no list number, in the body and in table cells, as in Word; the number is still counted, so the next item keeps its place (#3982).
- 2026-10-24: Spelling: with Ignore web and file addresses on, a web or e-mail address after a no-break space or an ideographic space is skipped, as it is after a plain space. Its words were marked as misspelled.
- 2026-10-23: Font dialog: "NaN" or "inf" typed in the Size box previews the text at 12 pt. "NaN" reads as a number, and a font size that is not a number stopped the app while drawing the preview (#3663).
- 2026-10-23: Tables: a cell span read from a `.json` document (any 32-bit number) no longer overflows when a column is inserted through the cell or the cell is merged: the span is counted from its clamped value and stays within 63 columns. It panicked in debug builds and wrapped to a small span in release (#3637).
- 2026-10-22: Spanish proofing (#3360, #3553): text in Spanish was never checked (it fell through to the languages without tools). Now Spain (`es-ES`), Mexico (`es-MX`), Latin America (`es-419` and the other countries of the Americas) and Uruguay (`es-UY`) each read their own installed dictionary, with fallbacks; suggestions restore accents first (`prueva` → `prueba`, `tambien` → `también`); Uruguayan Spanish accepts the `vos` forms, imperatives included (`vení`, `decime`, `decímelo`); repeated-word, spacing and missing `¿`/`¡` checks explain in Spanish; Review › Language, the status bar and File › Options › Proofing list the four varieties, and a Spanish interface writes new documents in the region's Spanish. No dictionary data bundled; parity estimates unchanged
+15 -1
View File
@@ -717,7 +717,9 @@ fn spelling_issues(text: &str, lang: impl Fn(usize) -> ProofLang, opts: &Options
/// The whitespace-delimited token enclosing the word at `a..b` of `text`: its byte range, and
/// whether it looks like a web or e-mail address.
fn enclosing_token(text: &str, a: usize, b: usize) -> (usize, usize, bool) {
let ts = text.get(..a).and_then(|t| t.rfind(char::is_whitespace)).map(|i| i + 1).unwrap_or(0);
// The token starts after the last white-space character, whatever its width (a no-break
// space is two bytes and an ideographic space is three).
let ts = text.get(..a).and_then(|t| t.char_indices().rev().find(|(_, c)| c.is_whitespace())).map(|(i, c)| i + c.len_utf8()).unwrap_or(0);
let te = text.get(b..).and_then(|t| t.find(char::is_whitespace)).map(|i| b + i).unwrap_or(text.len());
(ts, te, text.get(ts..te).is_some_and(is_address))
}
@@ -1507,6 +1509,18 @@ mod tests {
assert_eq!(bad.last(), Some(&"wugwug"), "{bad:?}");
}
#[test]
fn address_after_a_wide_space_is_an_internet_token() {
// A web or e-mail address after a no-break space or an ideographic space is skipped,
// as it is after a plain space.
let t = "see\u{a0}https://xyzq.example/blorp and\u{3000}www.qwirpy.example or\u{a0}zxq@mail.example wugwug";
let bad: Vec<&str> = check_spelling(t).iter().map(|i| &t[i.start..i.end]).collect();
assert_eq!(bad, vec!["wugwug"]);
let opts = Options { ignore_internet: false, ..Options::default() };
let bad: Vec<&str> = check_spelling_with(t, &opts).iter().map(|i| &t[i.start..i.end]).collect();
assert!(["xyzq", "blorp", "qwirpy", "zxq", "wugwug"].iter().all(|w| bad.contains(w)), "{bad:?}");
}
#[test]
fn spelling_scales_over_one_long_token() {
// #1335: thousands of punctuation-separated words inside a single