authorclover caruso <git@paperclover.net> 2026-10-15 14:14:45-07:00
committerclover caruso <git@paperclover.net> 2026-10-15 14:23:02-07:00
logc780bc064c84cc299b01189b7b225c335dd00da7
tree5bc95f8d3a11e329842d46ce50d00321bf3c2e3f
parent1dcb0343511f2ace03aa4f29c9c7a1b39716096b
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

feat: move lines and list items with Alt+Up and Alt+Down, as in VS Code

Alt+Up and Alt+Down now move the selected paragraphs, list items included, past their sibling on Windows and Linux, alongside OneNote's Alt+Shift+Up and Down. On macOS, Option+Up and Down do the same, as VS Code binds them there, taking over from plain Option's jump to the paragraph start or end; Option-Shift still extends the selection to the paragraph ends, and Option-Command keeps moving paragraphs as OneNote for Mac does. Fixes #151 Assisted-by: claude-opus-5.5

1 files changed, 54 insertions(+), 13 deletions(-)

crates/draw/src/edit.rs+54-13
...@@ -88,7 +88,8 @@ pub enum Command {...@@ -88,7 +88,8 @@ pub enum Command {
88 /// Moves the caret a page and the view with it, extending the selection with Shift.88 /// Moves the caret a page and the view with it, extending the selection with Shift.
89 MovePage { up: bool },89 MovePage { up: bool },
90 /// Moves the selected paragraphs past their sibling above or below: OneNote's Alt+Shift+Up90 /// Moves the selected paragraphs past their sibling above or below: OneNote's Alt+Shift+Up
91 /// and Down, Option-Command on macOS as OneNote for Mac binds them.91 /// and Down, Option-Command on macOS as OneNote for Mac binds them, and VS Code's Alt or
92 /// Option with Up and Down.
92 MoveParagraphs { up: bool },93 MoveParagraphs { up: bool },
93}94}
9495
...@@ -289,7 +290,7 @@ fn mac_command(key: &Key, modifiers: Modifiers) -> Option<Command> {...@@ -289,7 +290,7 @@ fn mac_command(key: &Key, modifiers: Modifiers) -> Option<Command> {
289 };290 };
290 match key {291 match key {
291 Key::Named(arrow @ (NamedKey::ArrowUp | NamedKey::ArrowDown))292 Key::Named(arrow @ (NamedKey::ArrowUp | NamedKey::ArrowDown))
292 if command && option && !shift && !control =>293 if option && !shift && !control =>
293 {294 {
294 Some(Command::MoveParagraphs {295 Some(Command::MoveParagraphs {
295 up: *arrow == NamedKey::ArrowUp,296 up: *arrow == NamedKey::ArrowUp,
...@@ -351,14 +352,11 @@ fn mac_command(key: &Key, modifiers: Modifiers) -> Option<Command> {...@@ -351,14 +352,11 @@ fn mac_command(key: &Key, modifiers: Modifiers) -> Option<Command> {
351/// and End reaches the document's ends; Control with Up and Down moves by paragraphs.352/// and End reaches the document's ends; Control with Up and Down moves by paragraphs.
352fn pc_command(key: &Key, modifiers: Modifiers) -> Option<Command> {353fn pc_command(key: &Key, modifiers: Modifiers) -> Option<Command> {
353 let Modifiers {354 let Modifiers {
354 shift,355 control, option, ..
355 control,
356 option,
357 ..
358 } = modifiers;356 } = modifiers;
359 if option {357 if option {
360 return match key {358 return match key {
361 _ if !shift || control => None,359 _ if control => None,
362 Key::Named(NamedKey::ArrowUp) => Some(Command::MoveParagraphs { up: true }),360 Key::Named(NamedKey::ArrowUp) => Some(Command::MoveParagraphs { up: true }),
363 Key::Named(NamedKey::ArrowDown) => Some(Command::MoveParagraphs { up: false }),361 Key::Named(NamedKey::ArrowDown) => Some(Command::MoveParagraphs { up: false }),
364 _ => None,362 _ => None,
...@@ -605,11 +603,6 @@ mod tests {...@@ -605,11 +603,6 @@ mod tests {
605 with(|m| (m.command, m.option) = (true, true)),603 with(|m| (m.command, m.option) = (true, true)),
606 Some(Command::Move(Movement::LineEnd)),604 Some(Command::Move(Movement::LineEnd)),
607 ),605 ),
608 (
609 named(NamedKey::ArrowUp),
610 with(|m| m.option = true),
611 Some(Command::Move(Movement::ParagraphStart)),
612 ),
613 (named(NamedKey::Home), Modifiers::default(), None),606 (named(NamedKey::Home), Modifiers::default(), None),
614 (607 (
615 named(NamedKey::End),608 named(NamedKey::End),
...@@ -652,11 +645,36 @@ mod tests {...@@ -652,11 +645,36 @@ mod tests {
652 with(|m| (m.command, m.option) = (true, true)),645 with(|m| (m.command, m.option) = (true, true)),
653 Some(Command::MoveParagraphs { up: false }),646 Some(Command::MoveParagraphs { up: false }),
654 ),647 ),
648 (
649 named(NamedKey::ArrowUp),
650 with(|m| (m.command, m.option) = (true, true)),
651 Some(Command::MoveParagraphs { up: true }),
652 ),
653 (
654 named(NamedKey::ArrowUp),
655 with(|m| m.option = true),
656 Some(Command::MoveParagraphs { up: true }),
657 ),
658 (
659 named(NamedKey::ArrowDown),
660 with(|m| m.option = true),
661 Some(Command::MoveParagraphs { up: false }),
662 ),
655 (663 (
656 named(NamedKey::ArrowUp),664 named(NamedKey::ArrowUp),
657 with(|m| (m.shift, m.option) = (true, true)),665 with(|m| (m.shift, m.option) = (true, true)),
658 Some(Command::Move(Movement::ParagraphStart)),666 Some(Command::Move(Movement::ParagraphStart)),
659 ),667 ),
668 (
669 named(NamedKey::ArrowDown),
670 with(|m| (m.shift, m.option) = (true, true)),
671 Some(Command::Move(Movement::ParagraphEnd)),
672 ),
673 (
674 named(NamedKey::ArrowUp),
675 with(|m| (m.control, m.option) = (true, true)),
676 Some(Command::Move(Movement::ParagraphStart)),
677 ),
660 ];678 ];
661 for (key, modifiers, command) in cases {679 for (key, modifiers, command) in cases {
662 assert_eq!(680 assert_eq!(
...@@ -688,7 +706,30 @@ mod tests {...@@ -688,7 +706,30 @@ mod tests {
688 alt_shift,706 alt_shift,
689 Some(Command::MoveParagraphs { up: true }),707 Some(Command::MoveParagraphs { up: true }),
690 ),708 ),
691 (named(NamedKey::ArrowUp), alt, None),709 (
710 named(NamedKey::ArrowDown),
711 alt_shift,
712 Some(Command::MoveParagraphs { up: false }),
713 ),
714 (
715 named(NamedKey::ArrowUp),
716 alt,
717 Some(Command::MoveParagraphs { up: true }),
718 ),
719 (
720 named(NamedKey::ArrowDown),
721 alt,
722 Some(Command::MoveParagraphs { up: false }),
723 ),
724 (
725 named(NamedKey::ArrowUp),
726 Modifiers {
727 control: true,
728 command: true,
729 ..alt
730 },
731 None,
732 ),
692 (733 (
693 named(NamedKey::ArrowLeft),734 named(NamedKey::ArrowLeft),
694 control,735 control,