- Correct TWo INitial CApitals: a finished word of 3+ letters with two
leading capitals and the rest lowercase loses its second capital
("WOrd" -> "Word"), before the replacement list ("TEh" -> "The").
Built-in exceptions (IDs, CDs, PCs, TVs, ...) plus user exceptions.
- Correct accidental cAPS LOCK: "wORD" -> "Word" (not iOS, mRNA, ...).
- Capitalize first letter of table cells, under its own option.
- Session::autocorrect_caps (AutoCorrectCaps, all on by default);
tools.autocorrect takes twoInitialCaps / capsLock / capCells /
addException / removeException.
- Each fix is one undo step right after it (the existing AutoFormat
snapshot); case changes replace letter by letter, keeping formatting.
- Tests in tests_typing.rs; docs/typing-parity.md and ROADMAP updated.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
parent
417896301b
commit
2f9bf4e800
|
|
@ -135,6 +135,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
|
|||
| 2026-10-11 | Hide White Space (#645): `view.hideWhiteSpace` (View › Show checkbox, or double-click the gap between pages) collapses margins, headers and footers between pages to a thin line in Print Layout; screen only, page count unchanged |
|
||||
| 2026-10-11 | Fonts: Palatino Linotype and Book Antiqua fall back to Palatino (macOS) or TeX Gyre Pagella / P052 / URW Palladio (Linux) before generic serif (#670) |
|
||||
|---|---|
|
||||
| 2026-10-12 | AutoCorrect capitals (#544): Correct TWo INitial CApitals (with exceptions), accidental cAPS LOCK, first letter of table cells; each undoable and switchable |
|
||||
| 2026-10-11 | Turkish (`tr`) interface completed (#774): 2,325 entries, every key of the Russian catalog (equation gallery, built-in style names, watermarks, paper sizes, preview tiles); the interface audit checks Turkish too |
|
||||
| 2026-10-12 | Navigation headings truncate within the resizable pane, with an ellipsis for long titles and full text on hover (#759) |
|
||||
| 2026-10-11 | Reading Mode: existing View ribbon button now opens a read-only, viewport-column layout with animated horizontal paging, transient display controls, heading navigation (Navigation pane) and resize anchoring; document layout and export unchanged |
|
||||
|
|
|
|||
|
|
@ -71,12 +71,16 @@ pub struct AutoCorrectPrefs {
|
|||
pub enabled: bool,
|
||||
/// Replace text as you type (the replacement list).
|
||||
pub replace_text: bool,
|
||||
/// Correct TWo INitial CApitals: "WOrd" → "Word" (not the exceptions, e.g. "IDs").
|
||||
pub two_initial_caps: bool,
|
||||
/// Capitalize the first letter of sentences.
|
||||
pub cap_sentences: bool,
|
||||
/// Capitalize the first letter of table cells.
|
||||
pub cap_cells: bool,
|
||||
/// Capitalize names of days.
|
||||
pub cap_days: bool,
|
||||
/// Correct accidental usage of cAPS LOCK: "wORD" → "Word".
|
||||
pub caps_lock: bool,
|
||||
/// AutoFormat As You Type: "straight quotes" with “smart quotes”.
|
||||
pub smart_quotes: bool,
|
||||
/// Fractions (1/2) with fraction characters (½).
|
||||
|
|
@ -101,6 +105,8 @@ pub struct AutoCorrectPrefs {
|
|||
pub exceptions: Vec<String>,
|
||||
/// Built-in abbreviations the user deleted from the exceptions.
|
||||
pub exceptions_removed: Vec<String>,
|
||||
/// The user's words that keep two initial capitals (besides the built-in ones).
|
||||
pub initial_caps_exceptions: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for AutoCorrectPrefs {
|
||||
|
|
@ -108,9 +114,11 @@ impl Default for AutoCorrectPrefs {
|
|||
AutoCorrectPrefs {
|
||||
enabled: true,
|
||||
replace_text: true,
|
||||
two_initial_caps: true,
|
||||
cap_sentences: true,
|
||||
cap_cells: true,
|
||||
cap_days: true,
|
||||
caps_lock: true,
|
||||
smart_quotes: true,
|
||||
fractions: true,
|
||||
ordinals: true,
|
||||
|
|
@ -123,6 +131,7 @@ impl Default for AutoCorrectPrefs {
|
|||
removed: Vec::new(),
|
||||
exceptions: Vec::new(),
|
||||
exceptions_removed: Vec::new(),
|
||||
initial_caps_exceptions: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,7 +154,7 @@ impl AutoCorrectPrefs {
|
|||
let mut seen = std::collections::HashSet::new();
|
||||
self.entries.retain(|(a, b)| ok(a, MAX_FROM) && b.chars().count() <= MAX_TO && !b.contains(['\n', '\r']) && seen.insert(a.clone()));
|
||||
self.entries.truncate(MAX_ENTRIES);
|
||||
for list in [&mut self.removed, &mut self.exceptions, &mut self.exceptions_removed] {
|
||||
for list in [&mut self.removed, &mut self.exceptions, &mut self.exceptions_removed, &mut self.initial_caps_exceptions] {
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
list.retain(|a| ok(a, MAX_FROM) && seen.insert(a.clone()));
|
||||
list.truncate(MAX_ENTRIES);
|
||||
|
|
@ -216,9 +225,11 @@ fn autocorrect_options(s: &mut Session, v: &Value) -> CmdResult {
|
|||
for (k, f) in [
|
||||
("enabled", &mut ac.enabled as &mut bool),
|
||||
("replaceText", &mut ac.replace_text),
|
||||
("twoInitialCaps", &mut ac.two_initial_caps),
|
||||
("capSentences", &mut ac.cap_sentences),
|
||||
("capCells", &mut ac.cap_cells),
|
||||
("capDays", &mut ac.cap_days),
|
||||
("capsLock", &mut ac.caps_lock),
|
||||
("smartQuotes", &mut ac.smart_quotes),
|
||||
("fractions", &mut ac.fractions),
|
||||
("ordinals", &mut ac.ordinals),
|
||||
|
|
@ -273,13 +284,31 @@ fn autocorrect_options(s: &mut Session, v: &Value) -> CmdResult {
|
|||
ac.exceptions_removed.push(e);
|
||||
}
|
||||
}
|
||||
if let Some(w) = p::str(v, "addInitialCapsException") {
|
||||
let w = w.trim();
|
||||
if bad(w, MAX_FROM) || w.contains(char::is_whitespace) {
|
||||
return Err(CmdError::Params("an INitial CAps exception is one word, like \"IDs\"".into()));
|
||||
}
|
||||
if !INITIAL_CAPS_EXCEPTIONS.contains(&w)
|
||||
&& !ac.initial_caps_exceptions.iter().any(|e| e == w)
|
||||
&& ac.initial_caps_exceptions.len() < MAX_ENTRIES
|
||||
{
|
||||
ac.initial_caps_exceptions.push(w.to_string());
|
||||
}
|
||||
}
|
||||
if let Some(w) = p::str(v, "deleteInitialCapsException") {
|
||||
let w = w.trim();
|
||||
ac.initial_caps_exceptions.retain(|e| e != w);
|
||||
}
|
||||
let ac = &s.prefs.autocorrect;
|
||||
Ok(json!({
|
||||
"enabled": ac.enabled,
|
||||
"replaceText": ac.replace_text,
|
||||
"twoInitialCaps": ac.two_initial_caps,
|
||||
"capSentences": ac.cap_sentences,
|
||||
"capCells": ac.cap_cells,
|
||||
"capDays": ac.cap_days,
|
||||
"capsLock": ac.caps_lock,
|
||||
"smartQuotes": ac.smart_quotes,
|
||||
"fractions": ac.fractions,
|
||||
"ordinals": ac.ordinals,
|
||||
|
|
@ -291,6 +320,7 @@ fn autocorrect_options(s: &mut Session, v: &Value) -> CmdResult {
|
|||
"entries": ac.list().into_iter().map(|(a, b, builtin)| json!({"from": a, "to": b, "builtIn": builtin})).collect::<Vec<_>>(),
|
||||
"user": ac.entries,
|
||||
"exceptions": ac.exception_list(),
|
||||
"initialCapsExceptions": INITIAL_CAPS_EXCEPTIONS.iter().map(|e| e.to_string()).chain(ac.initial_caps_exceptions.iter().cloned()).collect::<Vec<_>>(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +351,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
.pure(),
|
||||
CommandSpec::new("tools.autocorrect", "AutoCorrect Options", "File › Options › Proofing", autocorrect_options)
|
||||
.params(
|
||||
r#"{"enabled"?: bool (all of AutoCorrect), "replaceText"|"capSentences"|"capCells"|"capDays"|"smartQuotes"|"fractions"|"ordinals"|"dashes"|"links"|"bullets"|"numbering"|"borderLines"?: bool, "add"?: {"from": string, "to": string} (adds or replaces an entry), "delete"?: string (an entry's "from", built in or added), "addException"?: string, "deleteException"?: string} → options, entries and exceptions"#,
|
||||
r#"{"enabled"?: bool (all of AutoCorrect), "replaceText"|"twoInitialCaps"|"capSentences"|"capCells"|"capDays"|"capsLock"|"smartQuotes"|"fractions"|"ordinals"|"dashes"|"links"|"bullets"|"numbering"|"borderLines"?: bool, "add"?: {"from": string, "to": string} (adds or replaces an entry), "delete"?: string (an entry's "from", built in or added), "addException"?: string, "deleteException"?: string, "addInitialCapsException"?: string (keeps TWo INitial CApitals, like "IDs"), "deleteInitialCapsException"?: string} → options, entries and exceptions"#,
|
||||
)
|
||||
.pure(),
|
||||
CommandSpec::new("tools.customizeKeyboard", "Customize Keyboard", "File › Options", customize_keyboard)
|
||||
|
|
@ -714,7 +744,7 @@ pub fn autocorrect_word(s: &mut Session, on_enter: bool) -> Result<(), CmdError>
|
|||
return Ok(());
|
||||
}
|
||||
let ac = s.prefs.autocorrect.clone();
|
||||
let f = s.sel.focus.clone();
|
||||
let mut f = s.sel.focus.clone();
|
||||
let Some(text) = s.doc.para_at(&f).map(|p| p.text.clone()) else { return Ok(()) };
|
||||
let Some(before) = text.get(..f.off) else { return Ok(()) };
|
||||
let Some(last) = before.chars().next_back() else { return Ok(()) };
|
||||
|
|
@ -724,10 +754,29 @@ pub fn autocorrect_word(s: &mut Session, on_enter: bool) -> Result<(), CmdError>
|
|||
let trigger = if on_enter { 0 } else { last.len_utf8() };
|
||||
let Some(body) = before.get(..before.len() - trigger) else { return Ok(()) };
|
||||
let start = body.rfind(char::is_whitespace).map(|i| i + 1).unwrap_or(0);
|
||||
let Some(word) = body.get(start..).map(str::to_string) else { return Ok(()) };
|
||||
let Some(mut word) = body.get(start..).map(str::to_string) else { return Ok(()) };
|
||||
if word.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
// TWo INitial CApitals and cAPS LOCK come first, so "TEh" is still a replacement's typo.
|
||||
if let Some((at, reps)) = case_fix(&ac, &word) {
|
||||
let para = s.doc.para_mut(f.story, &f.path)?;
|
||||
let letters = word.get(at..).unwrap_or("").chars().take(reps.len()).collect::<String>();
|
||||
let mut end = start + at + letters.len();
|
||||
for (c, rep) in letters.chars().rev().zip(reps.iter().rev()) {
|
||||
let from = end.saturating_sub(c.len_utf8());
|
||||
if rep.chars().ne(std::iter::once(c)) {
|
||||
let props = para.props_of_char(from).clone();
|
||||
para.delete(from, end)?;
|
||||
para.insert_text(from, rep, &props)?;
|
||||
}
|
||||
end = from;
|
||||
}
|
||||
let fixed: String = reps.concat();
|
||||
f.off = (f.off + fixed.len()).saturating_sub(letters.len());
|
||||
s.sel = Selection::caret(f.clone());
|
||||
word = format!("{}{fixed}{}", word.get(..at).unwrap_or(""), word.get(at + letters.len()..).unwrap_or(""));
|
||||
}
|
||||
// A whole-word replacement, else "wait..." → "wait…".
|
||||
let found = if ac.replace_text { ac.replacement(&word) } else { None };
|
||||
let (from, rep) = match found {
|
||||
|
|
@ -804,6 +853,49 @@ pub fn autocorrect_word(s: &mut Session, on_enter: bool) -> Result<(), CmdError>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Built-in words that keep their two initial capitals (plurals of two-letter abbreviations).
|
||||
pub const INITIAL_CAPS_EXCEPTIONS: &[&str] = &["CDs", "IDs", "PCs", "TVs", "DJs", "GBs", "KBs", "MBs", "TBs", "VMs"];
|
||||
|
||||
/// Words with a lowercase first letter and the rest in capitals that aren't cAPS LOCK slips.
|
||||
const CAPS_LOCK_EXCEPTIONS: &[&str] = &["iOS", "mRNA", "tRNA", "rRNA", "cDNA", "eSIM", "eSATA", "iSCSI", "vCPU"];
|
||||
|
||||
/// The capitalisation fix for a finished word: TWo INitial CApitals ("WOrd" → "Word") or cAPS
|
||||
/// LOCK ("wORD" → "Word"). Returns the byte offset of the word's letters and each letter's
|
||||
/// replacement. Only words of three or more letters, optionally in quotes or brackets.
|
||||
fn case_fix(caps: &AutoCorrectPrefs, word: &str) -> Option<(usize, Vec<String>)> {
|
||||
if !caps.two_initial_caps && !caps.caps_lock {
|
||||
return None;
|
||||
}
|
||||
let at = word.find(char::is_alphabetic)?;
|
||||
let rest = word.get(at..)?;
|
||||
let letters = rest.get(..rest.find(|c: char| !c.is_alphabetic()).unwrap_or(rest.len()))?;
|
||||
let tail = rest.get(letters.len()..)?;
|
||||
if !word.get(..at)?.chars().all(|c| "\"'“‘([".contains(c)) || !tail.chars().all(|c| "\"'”’)]".contains(c)) {
|
||||
return None;
|
||||
}
|
||||
let cs: Vec<char> = letters.chars().collect();
|
||||
let (&a, &b, more) = match cs.as_slice() {
|
||||
[a, b, more @ ..] if !more.is_empty() => (a, b, more),
|
||||
_ => return None,
|
||||
};
|
||||
let one = |c: char| c.to_string();
|
||||
let lower = |c: char| c.to_lowercase().collect::<String>();
|
||||
if caps.two_initial_caps
|
||||
&& a.is_uppercase()
|
||||
&& b.is_uppercase()
|
||||
&& more.iter().all(|c| c.is_lowercase())
|
||||
&& !INITIAL_CAPS_EXCEPTIONS.contains(&letters)
|
||||
&& !caps.initial_caps_exceptions.iter().any(|e| e == letters)
|
||||
{
|
||||
return Some((at, std::iter::once(one(a)).chain(std::iter::once(lower(b))).chain(more.iter().map(|&c| one(c))).collect()));
|
||||
}
|
||||
if caps.caps_lock && a.is_lowercase() && b.is_uppercase() && more.iter().all(|c| c.is_uppercase()) && !CAPS_LOCK_EXCEPTIONS.contains(&letters) {
|
||||
let up = a.to_uppercase().collect::<String>();
|
||||
return Some((at, std::iter::once(up).chain(std::iter::once(lower(b))).chain(more.iter().map(|&c| lower(c))).collect()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Byte length of an ordinal suffix to superscript ("1st" → 2), if `word` is a correct ordinal.
|
||||
fn ordinal_suffix(word: &str) -> Option<usize> {
|
||||
let digits = word.find(|c: char| !c.is_ascii_digit())?;
|
||||
|
|
|
|||
|
|
@ -634,6 +634,61 @@ fn shift_f5_goes_back_through_the_last_edits() {
|
|||
assert!(Session::new(wordcraft_doc::Document::new()).run("edit.goBack", &json!({})).is_err(), "nothing edited yet");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_initial_capitals_are_corrected() {
|
||||
let s = typed("the WOrd and THe end, not IDs or CDs. TV is fine ");
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "The Word and The end, not IDs or CDs. TV is fine ");
|
||||
// "TEh" loses its second capital, then the replacement list fixes the typo.
|
||||
assert_eq!(typed("TEh ").doc.plain_text(StoryRef::Body), "The ");
|
||||
// A user exception.
|
||||
let mut s = typed("");
|
||||
run(&mut s, "tools.autocorrect", json!({"addInitialCapsException": "WCraft"}));
|
||||
typ(&mut s, "WCraft ");
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "WCraft ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accidental_caps_lock_is_corrected() {
|
||||
let s = typed("a wORD and hELLO there. iOS stays ");
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "A Word and Hello there. iOS stays ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capitalisation_fixes_undo_and_turn_off() {
|
||||
let mut s = typed("WOrd");
|
||||
typ(&mut s, " ");
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "Word ");
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "WOrd ");
|
||||
let mut s = typed("x wORD");
|
||||
typ(&mut s, " ");
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "X wORD ");
|
||||
let mut s = typed("");
|
||||
run(&mut s, "tools.autocorrect", json!({"twoInitialCaps": false, "capsLock": false}));
|
||||
typ(&mut s, "x WOrd wORD ");
|
||||
assert_eq!(s.doc.plain_text(StoryRef::Body), "X WOrd wORD ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_letter_of_a_table_cell_is_capitalised() {
|
||||
let cell_text = |s: &Session, c: usize| {
|
||||
s.doc.body.iter().find_map(|b| b.as_table()).and_then(|t| t.rows.first()?.cells.get(c)?.blocks.first()?.as_para().map(|p| p.text.clone()))
|
||||
};
|
||||
let mut s = typed("");
|
||||
run(&mut s, "insert.table", json!({"rows": 1, "cols": 2}));
|
||||
typ(&mut s, "apples and pears\tplums ");
|
||||
assert_eq!(cell_text(&s, 0).as_deref(), Some("Apples and pears"));
|
||||
assert_eq!(cell_text(&s, 1).as_deref(), Some("Plums "));
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(cell_text(&s, 1).as_deref(), Some("plums "));
|
||||
let mut s = typed("");
|
||||
run(&mut s, "tools.autocorrect", json!({"capCells": false}));
|
||||
run(&mut s, "insert.table", json!({"rows": 1, "cols": 1}));
|
||||
typ(&mut s, "plums ");
|
||||
assert_eq!(cell_text(&s, 0).as_deref(), Some("plums "));
|
||||
}
|
||||
|
||||
/// File › Options › Advanced, "Typing replaces selected text" (#485): on (Word's default) the
|
||||
/// typed text replaces the selection; off, it goes in front of the selection and keeps it.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -336,9 +336,11 @@ impl AutoCorrectForm {
|
|||
let o = &self.options;
|
||||
json!({
|
||||
"replaceText": o.replace_text,
|
||||
"twoInitialCaps": o.two_initial_caps,
|
||||
"capSentences": o.cap_sentences,
|
||||
"capCells": o.cap_cells,
|
||||
"capDays": o.cap_days,
|
||||
"capsLock": o.caps_lock,
|
||||
"smartQuotes": o.smart_quotes,
|
||||
"fractions": o.fractions,
|
||||
"ordinals": o.ordinals,
|
||||
|
|
@ -410,6 +412,7 @@ fn autocorrect_ui(app: &mut WordApp, ui: &mut Ui, f: &mut AutoCorrectForm) -> bo
|
|||
|
||||
fn autocorrect_tab(app: &mut WordApp, ui: &mut Ui, f: &mut AutoCorrectForm) {
|
||||
let o = &mut f.options;
|
||||
ui.checkbox(&mut o.two_initial_caps, tl!("Correct TWo INitial CApitals"));
|
||||
ui.checkbox(&mut o.cap_sentences, tl!("Capitalize first letter of sentences"));
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(18.0);
|
||||
|
|
@ -423,6 +426,7 @@ fn autocorrect_tab(app: &mut WordApp, ui: &mut Ui, f: &mut AutoCorrectForm) {
|
|||
let o = &mut f.options;
|
||||
ui.checkbox(&mut o.cap_cells, tl!("Capitalize first letter of table cells"));
|
||||
ui.checkbox(&mut o.cap_days, tl!("Capitalize names of days"));
|
||||
ui.checkbox(&mut o.caps_lock, tl!("Correct accidental usage of cAPS LOCK key"));
|
||||
ui.checkbox(&mut o.replace_text, tl!("Replace text as you type"));
|
||||
egui::Grid::new("ac_boxes").num_columns(2).spacing(vec2(8.0, 2.0)).show(ui, |ui| {
|
||||
ui.label(tl!("Replace:"));
|
||||
|
|
|
|||
|
|
@ -2058,3 +2058,5 @@
|
|||
Reading Column Width Šířka sloupce pro čtení
|
||||
Reading Line Spacing Řádkování pro čtení
|
||||
Reading Text Size Velikost textu pro čtení
|
||||
Correct TWo INitial CApitals Opravovat DVě POčáteční VElká písmena
|
||||
Correct accidental usage of cAPS LOCK key Opravovat náhodné použití klávesy cAPS LOCK
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -2052,3 +2052,5 @@
|
|||
Reading Column Width Spaltenbreite beim Lesen
|
||||
Reading Line Spacing Zeilenabstand beim Lesen
|
||||
Reading Text Size Textgröße beim Lesen
|
||||
Correct TWo INitial CApitals ZWei GRoßbuchstaben am WOrtanfang korrigieren
|
||||
Correct accidental usage of cAPS LOCK key Unbeabsichtigtes Verwenden der fESTSTELLTASTE korrigieren
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -1468,6 +1468,8 @@
|
|||
Border lines Líneas de borde
|
||||
Capitalize first letter of sentences Poner en mayúscula la primera letra de las oraciones
|
||||
Capitalize first letter of table cells Poner en mayúscula la primera letra de las celdas
|
||||
Correct TWo INitial CApitals Corregir DOs MAyúsculas SEguidas
|
||||
Correct accidental usage of cAPS LOCK key Corregir el uso accidental de bLOQ mAYÚS
|
||||
Capitalize names of days Poner en mayúscula los nombres de los días
|
||||
Case changes Cambios de mayúsculas
|
||||
Character level Nivel de carácter
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -1447,6 +1447,8 @@
|
|||
Border lines Äärisjooned
|
||||
Capitalize first letter of sentences Lausete esitäht suureks
|
||||
Capitalize first letter of table cells Tabelilahtrite esitäht suureks
|
||||
Correct TWo INitial CApitals Paranda KAks SUurt algustähte
|
||||
Correct accidental usage of cAPS LOCK key Paranda kogemata sisse jäänud cAPS LOCK
|
||||
Capitalize names of days Nädalapäevade nimed suure algustähega
|
||||
Case changes Tähesuuruse muutused
|
||||
Character level Märgi tasandil
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 12.
|
|
|
@ -2061,3 +2061,5 @@
|
|||
Reading Column Width Largeur de colonne de lecture
|
||||
Reading Line Spacing Interligne de lecture
|
||||
Reading Text Size Taille du texte de lecture
|
||||
Correct TWo INitial CApitals Corriger DEux MAjuscules EN début de mot
|
||||
Correct accidental usage of cAPS LOCK key Corriger l’utilisation accidentelle de la touche vERR. mAJ
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 7 and column 25.
|
|
|
@ -2451,3 +2451,5 @@
|
|||
Reading Column Width Olvasási oszlopszélesség
|
||||
Reading Line Spacing Olvasási sorköz
|
||||
Reading Text Size Olvasási szövegméret
|
||||
Correct TWo INitial CApitals KÉt KEzdő NAgybetű javítása
|
||||
Correct accidental usage of cAPS LOCK key A cAPS LOCK billentyű véletlen használatának javítása
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -2437,3 +2437,5 @@ preview Title Esempio
|
|||
Reading Column Width Larghezza colonna di lettura
|
||||
Reading Line Spacing Interlinea di lettura
|
||||
Reading Text Size Dimensione testo di lettura
|
||||
Correct TWo INitial CApitals Correggi DUe MAiuscole INiziali
|
||||
Correct accidental usage of cAPS LOCK key Correggi l'uso accidentale del tasto bLOC mAIUSC
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -1428,6 +1428,8 @@
|
|||
Border lines 罫線
|
||||
Capitalize first letter of sentences 文の先頭文字を大文字にする
|
||||
Capitalize first letter of table cells 表のセルの先頭文字を大文字にする
|
||||
Correct TWo INitial CApitals 先頭 2 文字の大文字を修正する
|
||||
Correct accidental usage of cAPS LOCK key Caps Lock キーの押し間違いを修正する
|
||||
Capitalize names of days 曜日の先頭文字を大文字にする
|
||||
Case changes 大文字/小文字の変更
|
||||
Character level 文字単位
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 3.
|
|
|
@ -2094,3 +2094,5 @@
|
|||
Reading Column Width Szerokość kolumny do czytania
|
||||
Reading Line Spacing Interlinia do czytania
|
||||
Reading Text Size Rozmiar tekstu do czytania
|
||||
Correct TWo INitial CApitals Poprawiaj DWie POczątkowe WIelkie litery
|
||||
Correct accidental usage of cAPS LOCK key Poprawiaj przypadkowe użycie klawisza cAPS LOCK
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -1440,6 +1440,8 @@
|
|||
Border lines Linhas de borda
|
||||
Capitalize first letter of sentences Primeira letra das frases em maiúscula
|
||||
Capitalize first letter of table cells Primeira letra das células em maiúscula
|
||||
Correct TWo INitial CApitals Corrigir DUas INiciais MAiúsculas
|
||||
Correct accidental usage of cAPS LOCK key Corrigir o uso acidental da tecla cAPS LOCK
|
||||
Capitalize names of days Nomes dos dias em maiúscula
|
||||
Case changes Alterações de maiúsculas
|
||||
Character level Nível de caractere
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -2037,6 +2037,8 @@ preview Title Пример
|
|||
Border lines Линии границ
|
||||
Capitalize first letter of sentences Делать первые буквы предложений прописными
|
||||
Capitalize first letter of table cells Делать первые буквы ячеек таблиц прописными
|
||||
Correct TWo INitial CApitals Исправлять ДВе ПРописные буквы в начале слова
|
||||
Correct accidental usage of cAPS LOCK key Устранять последствия случайного нажатия cAPS LOCK
|
||||
Capitalize names of days Названия дней с прописной буквы
|
||||
Case changes Изменения регистра
|
||||
Character level На уровне символов
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 7 and column 25.
|
|
|
@ -1438,6 +1438,8 @@
|
|||
Border lines Linije ivica
|
||||
Capitalize first letter of sentences Veliko prvo slovo rečenice
|
||||
Capitalize first letter of table cells Veliko prvo slovo u ćelijama tabele
|
||||
Correct TWo INitial CApitals Ispravi DVa POčetna velika slova
|
||||
Correct accidental usage of cAPS LOCK key Ispravi slučajno uključen cAPS LOCK
|
||||
Capitalize names of days Veliko slovo u nazivima dana
|
||||
Case changes Promene veličine slova
|
||||
Character level Nivo znaka
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 25.
|
|
|
@ -1442,6 +1442,8 @@
|
|||
Border lines Линије ивица
|
||||
Capitalize first letter of sentences Велико прво слово реченице
|
||||
Capitalize first letter of table cells Велико прво слово у ћелијама табеле
|
||||
Correct TWo INitial CApitals Исправи ДВа ПОчетна велика слова
|
||||
Correct accidental usage of cAPS LOCK key Исправи случајно укључен cAPS LOCK
|
||||
Capitalize names of days Велико слово у називима дана
|
||||
Case changes Промене величине слова
|
||||
Character level Ниво знака
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 11 and column 25.
|
|
|
@ -2039,6 +2039,8 @@ preview Title Örnek
|
|||
Border lines Kenarlık çizgileri
|
||||
Capitalize first letter of sentences Cümlelerin ilk harfini büyük yap
|
||||
Capitalize first letter of table cells Tablo hücrelerinin ilk harfini büyük yap
|
||||
Correct TWo INitial CApitals İKi BAşharfi büyük olan sözcükleri düzelt
|
||||
Correct accidental usage of cAPS LOCK key cAPS LOCK tuşunun yanlışlıkla kullanımını düzelt
|
||||
Capitalize names of days Gün adlarını büyük harfle başlat
|
||||
Case changes Büyük/küçük harf değişiklikleri
|
||||
Character level Karakter düzeyi
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 15.
|
|
|
@ -1440,6 +1440,8 @@
|
|||
Border lines Лінії меж
|
||||
Capitalize first letter of sentences Перша літера речення велика
|
||||
Capitalize first letter of table cells Перша літера в клітинках таблиці велика
|
||||
Correct TWo INitial CApitals Виправляти ДВі ВЕликі літери на початку слова
|
||||
Correct accidental usage of cAPS LOCK key Виправляти випадкове натискання cAPS LOCK
|
||||
Capitalize names of days Назви днів із великої літери
|
||||
Case changes Зміни регістру
|
||||
Character level Рівень символів
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 31.
|
|
|
@ -1440,6 +1440,8 @@
|
|||
Border lines 边框线
|
||||
Capitalize first letter of sentences 句首字母大写
|
||||
Capitalize first letter of table cells 表格单元格首字母大写
|
||||
Correct TWo INitial CApitals 更正前两个字母连续大写
|
||||
Correct accidental usage of cAPS LOCK key 更正误按 Caps Lock 键造成的大小写错误
|
||||
Capitalize names of days 星期名称首字母大写
|
||||
Case changes 大小写更改
|
||||
Character level 字符级
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 7 and column 25.
|
|
|
@ -1429,6 +1429,8 @@
|
|||
Border lines 框線
|
||||
Capitalize first letter of sentences 句首字母大寫
|
||||
Capitalize first letter of table cells 表格儲存格首字母大寫
|
||||
Correct TWo INitial CApitals 更正前兩個字母連續大寫
|
||||
Correct accidental usage of cAPS LOCK key 更正意外使用 Caps Lock 鍵造成的大小寫錯誤
|
||||
Capitalize names of days 星期名稱首字母大寫
|
||||
Case changes 大小寫變更
|
||||
Character level 字元層級
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 4.
|
|
|
@ -1,6 +1,6 @@
|
|||
# Typing parity with Word
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-11 · **Change:** minor (right-to-left letters typed into an empty paragraph make it right-to-left, #713; previously minor: editing options: typing replaces selection, overtype, #485; previously minor: Move Paragraph Up/Down, Promote/Demote, Go Back and Alt+X keys, #343) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-12 · **Change:** minor (Correct TWo INitial CApitals, cAPS LOCK and table-cell capitals, #544; previously trivial: complex-script caret and Backspace, #604; Move Paragraph Up/Down, Promote/Demote, Go Back and Alt+X keys, #343) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
|
||||
What Word does when you type, observed black-box (same keystrokes typed into Word and
|
||||
WordCraft, results read back paragraph by paragraph) and pinned by tests in
|
||||
|
|
@ -63,6 +63,18 @@ Applied when a word is finished by a space, punctuation **or Enter** (`enter_fin
|
|||
after abbreviations (`e.g.`, `Dr.`, …) or initials (`J.`), and not for mixed-case words
|
||||
(`iPhone`) or single-letter labels (`a.`). `teh`→`the`, `i`→`I`, `(c)`→`©`, `(tm)`→`™`,
|
||||
`wait...`→`wait…`, smart quotes (`autocorrect_as_you_type`).
|
||||
- Correct TWo INitial CApitals: a finished word of three or more letters with two capitals and
|
||||
the rest lowercase loses its second capital (`WOrd`→`Word`, `TEh`→`The`); not the exceptions
|
||||
(`IDs`, `CDs`, `PCs`, `TVs`, … plus the user's, `tools.autocorrect {"addInitialCapsException"}`)
|
||||
(`two_initial_capitals_are_corrected`).
|
||||
- Correct accidental usage of cAPS LOCK: `wORD`→`Word` (three or more letters; not `iOS`,
|
||||
`mRNA`, …). Word also switches Caps Lock off; we only correct the word
|
||||
(`accidental_caps_lock_is_corrected`).
|
||||
- Capitalize first letter of table cells: the first word typed in a cell is capitalised like a
|
||||
sentence start, under its own option (`first_letter_of_a_table_cell_is_capitalised`).
|
||||
- Each fix is one undo step right after it, and each has its own switch (`twoInitialCaps`,
|
||||
`capsLock`, `capCells` on `tools.autocorrect`; on by default)
|
||||
(`capitalisation_fixes_undo_and_turn_off`).
|
||||
- `1/2 1/4 3/4` → `½ ¼ ¾`; `1st 22nd 13th` get superscript suffixes (`ordinals_become_superscript`).
|
||||
- `word -- word` → en dash, `word - word` → en dash, `word--word` → em dash (`dashes`).
|
||||
- Web addresses become links; following text isn't linked (`web_addresses_become_links`).
|
||||
|
|
@ -128,7 +140,9 @@ Word.
|
|||
- Moving paragraphs with Track Changes on isn't recorded as a tracked move; whole table rows
|
||||
don't move yet (a selection across cells is refused). Go Back's places aren't mapped through
|
||||
later edits (they are clamped to the document).
|
||||
- Not yet compared: Correct TWo INitial CApitals; the rows marked ¹ above.
|
||||
- Not yet compared: keystroke-by-keystroke comparison of Correct TWo INitial CApitals, cAPS LOCK
|
||||
and table-cell capitals (implemented from Word's AutoCorrect options; Word's exception list
|
||||
not compared); the rows marked ¹ above.
|
||||
|
||||
## How to compare
|
||||
|
||||
|
|
@ -144,6 +158,7 @@ Word.
|
|||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-12 | minor | AutoCorrect: Correct TWo INitial CApitals, accidental cAPS LOCK, capitalize first letter of table cells (#544) |
|
||||
| 2026-10-11 | minor | Text direction: right-to-left letters typed into an empty paragraph make it right-to-left; existing paragraphs keep theirs (#713) |
|
||||
| 2026-10-11 | trivial | Grammar proofing (not AutoCorrect) no longer suggests capitalising a sentence after `...` or `…` (`proof::tests::ellipses_do_not_start_sentences`) |
|
||||
| 2026-10-11 | minor | AutoFormat tables from `+---+`, arrow bullets, smileys inside words, undo after a border line (#546) |
|
||||
|
|
|
|||
Loading…
Reference in New Issue