diff --git a/crates/engine/src/cmd/caret.rs b/crates/engine/src/cmd/caret.rs index 50c1cd019..c4e38a2ba 100644 --- a/crates/engine/src/cmd/caret.rs +++ b/crates/engine/src/cmd/caret.rs @@ -14,17 +14,24 @@ pub fn specs() -> Vec { CommandSpec::new("caret.right", "Right", "Navigation", |s, v| lr(s, v, false)).key("Right").params(EXTEND).pure(), CommandSpec::new("caret.up", "Up", "Navigation", |s, v| vert(s, v, -1)).key("Up").params(EXTEND).pure(), CommandSpec::new("caret.down", "Down", "Navigation", |s, v| vert(s, v, 1)).key("Down").params(EXTEND).pure(), - // In a right-to-left paragraph the word to the left is the next one. + // In a right-to-left paragraph the word to the left is the next one. On macOS ⌘←/⌘→ go + // to the line's start and end, as in Word for Mac and every Mac text field (#954). CommandSpec::new("caret.wordLeft", "Word Left", "Navigation", |s, v| mv(s, v, if rtl_para(s) { word_right } else { word_left })) - .key("Mod+Left / Alt+Left") + .key(super::mac_or("Alt+Left", "Mod+Left / Alt+Left")) .params(EXTEND) .pure(), CommandSpec::new("caret.wordRight", "Word Right", "Navigation", |s, v| mv(s, v, if rtl_para(s) { word_left } else { word_right })) - .key("Mod+Right / Alt+Right") + .key(super::mac_or("Alt+Right", "Mod+Right / Alt+Right")) + .params(EXTEND) + .pure(), + CommandSpec::new("caret.home", "Line Start", "Navigation", |s, v| line(s, v, true)) + .key(super::mac_or("Home / Mod+Left", "Home")) + .params(EXTEND) + .pure(), + CommandSpec::new("caret.end", "Line End", "Navigation", |s, v| line(s, v, false)) + .key(super::mac_or("End / Mod+Right", "End")) .params(EXTEND) .pure(), - CommandSpec::new("caret.home", "Line Start", "Navigation", |s, v| line(s, v, true)).key("Home").params(EXTEND).pure(), - CommandSpec::new("caret.end", "Line End", "Navigation", |s, v| line(s, v, false)).key("End").params(EXTEND).pure(), CommandSpec::new("caret.paraUp", "Paragraph Up", "Navigation", |s, v| mv(s, v, para_up)).key("Mod+Up / Alt+Up").params(EXTEND).pure(), CommandSpec::new("caret.paraDown", "Paragraph Down", "Navigation", |s, v| mv(s, v, para_down)) .key("Mod+Down / Alt+Down") diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 321db0c0c..038786e02 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -122,6 +122,11 @@ fn word_default_shortcuts_resolve() { assert_eq!(id(cmd::mac_or("Ctrl+Q", "Mod+Q")), Some("para.reset")); assert_eq!(id(cmd::mac_or("Ctrl+Space", "Mod+Space")), Some("format.resetChar")); assert_eq!(id(cmd::mac_or("Ctrl+Shift+D", "Alt+Shift+D")), Some("insert.dateField")); + // Option+arrows move by word everywhere; ⌘←/⌘→ go to the line's ends on macOS (#954). + assert_eq!(id("Alt+Left"), Some("caret.wordLeft")); + assert_eq!(id("Alt+Right"), Some("caret.wordRight")); + assert_eq!(id("Mod+Left"), Some(cmd::mac_or("caret.home", "caret.wordLeft"))); + assert_eq!(id("Mod+Right"), Some(cmd::mac_or("caret.end", "caret.wordRight"))); assert_eq!(id("Shift+F1"), Some("styles.inspector")); assert_eq!(id("Alt+F8"), Some("tools.macros")); assert_eq!(id("Mod+Shift+X"), Some("format.strikethrough")); diff --git a/docs/typing-parity.md b/docs/typing-parity.md index 7abd05d4d..f4de6ecab 100644 --- a/docs/typing-parity.md +++ b/docs/typing-parity.md @@ -74,6 +74,7 @@ Windows/Linux keys are given first, macOS second. | Alt+Shift+Left / Right (⌃⇧← / ⌃⇧→) | Promote / Demote: a heading goes up or down one level (Heading 1 and Heading 9 are the ends); promoted body text becomes a heading at the level of the heading before it; a list item changes level like Shift+Tab / Tab | `alt_shift_left_and_right_promote_and_demote_headings_and_list_items` | | Outline view: Alt+Shift+Up / Down, Left / Right | as above, but a collapsed heading takes what is hidden under it along: moving passes whole shown paragraphs with their hidden content; promote/demote shifts the hidden subheadings too | `outline_collapse_hides_descendants_and_expand_restores` | | Outline view: Alt+Shift+Plus / Minus, Alt+Shift+1–9, Alt+Shift+A | Expand / Collapse the heading (one level at a time); Show Level 1–9; show all levels. The caret never stays in hidden text: it moves on to the next shown paragraph | `outline_show_level_filters_paragraphs` | +| Ctrl+Left / Right (⌥← / ⌥→); Home / End (⌘← / ⌘→) | one word left / right; the start / end of the line. On macOS ⌘← / ⌘→ go to the line's ends, as in every Mac text field, and ⌥ moves by word (#954); Shift extends the selection | `word_default_shortcuts_resolve` | | Shift+F5 | Go Back: the caret cycles through the last four places edited, most recent first | `shift_f5_goes_back_through_the_last_edits` | | Alt+X (no macOS default) | the hex code before the caret (up to six digits, `U+` optional) or the selected code becomes its character; otherwise the character before the caret becomes its code (`é` ↔ `00E9`); one undo step | `alt_x_toggles_between_a_character_and_its_code` | | Shift+Backspace / Shift+Delete | Shift+Backspace erases like Backspace (#707). Shift+Delete cuts the selection to the clipboard and does nothing without one (Word for Windows, observed 2026-10-14). Windows delivers Shift+Delete as a Cut; macOS and Linux deliver the key, which did nothing (#1669) | `shift_delete_cuts_the_selection_and_shift_backspace_erases` | @@ -205,6 +206,7 @@ Word. | 2026-10-14 | trivial | Track Changes: empty tables are removed under tracking; Delete Columns leaves merged cells that keep a column; repeated Change Case reads only live text (#1329 review) | | 2026-10-14 | trivial | Track Changes: table deletes mark the cells' text deleted and keep the table; Change Case is a tracked delete+insert (differs from Word's hidden formatting revision); Unlink Fields stays untracked; all three refuse locked content controls (#1329) | | 2026-10-12 | trivial | Track Changes: Accept All after a tracked delete from a paragraph end keeps the next paragraph; Reject All after typing into an empty paragraph keeps it (#864) | +| 2026-10-12 | trivial | macOS: ⌘← / ⌘→ go to the line's start and end instead of moving by word (#954) | | 2026-10-12 | minor | AutoCorrect: Correct TWo INitial CApitals, accidental cAPS LOCK, capitalize first letter of table cells (#544) | | 2026-10-12 | trivial | Editing keys: Shift+Right at the end of a cell's text selects the cell first, then the next cell with it (#971) | | 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) |