From e3b7604558896fe8f725fc1025f86e5d0bb17fda Mon Sep 17 00:00:00 2001 From: schlich Date: Thu, 12 Mar 2026 12:08:44 +0000 Subject: [PATCH 01/20] helix: add basic mode switching --- examples/helix.rs | 2 +- src/edit_mode/helix.rs | 108 ++++++++++++++++++++++++++++++----------- src/engine.rs | 4 +- 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/examples/helix.rs b/examples/helix.rs index 6c6bd393..224917da 100644 --- a/examples/helix.rs +++ b/examples/helix.rs @@ -11,7 +11,7 @@ fn main() -> io::Result<()> { println!("Helix edit mode demo:\nAbort with Ctrl-C"); let prompt = DefaultPrompt::default(); - let mut line_editor = Reedline::create().with_edit_mode(Box::new(Helix)); + let mut line_editor = Reedline::create().with_edit_mode(Box::new(Helix::default())); loop { let sig = line_editor.read_line(&prompt)?; diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index ec03dfc8..8703e7a5 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -1,64 +1,114 @@ use crate::{ - enums::{EventStatus, ReedlineEvent, ReedlineRawEvent}, + edit_mode::EditMode, + enums::{ReedlineEvent, ReedlineRawEvent}, PromptEditMode, PromptViMode, }; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; -use super::EditMode; +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum HelixMode { + #[default] + Insert, + Normal, +} /// A minimal custom edit mode example for Helix-style integrations. #[derive(Default)] -pub struct Helix; +pub struct Helix { + mode: HelixMode, +} + +impl Helix { + #[cfg(test)] + pub(crate) fn insert() -> Self { + Self { + mode: HelixMode::Insert, + } + } + + #[cfg(test)] + pub(crate) fn normal() -> Self { + Self { + mode: HelixMode::Normal, + } + } +} impl EditMode for Helix { fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { - match Event::from(event) { + match event.into() { Event::Key(KeyEvent { - code: KeyCode::Char('c'), - modifiers: KeyModifiers::CONTROL, - .. - }) => ReedlineEvent::CtrlC, + code, modifiers, .. + }) => match (self.mode, modifiers, code) { + (_, KeyModifiers::CONTROL, KeyCode::Char('c')) => ReedlineEvent::CtrlC, + (HelixMode::Insert, _, KeyCode::Esc) => { + self.mode = HelixMode::Normal; + ReedlineEvent::Repaint + } + (HelixMode::Normal, _, KeyCode::Char('i')) => { + self.mode = HelixMode::Insert; + ReedlineEvent::Repaint + } + _ => ReedlineEvent::None, + }, _ => ReedlineEvent::None, } } fn edit_mode(&self) -> PromptEditMode { - PromptEditMode::Vi(PromptViMode::Normal) - } - - fn handle_mode_specific_event(&mut self, _event: ReedlineEvent) -> EventStatus { - EventStatus::Inapplicable + match self.mode { + HelixMode::Insert => PromptEditMode::Vi(PromptViMode::Insert), + HelixMode::Normal => PromptEditMode::Vi(PromptViMode::Normal), + } } } #[cfg(test)] mod tests { use super::*; - use crate::PromptViMode; + use crossterm::event::{KeyEventKind, KeyEventState}; - #[test] - fn helix_edit_mode_defaults_to_normal_mode() { - let helix_mode = Helix; + fn key_press(code: KeyCode, modifiers: KeyModifiers) -> ReedlineRawEvent { + Event::Key(KeyEvent { + code, + modifiers, + kind: KeyEventKind::Press, + state: KeyEventState::NONE, + }) + .try_into() + .unwrap() + } - let edit_mode = helix_mode.edit_mode(); + #[test] + fn helix_editor_defaults_to_insert_mode() { + let helix_editor = Helix::default(); - assert!(matches!( - edit_mode, - PromptEditMode::Vi(PromptViMode::Normal) - )); + assert_eq!(helix_editor.mode, HelixMode::Insert); } #[test] - fn helix_edit_mode_parses_ctrl_c_event() { - let mut helix_mode = Helix; - let ctrl_c_raw_event = ReedlineRawEvent::try_from(Event::Key(KeyEvent::new( - KeyCode::Char('c'), - KeyModifiers::CONTROL, - ))); + fn normal_mode_parses_ctrl_c_event() { + let mut helix_mode = Helix::normal(); assert_eq!( - helix_mode.parse_event(ctrl_c_raw_event.unwrap()), + helix_mode.parse_event(key_press(KeyCode::Char('c'), KeyModifiers::CONTROL)), ReedlineEvent::CtrlC ); } + + #[test] + fn pressing_esc_in_insert_mode_switches_to_normal() { + let mut helix_mode = Helix::insert(); + helix_mode.parse_event(key_press(KeyCode::Esc, KeyModifiers::NONE)); + + assert_eq!(helix_mode.mode, HelixMode::Normal); + } + + #[test] + fn pressing_i_in_normal_mode_switches_to_insert() { + let mut helix_mode = Helix::normal(); + helix_mode.parse_event(key_press(KeyCode::Char('i'), KeyModifiers::NONE)); + + assert_eq!(helix_mode.mode, HelixMode::Insert); + } } diff --git a/src/engine.rs b/src/engine.rs index 0364acf4..457f726e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2320,11 +2320,11 @@ mod tests { fn with_edit_mode_builder_accepts_custom_helix_mode() { use crate::PromptViMode; - let reedline = Reedline::create().with_edit_mode(Box::new(crate::Helix)); + let reedline = Reedline::create().with_edit_mode(Box::new(crate::Helix::insert())); assert!(matches!( reedline.prompt_edit_mode(), - PromptEditMode::Vi(PromptViMode::Normal) + PromptEditMode::Vi(PromptViMode::Insert) )); } From 886bc14a673b51569caaf84f1052241041ceff54 Mon Sep 17 00:00:00 2001 From: schlich Date: Fri, 13 Mar 2026 03:50:01 +0000 Subject: [PATCH 02/20] helix-mode: Implement ModalKit infrastructure for basic mode switching --- Cargo.lock | 1097 ++++++++++++++++++++++++++++------------ Cargo.toml | 5 +- src/edit_mode/helix.rs | 260 ++++++++-- src/engine.rs | 2 +- src/enums.rs | 12 + 5 files changed, 999 insertions(+), 377 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21f2c957..22cf8b91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -26,6 +20,18 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + [[package]] name = "arboard" version = "3.6.1" @@ -39,43 +45,37 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "cc" -version = "1.2.37" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -83,21 +83,20 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.4", + "windows-link", ] [[package]] @@ -111,18 +110,15 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" -dependencies = [ - "unicode-segmentation", -] +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crossbeam" @@ -148,9 +144,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -167,33 +163,31 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" -version = "0.29.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.9.4", + "bitflags", "crossterm_winapi", - "derive_more", - "document-features", "libc", "mio", "parking_lot", - "rustix 1.1.2", + "rustix 0.38.44", "serde", "signal-hook", "signal-hook-mio", @@ -211,22 +205,14 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", ] @@ -238,40 +224,72 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.9.4", + "bitflags", "objc2", ] [[package]] -name = "document-features" -version = "0.2.12" +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "editor-types" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +checksum = "a9e9e99679670f67825fcd24a23cb4eb655a0f92c82bd4d1c1a1357c0cd71e87" dependencies = [ - "litrs", + "bitflags", + "editor-types-macros", + "keybindings", + "regex", ] [[package]] -name = "downcast-rs" -version = "1.2.0" +name = "editor-types-macros" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "42680de76cf91f231abd90cc623750d39077f7d2fadb7962325fb082871f4c66" +dependencies = [ + "editor-types-parser", + "nom 7.1.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "editor-types-parser" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac4b91fe830fbbe0a60c37ba0264b6e9ffc70e3664c028234dac59e79299ad4" +dependencies = [ + "nom 7.1.3", + "thiserror 1.0.69", +] [[package]] name = "either" -version = "1.10.0" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "endian-type" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -280,14 +298,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "fallible-iterator" @@ -314,21 +332,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.1.2", - "windows-sys 0.52.0", + "rustix 1.1.4", + "windows-sys 0.59.0", ] [[package]] name = "find-msvc-tools" -version = "0.1.1" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" -version = "0.4.2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" @@ -346,29 +364,34 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", + "wasip3", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" @@ -379,6 +402,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hashlink" version = "0.10.0" @@ -394,22 +423,17 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -423,14 +447,31 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "indexmap" -version = "2.2.6" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "intervaltree" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "270bc34e57047cab801a8c871c124d9dc7132f6473c6401f645524f4e6edd111" +dependencies = [ + "smallvec", ] [[package]] @@ -444,24 +485,42 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "keybindings" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a726307ed87e05155c31329676130e6a237e62dda80211f7e1ed811e47630f" +dependencies = [ + "textwrap", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.177" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libsqlite3-sys" @@ -482,37 +541,30 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litrs" -version = "1.0.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "minimal-lexical" @@ -522,15 +574,45 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ - "hermit-abi", "libc", "log", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "modalkit" +version = "0.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cbb03c35f23ec7d13f7870049803cd8829f5e60b69d38fa98f5e7876de9f34e" +dependencies = [ + "anymap2", + "bitflags", + "crossterm", + "derive_more", + "editor-types", + "intervaltree", + "keybindings", + "nom 7.1.3", + "radix_trie", + "regex", + "ropey", + "thiserror 1.0.69", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", ] [[package]] @@ -543,29 +625,38 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" -version = "0.50.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -576,7 +667,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.9.4", + "bitflags", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -588,7 +679,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags", "dispatch2", "objc2", ] @@ -599,7 +690,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.9.4", + "bitflags", "dispatch2", "objc2", "objc2-core-foundation", @@ -618,7 +709,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.4", + "bitflags", "objc2", "objc2-core-foundation", ] @@ -629,16 +720,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.9.4", + "bitflags", "objc2", "objc2-core-foundation", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "os_pipe" @@ -647,14 +738,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -662,15 +753,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-link", ] [[package]] @@ -681,70 +772,91 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", + "hashbrown 0.15.5", "indexmap", ] [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -756,8 +868,9 @@ dependencies = [ "crossbeam", "crossterm", "fd-lock", - "gethostname", + "gethostname 0.4.3", "itertools", + "modalkit", "nu-ansi-term", "pretty_assertions", "rstest", @@ -767,17 +880,17 @@ dependencies = [ "strip-ansi-escapes", "strum", "tempfile", - "thiserror", + "thiserror 2.0.18", "unicase", "unicode-segmentation", - "unicode-width", + "unicode-width 0.2.2", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -787,9 +900,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -798,9 +911,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -808,6 +921,16 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rstest" version = "0.23.0" @@ -841,7 +964,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.9.4", + "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -864,31 +987,31 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.9.4", + "bitflags", "errno", "libc", - "linux-raw-sys 0.11.0", - "windows-sys 0.52.0", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", ] [[package]] -name = "ryu" -version = "1.0.17" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "scopeguard" @@ -898,24 +1021,34 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -924,13 +1057,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] @@ -941,9 +1076,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -951,9 +1086,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -962,24 +1097,37 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "str_indices" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" [[package]] name = "strip-ansi-escapes" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" dependencies = [ "vte", ] @@ -1007,9 +1155,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1018,31 +1166,62 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.2", - "windows-sys 0.52.0", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.2.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1051,45 +1230,56 @@ dependencies = [ [[package]] name = "tree_magic_mini" -version = "3.2.0" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" dependencies = [ "memchr", - "nom", - "once_cell", + "nom 8.0.0", "petgraph", ] [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "utf8parse" -version = "0.2.1" +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "vcpkg" @@ -1099,69 +1289,55 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vte" -version = "0.11.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", + "memchr", ] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" +name = "wasm-bindgen" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ - "bumpalo", - "log", + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1169,55 +1345,92 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] [[package]] name = "wayland-backend" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.2", + "rustix 1.1.4", "smallvec", "wayland-sys", ] [[package]] name = "wayland-client" -version = "0.31.11" +version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" dependencies = [ - "bitflags 2.9.4", - "rustix 1.1.2", + "bitflags", + "rustix 1.1.4", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-protocols" -version = "0.32.9" +version = "0.32.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7" dependencies = [ - "bitflags 2.9.4", + "bitflags", "wayland-backend", "wayland-client", "wayland-scanner", @@ -1225,11 +1438,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" +checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235" dependencies = [ - "bitflags 2.9.4", + "bitflags", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1238,9 +1451,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.7" +version = "0.31.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" dependencies = [ "proc-macro2", "quick-xml", @@ -1249,9 +1462,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.7" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17" dependencies = [ "pkg-config", ] @@ -1280,29 +1493,88 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-targets 0.52.4", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-targets 0.52.4", + "windows-link", ] [[package]] @@ -1322,17 +1594,35 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -1343,9 +1633,15 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -1355,9 +1651,15 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -1367,9 +1669,27 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -1379,9 +1699,15 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -1391,9 +1717,15 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -1403,9 +1735,15 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -1415,28 +1753,115 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "wl-clipboard-rs" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" dependencies = [ "libc", "log", "os_pipe", - "rustix 0.38.44", - "tempfile", - "thiserror", + "rustix 1.1.4", + "thiserror 2.0.18", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -1446,23 +1871,29 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ - "gethostname", - "rustix 0.38.44", + "gethostname 1.1.0", + "rustix 1.1.4", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index d6d99bf3..fb73145d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "MIT" name = "reedline" repository = "https://github.com/nushell/reedline" -rust-version = "1.63.0" +rust-version = "1.74.0" version = "0.46.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -21,9 +21,10 @@ chrono = { version = "0.4.19", default-features = false, features = [ "serde", ] } crossbeam = { version = "0.8.2", optional = true } -crossterm = { version = "0.29.0", features = ["serde"] } +crossterm = { version = "0.28.1", features = ["serde"] } fd-lock = "4.0.2" itertools = "0.13.0" +modalkit = "0.0.24" nu-ansi-term = "0.50.0" rusqlite = { version = "0.37.0", optional = true } serde = { version = "1.0", features = ["derive"] } diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 8703e7a5..a9c9e9fd 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -1,62 +1,189 @@ use crate::{ edit_mode::EditMode, - enums::{ReedlineEvent, ReedlineRawEvent}, + enums::{EditCommand, ReedlineEvent, ReedlineRawEvent}, PromptEditMode, PromptViMode, }; -use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use modalkit::{ + key::TerminalKey, + keybindings::{ + BindingMachine, EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, EmptyKeyState, + InputBindings, InputKey, ModalMachine, Mode, ModeKeys, + }, +}; -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] enum HelixMode { #[default] Insert, Normal, } -/// A minimal custom edit mode example for Helix-style integrations. +impl Mode for HelixMode {} + +impl ModeKeys for HelixMode { + fn unmapped( + &self, + key: &TerminalKey, + _: &mut EmptyKeyState, + ) -> (Vec, Option) { + match self { + HelixMode::Normal => (vec![], None), + HelixMode::Insert => { + if let Some(c) = key.get_char() { + return (vec![HelixAction::Type(c)], None); + } + + (vec![], None) + } + } + } +} + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +enum HelixAction { + Type(char), + MoveCharRight, + MoveCharLeft, + #[default] + NoOp, +} + +type HelixStep = (Option, Option); + +type HelixEdgePath = EdgePath; + +type HelixMachine = ModalMachine; + #[derive(Default)] -pub struct Helix { - mode: HelixMode, +struct HelixBindings; + +impl HelixBindings { + fn add_single_keypress_mapping( + machine: &mut HelixMachine, + mode: HelixMode, + code: KeyCode, + step: HelixStep, + ) { + let path: &HelixEdgePath = &[(EdgeRepeat::Once, EdgeEvent::Key(TerminalKey::from(code)))]; + machine.add_mapping(mode, path, &step); + } } -impl Helix { - #[cfg(test)] - pub(crate) fn insert() -> Self { - Self { - mode: HelixMode::Insert, +impl InputBindings for HelixBindings { + fn setup(&self, machine: &mut HelixMachine) { + Self::add_single_keypress_mapping( + machine, + HelixMode::Insert, + KeyCode::Esc, + (None, Some(HelixMode::Normal)), + ); + Self::add_single_keypress_mapping( + machine, + HelixMode::Normal, + KeyCode::Char('i'), + (None, Some(HelixMode::Insert)), + ); + for code in [KeyCode::Char('h'), KeyCode::Left] { + Self::add_single_keypress_mapping( + machine, + HelixMode::Normal, + code, + (Some(HelixAction::MoveCharLeft), None), + ); + } + for code in [KeyCode::Char('l'), KeyCode::Right] { + Self::add_single_keypress_mapping( + machine, + HelixMode::Normal, + code, + (Some(HelixAction::MoveCharRight), None), + ); } } +} + +/// A minimal custom edit mode example for Helix-style integrations. +pub struct Helix { + machine: HelixMachine, +} + +impl std::fmt::Debug for Helix { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Helix") + .field("mode", &self.machine.mode()) + .finish_non_exhaustive() + } +} - #[cfg(test)] - pub(crate) fn normal() -> Self { - Self { - mode: HelixMode::Normal, +impl Default for Helix { + fn default() -> Self { + Self::new(PromptViMode::Insert) + } +} + +impl Helix { + /// Creates a Helix editor with the requested initial mode. + pub fn new(initial_mode: PromptViMode) -> Self { + let mut machine = HelixMachine::from_bindings::(); + + if matches!(initial_mode, PromptViMode::Normal) { + machine.input_key(TerminalKey::from(KeyCode::Esc)); + let _ = machine.pop(); } + Self { machine } } } impl EditMode for Helix { fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { - match event.into() { - Event::Key(KeyEvent { - code, modifiers, .. - }) => match (self.mode, modifiers, code) { - (_, KeyModifiers::CONTROL, KeyCode::Char('c')) => ReedlineEvent::CtrlC, - (HelixMode::Insert, _, KeyCode::Esc) => { - self.mode = HelixMode::Normal; - ReedlineEvent::Repaint - } - (HelixMode::Normal, _, KeyCode::Char('i')) => { - self.mode = HelixMode::Insert; + let Ok(key_event) = KeyEvent::try_from(event) else { + return ReedlineEvent::None; + }; + + if matches!( + &key_event, + KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + } + ) { + return ReedlineEvent::CtrlC; + } + + let previous_mode = self.machine.mode(); + self.machine.input_key(key_event.into()); + let mode_changed = self.machine.mode() != previous_mode; + + let Some((action, _ctx)) = self.machine.pop() else { + return if mode_changed { + ReedlineEvent::Repaint + } else { + ReedlineEvent::None + }; + }; + + match action { + HelixAction::Type(c) => ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)]), + HelixAction::MoveCharLeft => { + ReedlineEvent::Edit(vec![EditCommand::MoveLeft { select: false }]) + } + HelixAction::MoveCharRight => { + ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false }]) + } + HelixAction::NoOp => { + if mode_changed { ReedlineEvent::Repaint + } else { + ReedlineEvent::None } - _ => ReedlineEvent::None, - }, - _ => ReedlineEvent::None, + } } } fn edit_mode(&self) -> PromptEditMode { - match self.mode { + match self.machine.mode() { HelixMode::Insert => PromptEditMode::Vi(PromptViMode::Insert), HelixMode::Normal => PromptEditMode::Vi(PromptViMode::Normal), } @@ -66,7 +193,8 @@ impl EditMode for Helix { #[cfg(test)] mod tests { use super::*; - use crossterm::event::{KeyEventKind, KeyEventState}; + use crossterm::event::{Event, KeyEventKind, KeyEventState}; + use rstest::rstest; fn key_press(code: KeyCode, modifiers: KeyModifiers) -> ReedlineRawEvent { Event::Key(KeyEvent { @@ -76,19 +204,22 @@ mod tests { state: KeyEventState::NONE, }) .try_into() - .unwrap() + .expect("valid crossterm key event") } #[test] fn helix_editor_defaults_to_insert_mode() { let helix_editor = Helix::default(); - assert_eq!(helix_editor.mode, HelixMode::Insert); + assert!(matches!( + helix_editor.edit_mode(), + PromptEditMode::Vi(PromptViMode::Insert) + )); } #[test] - fn normal_mode_parses_ctrl_c_event() { - let mut helix_mode = Helix::normal(); + fn ctrl_c_maps_to_interrupt_event() { + let mut helix_mode = Helix::default(); assert_eq!( helix_mode.parse_event(key_press(KeyCode::Char('c'), KeyModifiers::CONTROL)), @@ -98,17 +229,64 @@ mod tests { #[test] fn pressing_esc_in_insert_mode_switches_to_normal() { - let mut helix_mode = Helix::insert(); - helix_mode.parse_event(key_press(KeyCode::Esc, KeyModifiers::NONE)); + let mut helix_mode = Helix::new(PromptViMode::Insert); + + assert_eq!( + helix_mode.parse_event(key_press(KeyCode::Esc, KeyModifiers::NONE)), + ReedlineEvent::Repaint + ); - assert_eq!(helix_mode.mode, HelixMode::Normal); + assert!(matches!( + helix_mode.edit_mode(), + PromptEditMode::Vi(PromptViMode::Normal) + )); } #[test] fn pressing_i_in_normal_mode_switches_to_insert() { - let mut helix_mode = Helix::normal(); - helix_mode.parse_event(key_press(KeyCode::Char('i'), KeyModifiers::NONE)); + let mut helix_mode = Helix::new(PromptViMode::Normal); - assert_eq!(helix_mode.mode, HelixMode::Insert); + assert_eq!( + helix_mode.parse_event(key_press(KeyCode::Char('i'), KeyModifiers::NONE)), + ReedlineEvent::Repaint + ); + assert!(matches!( + helix_mode.edit_mode(), + PromptEditMode::Vi(PromptViMode::Insert) + )); + } + + #[test] + fn typing_in_insert_mode_produces_insert_char_event() { + let mut helix_mode = Helix::new(PromptViMode::Insert); + + assert_eq!( + helix_mode.parse_event(key_press(KeyCode::Char('a'), KeyModifiers::NONE)), + ReedlineEvent::Edit(vec![EditCommand::InsertChar('a')]) + ); + } + + #[rstest] + #[case(KeyCode::Char('h'))] + #[case(KeyCode::Left)] + fn pressing_left_key_or_h_in_normal_mode_moves_cursor_left(#[case] key_code: KeyCode) { + let mut helix_mode = Helix::new(PromptViMode::Normal); + + assert_eq!( + helix_mode.parse_event(key_press(key_code, KeyModifiers::NONE)), + ReedlineEvent::Edit(vec![EditCommand::MoveLeft { select: false }]) + ); + } + + #[rstest] + #[case(KeyCode::Char('l'))] + #[case(KeyCode::Right)] + fn pressing_right_key_or_l_in_normal_mode_moves_cursor_right(#[case] key_code: KeyCode) { + let mut helix_mode = Helix::new(PromptViMode::Normal); + + assert_eq!( + helix_mode.parse_event(key_press(key_code, KeyModifiers::NONE)), + ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false }]) + ); } } diff --git a/src/engine.rs b/src/engine.rs index 457f726e..e178bf32 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2320,7 +2320,7 @@ mod tests { fn with_edit_mode_builder_accepts_custom_helix_mode() { use crate::PromptViMode; - let reedline = Reedline::create().with_edit_mode(Box::new(crate::Helix::insert())); + let reedline = Reedline::create().with_edit_mode(Box::new(crate::Helix::default())); assert!(matches!( reedline.prompt_edit_mode(), diff --git a/src/enums.rs b/src/enums.rs index 061fb75c..21e35803 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1042,3 +1042,15 @@ impl From for Event { event.0 } } + +#[cfg(feature = "helix")] +impl TryFrom for KeyEvent { + type Error = ReedlineRawEvent; + + fn try_from(event: ReedlineRawEvent) -> Result { + match event.0 { + Event::Key(key_event) => Ok(key_event), + other => Err(ReedlineRawEvent(other)), + } + } +} From 79f17af0a1b4745e4382dfa59cae6f2c02b37bd6 Mon Sep 17 00:00:00 2001 From: schlich Date: Fri, 13 Mar 2026 18:01:33 +0000 Subject: [PATCH 03/20] new clippy fixes from upgrade --- examples/demo.rs | 6 ++---- src/core_editor/line_buffer.rs | 2 +- src/painting/utils.rs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index 83c6883c..93818d99 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -39,7 +39,7 @@ fn main() -> reedline::Result<()> { history_session_id, Some(chrono::Utc::now()), ) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + .map_err(std::io::Error::other)?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); @@ -195,9 +195,7 @@ fn main() -> reedline::Result<()> { } if buffer.trim() == "clear-history" { let hstry = Box::new(line_editor.history_mut()); - hstry - .clear() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + hstry.clear().map_err(std::io::Error::other)?; continue; } println!("Our buffer: {buffer}"); diff --git a/src/core_editor/line_buffer.rs b/src/core_editor/line_buffer.rs index 416e13fe..7d34ec6c 100644 --- a/src/core_editor/line_buffer.rs +++ b/src/core_editor/line_buffer.rs @@ -323,7 +323,7 @@ impl LineBuffer { fn at_end_of_line_with_preceding_whitespace(&self) -> bool { !self.is_empty() // No point checking if empty && self.insertion_point == self.lines.len() - && self.lines.chars().last().map_or(false, |c| c.is_whitespace()) + && self.lines.chars().last().is_some_and(|c| c.is_whitespace()) } /// Cursor position at the end of the current whitespace block. diff --git a/src/painting/utils.rs b/src/painting/utils.rs index 0c9d8ed5..4649197a 100644 --- a/src/painting/utils.rs +++ b/src/painting/utils.rs @@ -59,7 +59,7 @@ pub(crate) fn estimate_single_line_wraps(line: &str, terminal_columns: u16) -> u let terminal_columns: usize = terminal_columns.into(); // integer ceiling rounding division for positive divisors - let estimated_line_count = (estimated_width + terminal_columns - 1) / terminal_columns; + let estimated_line_count = estimated_width.div_ceil(terminal_columns); // Any wrapping will add to our overall line count estimated_line_count.saturating_sub(1) From a3ad4ac503eb24705117a3a9ca324d71111d5dd7 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Sat, 14 Mar 2026 16:43:34 -0500 Subject: [PATCH 04/20] avoid modalkit::key::TerminalKey --- Cargo.lock | 78 ++++++++++++++++++++++++++++++++---- Cargo.toml | 2 +- src/edit_mode/helix.rs | 89 +++++++++++++++++++++++++++++++++++------- 3 files changed, 147 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22cf8b91..56e78c8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "cc" -version = "1.2.56" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "shlex", @@ -114,6 +114,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -184,10 +193,28 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags", "crossterm_winapi", - "libc", "mio", "parking_lot", "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "derive_more 2.1.1", + "document-features", + "libc", + "mio", + "parking_lot", + "rustix 1.1.4", "serde", "signal-hook", "signal-hook-mio", @@ -209,7 +236,29 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case", + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -232,6 +281,15 @@ dependencies = [ "objc2", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "downcast-rs" version = "1.2.1" @@ -545,6 +603,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -592,8 +656,8 @@ checksum = "8cbb03c35f23ec7d13f7870049803cd8829f5e60b69d38fa98f5e7876de9f34e" dependencies = [ "anymap2", "bitflags", - "crossterm", - "derive_more", + "crossterm 0.28.1", + "derive_more 0.99.20", "editor-types", "intervaltree", "keybindings", @@ -866,7 +930,7 @@ dependencies = [ "arboard", "chrono", "crossbeam", - "crossterm", + "crossterm 0.29.0", "fd-lock", "gethostname 0.4.3", "itertools", diff --git a/Cargo.toml b/Cargo.toml index fb73145d..3d4e0ba5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ chrono = { version = "0.4.19", default-features = false, features = [ "serde", ] } crossbeam = { version = "0.8.2", optional = true } -crossterm = { version = "0.28.1", features = ["serde"] } +crossterm = { version = "0.29.0", features = ["serde"] } fd-lock = "4.0.2" itertools = "0.13.0" modalkit = "0.0.24" diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index a9c9e9fd..3c7652de 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -4,12 +4,9 @@ use crate::{ PromptEditMode, PromptViMode, }; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use modalkit::{ - key::TerminalKey, - keybindings::{ - BindingMachine, EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, EmptyKeyState, - InputBindings, InputKey, ModalMachine, Mode, ModeKeys, - }, +use modalkit::keybindings::{ + BindingMachine, EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, EmptyKeyState, InputBindings, + InputKey, ModalMachine, Mode, ModeKeys, }; #[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] @@ -19,12 +16,73 @@ enum HelixMode { Normal, } +/// A simple `InputKey` implementation around `crossterm` types. +/// +/// This avoids pulling in the `crossterm` types used by `modalkit` (which can be a different +/// version than the one used by `reedline`). +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +struct HelixKey { + code: KeyCode, + modifiers: KeyModifiers, +} + +impl HelixKey { + fn new(mut code: KeyCode, mut modifiers: KeyModifiers) -> Self { + if let KeyCode::Char(ref mut c) = code { + if modifiers.intersects(KeyModifiers::SHIFT | KeyModifiers::CONTROL) { + *c = c.to_ascii_uppercase(); + } else if c.is_ascii_uppercase() { + modifiers.insert(KeyModifiers::SHIFT); + } + + if modifiers == KeyModifiers::SHIFT && *c != ' ' { + modifiers -= KeyModifiers::SHIFT; + } + } + + Self { code, modifiers } + } + + fn from_event(event: KeyEvent) -> Self { + Self::new(event.code, event.modifiers) + } + + fn get_char(&self) -> Option { + if let KeyCode::Char(c) = self.code { + if (self.modifiers - KeyModifiers::SHIFT).is_empty() { + return Some(c); + } + } + + None + } +} + +impl InputKey for HelixKey { + type Error = std::convert::Infallible; + + fn decompose(&mut self) -> Option { + None + } + + fn from_macro_str(mstr: &str) -> Result, Self::Error> { + Ok(mstr + .chars() + .map(|c| HelixKey::new(KeyCode::Char(c), KeyModifiers::NONE)) + .collect()) + } + + fn get_char(&self) -> Option { + self.get_char() + } +} + impl Mode for HelixMode {} -impl ModeKeys for HelixMode { +impl ModeKeys for HelixMode { fn unmapped( &self, - key: &TerminalKey, + key: &HelixKey, _: &mut EmptyKeyState, ) -> (Vec, Option) { match self { @@ -51,9 +109,9 @@ enum HelixAction { type HelixStep = (Option, Option); -type HelixEdgePath = EdgePath; +type HelixEdgePath = EdgePath; -type HelixMachine = ModalMachine; +type HelixMachine = ModalMachine; #[derive(Default)] struct HelixBindings; @@ -65,12 +123,15 @@ impl HelixBindings { code: KeyCode, step: HelixStep, ) { - let path: &HelixEdgePath = &[(EdgeRepeat::Once, EdgeEvent::Key(TerminalKey::from(code)))]; + let path: &HelixEdgePath = &[( + EdgeRepeat::Once, + EdgeEvent::Key(HelixKey::new(code, KeyModifiers::NONE)), + )]; machine.add_mapping(mode, path, &step); } } -impl InputBindings for HelixBindings { +impl InputBindings for HelixBindings { fn setup(&self, machine: &mut HelixMachine) { Self::add_single_keypress_mapping( machine, @@ -128,7 +189,7 @@ impl Helix { let mut machine = HelixMachine::from_bindings::(); if matches!(initial_mode, PromptViMode::Normal) { - machine.input_key(TerminalKey::from(KeyCode::Esc)); + machine.input_key(HelixKey::new(KeyCode::Esc, KeyModifiers::NONE)); let _ = machine.pop(); } Self { machine } @@ -153,7 +214,7 @@ impl EditMode for Helix { } let previous_mode = self.machine.mode(); - self.machine.input_key(key_event.into()); + self.machine.input_key(HelixKey::from_event(key_event)); let mode_changed = self.machine.mode() != previous_mode; let Some((action, _ctx)) = self.machine.pop() else { From 9ba71a24c6bb5f8abf429a4b48a5346ded2f3ec4 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 15:17:10 +0000 Subject: [PATCH 05/20] add 'a' keymap/action for append_mode --- src/edit_mode/helix.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 3c7652de..351f0a76 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -317,6 +317,22 @@ mod tests { )); } + #[test] + fn pressing_a_in_normal_mode_switches_to_insert_with_cursor_after_selection() { + let mut helix_mode = Helix::new(PromptViMode::Normal); + + let event_result = + helix_mode.parse_event(key_press(KeyCode::Char('a'), KeyModifiers::NONE)); + assert!(matches!( + helix_mode.edit_mode(), + PromptEditMode::Vi(PromptViMode::Insert) + )); + assert_eq!( + event_result, + ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false },]) + ); + } + #[test] fn typing_in_insert_mode_produces_insert_char_event() { let mut helix_mode = Helix::new(PromptViMode::Insert); From 9d0aafb26af9b154347a47de04ded1fbcb9edac1 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 19:05:28 +0000 Subject: [PATCH 06/20] refactor mode initialization --- src/edit_mode/helix.rs | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 351f0a76..519e3c4c 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -79,6 +79,15 @@ impl InputKey for HelixKey { impl Mode for HelixMode {} +impl From for HelixMode { + fn from(mode: PromptViMode) -> Self { + match mode { + PromptViMode::Insert => HelixMode::Insert, + PromptViMode::Normal => HelixMode::Normal, + } + } +} + impl ModeKeys for HelixMode { fn unmapped( &self, @@ -161,6 +170,12 @@ impl InputBindings for HelixBindings { (Some(HelixAction::MoveCharRight), None), ); } + Self::add_single_keypress_mapping( + machine, + HelixMode::Normal, + KeyCode::Char('a'), + (Some(HelixAction::MoveCharRight), Some(HelixMode::Insert)), + ); } } @@ -187,13 +202,20 @@ impl Helix { /// Creates a Helix editor with the requested initial mode. pub fn new(initial_mode: PromptViMode) -> Self { let mut machine = HelixMachine::from_bindings::(); + Self::initialize_mode(&mut machine, initial_mode.into()); - if matches!(initial_mode, PromptViMode::Normal) { - machine.input_key(HelixKey::new(KeyCode::Esc, KeyModifiers::NONE)); - let _ = machine.pop(); - } Self { machine } } + + fn initialize_mode(machine: &mut HelixMachine, mode: HelixMode) { + if mode == HelixMode::Insert { + return; + } + + machine.input_key(HelixKey::new(KeyCode::Esc, KeyModifiers::NONE)); + let _ = machine.pop(); + + } } impl EditMode for Helix { @@ -278,6 +300,16 @@ mod tests { )); } + #[test] + fn helix_editor_can_start_in_normal_mode() { + let helix_editor = Helix::new(PromptViMode::Normal); + + assert!(matches!( + helix_editor.edit_mode(), + PromptEditMode::Vi(PromptViMode::Normal) + )); + } + #[test] fn ctrl_c_maps_to_interrupt_event() { let mut helix_mode = Helix::default(); From 9e54024beaf1e210f12efa43c768777bbdda7230 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 19:18:46 +0000 Subject: [PATCH 07/20] refactor parse_event, HelixAction --- src/edit_mode/helix.rs | 85 +++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 519e3c4c..47df7afb 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -116,6 +116,21 @@ enum HelixAction { NoOp, } +impl HelixAction { + fn into_reedline_event(self) -> Option { + match self { + HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), + HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveLeft { select: false }, + ])), + HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveRight { select: false }, + ])), + HelixAction::NoOp => None, + } + } +} + type HelixStep = (Option, Option); type HelixEdgePath = EdgePath; @@ -215,55 +230,63 @@ impl Helix { machine.input_key(HelixKey::new(KeyCode::Esc, KeyModifiers::NONE)); let _ = machine.pop(); + debug_assert_eq!(machine.mode(), mode); } -} -impl EditMode for Helix { - fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { - let Ok(key_event) = KeyEvent::try_from(event) else { - return ReedlineEvent::None; - }; + fn key_event_from_raw(event: ReedlineRawEvent) -> Option { + KeyEvent::try_from(event).ok() + } - if matches!( - &key_event, + fn is_interrupt_event(key_event: &KeyEvent) -> bool { + matches!( + key_event, KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. } - ) { + ) + } + + fn handle_key_event(&mut self, key_event: KeyEvent) -> ReedlineEvent { + if Self::is_interrupt_event(&key_event) { return ReedlineEvent::CtrlC; } + let (action, mode_changed) = self.apply_key_event(key_event); + + action + .and_then(HelixAction::into_reedline_event) + .unwrap_or_else(|| Self::mode_change_event(mode_changed)) + } + + fn apply_key_event(&mut self, key_event: KeyEvent) -> (Option, bool) { let previous_mode = self.machine.mode(); self.machine.input_key(HelixKey::from_event(key_event)); + let mode_changed = self.machine.mode() != previous_mode; + let action = self.machine.pop().map(|(action, _ctx)| action); - let Some((action, _ctx)) = self.machine.pop() else { - return if mode_changed { - ReedlineEvent::Repaint - } else { - ReedlineEvent::None - }; - }; + (action, mode_changed) + } - match action { - HelixAction::Type(c) => ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)]), - HelixAction::MoveCharLeft => { - ReedlineEvent::Edit(vec![EditCommand::MoveLeft { select: false }]) - } - HelixAction::MoveCharRight => { - ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false }]) - } - HelixAction::NoOp => { - if mode_changed { - ReedlineEvent::Repaint - } else { - ReedlineEvent::None - } - } + fn mode_change_event(mode_changed: bool) -> ReedlineEvent { + if mode_changed { + ReedlineEvent::Repaint + } else { + ReedlineEvent::None } } +} + +impl EditMode for Helix { + fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { + let Some(key_event) = Self::key_event_from_raw(event) else { + return ReedlineEvent::None; + }; + + self.handle_key_event(key_event) + } fn edit_mode(&self) -> PromptEditMode { match self.machine.mode() { From feeb12668dad89154c6dcefa395cfc665e3de6e1 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 19:33:55 +0000 Subject: [PATCH 08/20] refactor: remove key normalization logic for now --- src/edit_mode/helix.rs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 47df7afb..59593484 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -27,19 +27,7 @@ struct HelixKey { } impl HelixKey { - fn new(mut code: KeyCode, mut modifiers: KeyModifiers) -> Self { - if let KeyCode::Char(ref mut c) = code { - if modifiers.intersects(KeyModifiers::SHIFT | KeyModifiers::CONTROL) { - *c = c.to_ascii_uppercase(); - } else if c.is_ascii_uppercase() { - modifiers.insert(KeyModifiers::SHIFT); - } - - if modifiers == KeyModifiers::SHIFT && *c != ' ' { - modifiers -= KeyModifiers::SHIFT; - } - } - + fn new(code: KeyCode, modifiers: KeyModifiers) -> Self { Self { code, modifiers } } @@ -47,14 +35,15 @@ impl HelixKey { Self::new(event.code, event.modifiers) } - fn get_char(&self) -> Option { - if let KeyCode::Char(c) = self.code { - if (self.modifiers - KeyModifiers::SHIFT).is_empty() { - return Some(c); - } + fn insertable_char(&self) -> Option { + match self.code { + KeyCode::Char(c) if Self::is_plain_char(self.modifiers) => Some(c), + _ => None, } + } - None + fn is_plain_char(modifiers: KeyModifiers) -> bool { + (modifiers - KeyModifiers::SHIFT).is_empty() } } @@ -73,7 +62,7 @@ impl InputKey for HelixKey { } fn get_char(&self) -> Option { - self.get_char() + self.insertable_char() } } @@ -97,7 +86,7 @@ impl ModeKeys for HelixMode { match self { HelixMode::Normal => (vec![], None), HelixMode::Insert => { - if let Some(c) = key.get_char() { + if let Some(c) = key.insertable_char() { return (vec![HelixAction::Type(c)], None); } From bda3153b862b6ca587f4f896b394a3a1b4407276 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 19:50:14 +0000 Subject: [PATCH 09/20] refactor HelixBindings logic --- src/edit_mode/helix.rs | 57 ++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 59593484..5bcf3bdb 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -142,44 +142,37 @@ impl HelixBindings { )]; machine.add_mapping(mode, path, &step); } + + fn add_bindings(machine: &mut HelixMachine, mode: HelixMode, bindings: &[(KeyCode, HelixStep)]) { + for (code, step) in bindings { + Self::add_single_keypress_mapping(machine, mode, *code, step.clone()); + } + } } impl InputBindings for HelixBindings { fn setup(&self, machine: &mut HelixMachine) { - Self::add_single_keypress_mapping( - machine, - HelixMode::Insert, - KeyCode::Esc, - (None, Some(HelixMode::Normal)), - ); - Self::add_single_keypress_mapping( - machine, - HelixMode::Normal, - KeyCode::Char('i'), - (None, Some(HelixMode::Insert)), - ); - for code in [KeyCode::Char('h'), KeyCode::Left] { - Self::add_single_keypress_mapping( - machine, - HelixMode::Normal, - code, + let insert_bindings = [(KeyCode::Esc, (None, Some(HelixMode::Normal)))]; + let normal_bindings = [ + (KeyCode::Char('i'), (None, Some(HelixMode::Insert))), + ( + KeyCode::Char('h'), (Some(HelixAction::MoveCharLeft), None), - ); - } - for code in [KeyCode::Char('l'), KeyCode::Right] { - Self::add_single_keypress_mapping( - machine, - HelixMode::Normal, - code, + ), + (KeyCode::Left, (Some(HelixAction::MoveCharLeft), None)), + ( + KeyCode::Char('l'), (Some(HelixAction::MoveCharRight), None), - ); - } - Self::add_single_keypress_mapping( - machine, - HelixMode::Normal, - KeyCode::Char('a'), - (Some(HelixAction::MoveCharRight), Some(HelixMode::Insert)), - ); + ), + (KeyCode::Right, (Some(HelixAction::MoveCharRight), None)), + ( + KeyCode::Char('a'), + (Some(HelixAction::MoveCharRight), Some(HelixMode::Insert)), + ), + ]; + + Self::add_bindings(machine, HelixMode::Insert, &insert_bindings); + Self::add_bindings(machine, HelixMode::Normal, &normal_bindings); } } From 64538bd331456f7736c895531e78b1155aafefc9 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 20:03:23 +0000 Subject: [PATCH 10/20] refactor HelixKey conversion to use From trait --- src/edit_mode/helix.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 5bcf3bdb..597f2c97 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -31,10 +31,6 @@ impl HelixKey { Self { code, modifiers } } - fn from_event(event: KeyEvent) -> Self { - Self::new(event.code, event.modifiers) - } - fn insertable_char(&self) -> Option { match self.code { KeyCode::Char(c) if Self::is_plain_char(self.modifiers) => Some(c), @@ -47,6 +43,12 @@ impl HelixKey { } } +impl From for HelixKey { + fn from(event: KeyEvent) -> Self { + Self::new(event.code, event.modifiers) + } +} + impl InputKey for HelixKey { type Error = std::convert::Infallible; @@ -244,7 +246,7 @@ impl Helix { fn apply_key_event(&mut self, key_event: KeyEvent) -> (Option, bool) { let previous_mode = self.machine.mode(); - self.machine.input_key(HelixKey::from_event(key_event)); + self.machine.input_key(key_event.into()); let mode_changed = self.machine.mode() != previous_mode; let action = self.machine.pop().map(|(action, _ctx)| action); From 01288467cbe08ccc5132ebe48dbff73a0a715bd0 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 20:11:00 +0000 Subject: [PATCH 11/20] remove more key normalization logic --- src/edit_mode/helix.rs | 79 ++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix.rs index 597f2c97..12e0d59c 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix.rs @@ -9,12 +9,7 @@ use modalkit::keybindings::{ InputKey, ModalMachine, Mode, ModeKeys, }; -#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] -enum HelixMode { - #[default] - Insert, - Normal, -} + /// A simple `InputKey` implementation around `crossterm` types. /// @@ -30,17 +25,6 @@ impl HelixKey { fn new(code: KeyCode, modifiers: KeyModifiers) -> Self { Self { code, modifiers } } - - fn insertable_char(&self) -> Option { - match self.code { - KeyCode::Char(c) if Self::is_plain_char(self.modifiers) => Some(c), - _ => None, - } - } - - fn is_plain_char(modifiers: KeyModifiers) -> bool { - (modifiers - KeyModifiers::SHIFT).is_empty() - } } impl From for HelixKey { @@ -64,10 +48,44 @@ impl InputKey for HelixKey { } fn get_char(&self) -> Option { - self.insertable_char() + match self.code { + KeyCode::Char(c) => Some(c), + _ => None, + } } } +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +enum HelixAction { + Type(char), + MoveCharRight, + MoveCharLeft, + #[default] + NoOp, +} + +impl HelixAction { + fn into_reedline_event(self) -> Option { + match self { + HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), + HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveLeft { select: false }, + ])), + HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveRight { select: false }, + ])), + HelixAction::NoOp => None, + } + } +} + +#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] +enum HelixMode { + #[default] + Insert, + Normal, +} + impl Mode for HelixMode {} impl From for HelixMode { @@ -88,7 +106,7 @@ impl ModeKeys for HelixMode { match self { HelixMode::Normal => (vec![], None), HelixMode::Insert => { - if let Some(c) = key.insertable_char() { + if let Some(c) = key.get_char() { return (vec![HelixAction::Type(c)], None); } @@ -98,29 +116,6 @@ impl ModeKeys for HelixMode { } } -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -enum HelixAction { - Type(char), - MoveCharRight, - MoveCharLeft, - #[default] - NoOp, -} - -impl HelixAction { - fn into_reedline_event(self) -> Option { - match self { - HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), - HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveLeft { select: false }, - ])), - HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveRight { select: false }, - ])), - HelixAction::NoOp => None, - } - } -} type HelixStep = (Option, Option); From 30cd391626898aa09f64b85a7b96f72ae7398c26 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 20:29:15 +0000 Subject: [PATCH 12/20] factor out into files --- src/edit_mode/helix/action.rs | 27 ++++ src/edit_mode/helix/bindings.rs | 68 +++++++++ src/edit_mode/helix/key.rs | 46 ++++++ src/edit_mode/{helix.rs => helix/mod.rs} | 184 ++--------------------- src/edit_mode/helix/mode.rs | 47 ++++++ 5 files changed, 203 insertions(+), 169 deletions(-) create mode 100644 src/edit_mode/helix/action.rs create mode 100644 src/edit_mode/helix/bindings.rs create mode 100644 src/edit_mode/helix/key.rs rename src/edit_mode/{helix.rs => helix/mod.rs} (58%) create mode 100644 src/edit_mode/helix/mode.rs diff --git a/src/edit_mode/helix/action.rs b/src/edit_mode/helix/action.rs new file mode 100644 index 00000000..2d3ba4cb --- /dev/null +++ b/src/edit_mode/helix/action.rs @@ -0,0 +1,27 @@ +use crate::{ + enums::{EditCommand, ReedlineEvent}, +}; + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub(super) enum HelixAction { + Type(char), + MoveCharRight, + MoveCharLeft, + #[default] + NoOp, +} + +impl HelixAction { + pub(super) fn into_reedline_event(self) -> Option { + match self { + HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), + HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveLeft { select: false }, + ])), + HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ + EditCommand::MoveRight { select: false }, + ])), + HelixAction::NoOp => None, + } + } +} \ No newline at end of file diff --git a/src/edit_mode/helix/bindings.rs b/src/edit_mode/helix/bindings.rs new file mode 100644 index 00000000..d2468b14 --- /dev/null +++ b/src/edit_mode/helix/bindings.rs @@ -0,0 +1,68 @@ +use crossterm::event::{KeyCode, KeyModifiers}; +use modalkit::keybindings::{ + EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, InputBindings, +}; + +use super::{ + key::HelixKey, + mode::{HelixMachine, HelixMode, HelixStep}, +}; + +#[derive(Default)] +pub(super) struct HelixBindings; + +impl HelixBindings { + fn add_single_keypress_mapping( + machine: &mut HelixMachine, + mode: HelixMode, + code: KeyCode, + step: HelixStep, + ) { + let path: &EdgePath = &[( + EdgeRepeat::Once, + EdgeEvent::Key(HelixKey::new(code, KeyModifiers::NONE)), + )]; + machine.add_mapping(mode, path, &step); + } + + fn add_bindings(machine: &mut HelixMachine, mode: HelixMode, bindings: &[(KeyCode, HelixStep)]) { + for (code, step) in bindings { + Self::add_single_keypress_mapping(machine, mode, *code, step.clone()); + } + } +} + +impl InputBindings for HelixBindings { + fn setup(&self, machine: &mut HelixMachine) { + let insert_bindings = [(KeyCode::Esc, (None, Some(HelixMode::Normal)))]; + let normal_bindings = [ + (KeyCode::Char('i'), (None, Some(HelixMode::Insert))), + ( + KeyCode::Char('h'), + (Some(super::action::HelixAction::MoveCharLeft), None), + ), + ( + KeyCode::Left, + (Some(super::action::HelixAction::MoveCharLeft), None), + ), + ( + KeyCode::Char('l'), + (Some(super::action::HelixAction::MoveCharRight), None), + ), + ( + KeyCode::Right, + (Some(super::action::HelixAction::MoveCharRight), None), + ), + ( + KeyCode::Char('a'), + ( + Some(super::action::HelixAction::MoveCharRight), + Some(HelixMode::Insert), + ), + ), + ]; + + Self::add_bindings(machine, HelixMode::Insert, &insert_bindings); + Self::add_bindings(machine, HelixMode::Normal, &normal_bindings); + } +} \ No newline at end of file diff --git a/src/edit_mode/helix/key.rs b/src/edit_mode/helix/key.rs new file mode 100644 index 00000000..234e3153 --- /dev/null +++ b/src/edit_mode/helix/key.rs @@ -0,0 +1,46 @@ +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use modalkit::keybindings::InputKey; + +/// A simple `InputKey` implementation around `crossterm` types. +/// +/// This avoids pulling in the `crossterm` types used by `modalkit` (which can be a different +/// version than the one used by `reedline`). +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub(super) struct HelixKey { + code: KeyCode, + modifiers: KeyModifiers, +} + +impl HelixKey { + pub(super) fn new(code: KeyCode, modifiers: KeyModifiers) -> Self { + Self { code, modifiers } + } +} + +impl From for HelixKey { + fn from(event: KeyEvent) -> Self { + Self::new(event.code, event.modifiers) + } +} + +impl InputKey for HelixKey { + type Error = std::convert::Infallible; + + fn decompose(&mut self) -> Option { + None + } + + fn from_macro_str(mstr: &str) -> Result, Self::Error> { + Ok(mstr + .chars() + .map(|c| HelixKey::new(KeyCode::Char(c), KeyModifiers::NONE)) + .collect()) + } + + fn get_char(&self) -> Option { + match self.code { + KeyCode::Char(c) => Some(c), + _ => None, + } + } +} \ No newline at end of file diff --git a/src/edit_mode/helix.rs b/src/edit_mode/helix/mod.rs similarity index 58% rename from src/edit_mode/helix.rs rename to src/edit_mode/helix/mod.rs index 12e0d59c..64fcca65 100644 --- a/src/edit_mode/helix.rs +++ b/src/edit_mode/helix/mod.rs @@ -1,177 +1,22 @@ +mod action; +mod bindings; +mod key; +mod mode; + use crate::{ edit_mode::EditMode, - enums::{EditCommand, ReedlineEvent, ReedlineRawEvent}, + enums::{ReedlineEvent, ReedlineRawEvent}, PromptEditMode, PromptViMode, }; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use modalkit::keybindings::{ - BindingMachine, EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, EmptyKeyState, InputBindings, - InputKey, ModalMachine, Mode, ModeKeys, -}; - - - -/// A simple `InputKey` implementation around `crossterm` types. -/// -/// This avoids pulling in the `crossterm` types used by `modalkit` (which can be a different -/// version than the one used by `reedline`). -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -struct HelixKey { - code: KeyCode, - modifiers: KeyModifiers, -} - -impl HelixKey { - fn new(code: KeyCode, modifiers: KeyModifiers) -> Self { - Self { code, modifiers } - } -} - -impl From for HelixKey { - fn from(event: KeyEvent) -> Self { - Self::new(event.code, event.modifiers) - } -} - -impl InputKey for HelixKey { - type Error = std::convert::Infallible; - - fn decompose(&mut self) -> Option { - None - } - - fn from_macro_str(mstr: &str) -> Result, Self::Error> { - Ok(mstr - .chars() - .map(|c| HelixKey::new(KeyCode::Char(c), KeyModifiers::NONE)) - .collect()) - } - - fn get_char(&self) -> Option { - match self.code { - KeyCode::Char(c) => Some(c), - _ => None, - } - } -} - -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -enum HelixAction { - Type(char), - MoveCharRight, - MoveCharLeft, - #[default] - NoOp, -} - -impl HelixAction { - fn into_reedline_event(self) -> Option { - match self { - HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), - HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveLeft { select: false }, - ])), - HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveRight { select: false }, - ])), - HelixAction::NoOp => None, - } - } -} +use modalkit::keybindings::BindingMachine; -#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] -enum HelixMode { - #[default] - Insert, - Normal, -} - -impl Mode for HelixMode {} - -impl From for HelixMode { - fn from(mode: PromptViMode) -> Self { - match mode { - PromptViMode::Insert => HelixMode::Insert, - PromptViMode::Normal => HelixMode::Normal, - } - } -} - -impl ModeKeys for HelixMode { - fn unmapped( - &self, - key: &HelixKey, - _: &mut EmptyKeyState, - ) -> (Vec, Option) { - match self { - HelixMode::Normal => (vec![], None), - HelixMode::Insert => { - if let Some(c) = key.get_char() { - return (vec![HelixAction::Type(c)], None); - } - - (vec![], None) - } - } - } -} - - -type HelixStep = (Option, Option); - -type HelixEdgePath = EdgePath; - -type HelixMachine = ModalMachine; - -#[derive(Default)] -struct HelixBindings; - -impl HelixBindings { - fn add_single_keypress_mapping( - machine: &mut HelixMachine, - mode: HelixMode, - code: KeyCode, - step: HelixStep, - ) { - let path: &HelixEdgePath = &[( - EdgeRepeat::Once, - EdgeEvent::Key(HelixKey::new(code, KeyModifiers::NONE)), - )]; - machine.add_mapping(mode, path, &step); - } - - fn add_bindings(machine: &mut HelixMachine, mode: HelixMode, bindings: &[(KeyCode, HelixStep)]) { - for (code, step) in bindings { - Self::add_single_keypress_mapping(machine, mode, *code, step.clone()); - } - } -} - -impl InputBindings for HelixBindings { - fn setup(&self, machine: &mut HelixMachine) { - let insert_bindings = [(KeyCode::Esc, (None, Some(HelixMode::Normal)))]; - let normal_bindings = [ - (KeyCode::Char('i'), (None, Some(HelixMode::Insert))), - ( - KeyCode::Char('h'), - (Some(HelixAction::MoveCharLeft), None), - ), - (KeyCode::Left, (Some(HelixAction::MoveCharLeft), None)), - ( - KeyCode::Char('l'), - (Some(HelixAction::MoveCharRight), None), - ), - (KeyCode::Right, (Some(HelixAction::MoveCharRight), None)), - ( - KeyCode::Char('a'), - (Some(HelixAction::MoveCharRight), Some(HelixMode::Insert)), - ), - ]; - - Self::add_bindings(machine, HelixMode::Insert, &insert_bindings); - Self::add_bindings(machine, HelixMode::Normal, &normal_bindings); - } -} +use self::{ + action::HelixAction, + bindings::HelixBindings, + key::HelixKey, + mode::{HelixMachine, HelixMode}, +}; /// A minimal custom edit mode example for Helix-style integrations. pub struct Helix { @@ -278,6 +123,7 @@ impl EditMode for Helix { #[cfg(test)] mod tests { use super::*; + use crate::enums::EditCommand; use crossterm::event::{Event, KeyEventKind, KeyEventState}; use rstest::rstest; @@ -400,4 +246,4 @@ mod tests { ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false }]) ); } -} +} \ No newline at end of file diff --git a/src/edit_mode/helix/mode.rs b/src/edit_mode/helix/mode.rs new file mode 100644 index 00000000..c5a6e6d3 --- /dev/null +++ b/src/edit_mode/helix/mode.rs @@ -0,0 +1,47 @@ +use crate::PromptViMode; +use modalkit::keybindings::{ + EmptyKeyState, InputKey, ModalMachine, Mode, ModeKeys, +}; + +use super::{action::HelixAction, key::HelixKey}; + +#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] +pub(super) enum HelixMode { + #[default] + Insert, + Normal, +} + +impl Mode for HelixMode {} + +impl From for HelixMode { + fn from(mode: PromptViMode) -> Self { + match mode { + PromptViMode::Insert => HelixMode::Insert, + PromptViMode::Normal => HelixMode::Normal, + } + } +} + +impl ModeKeys for HelixMode { + fn unmapped( + &self, + key: &HelixKey, + _: &mut EmptyKeyState, + ) -> (Vec, Option) { + match self { + HelixMode::Normal => (vec![], None), + HelixMode::Insert => { + if let Some(c) = key.get_char() { + return (vec![HelixAction::Type(c)], None); + } + + (vec![], None) + } + } + } +} + +pub(super) type HelixStep = (Option, Option); + +pub(super) type HelixMachine = ModalMachine; \ No newline at end of file From 5052ba81daf3cf411fe437cd3e027ec829e66f77 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 20:37:40 +0000 Subject: [PATCH 13/20] refactor --- src/edit_mode/helix/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 64fcca65..6c3fe97b 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -57,10 +57,6 @@ impl Helix { debug_assert_eq!(machine.mode(), mode); } - fn key_event_from_raw(event: ReedlineRawEvent) -> Option { - KeyEvent::try_from(event).ok() - } - fn is_interrupt_event(key_event: &KeyEvent) -> bool { matches!( key_event, @@ -105,7 +101,7 @@ impl Helix { impl EditMode for Helix { fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { - let Some(key_event) = Self::key_event_from_raw(event) else { + let Some(key_event) = KeyEvent::try_from(event).ok() else { return ReedlineEvent::None; }; From e644fa301152eb1bbeb614cee700c0564d522cfe Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 20:50:08 +0000 Subject: [PATCH 14/20] refactor out event handling logic --- src/edit_mode/helix/event.rs | 59 ++++++++++++++++++++++++++++++++++++ src/edit_mode/helix/mod.rs | 53 +++----------------------------- 2 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 src/edit_mode/helix/event.rs diff --git a/src/edit_mode/helix/event.rs b/src/edit_mode/helix/event.rs new file mode 100644 index 00000000..94dc2064 --- /dev/null +++ b/src/edit_mode/helix/event.rs @@ -0,0 +1,59 @@ +use crate::{ + enums::{ReedlineEvent, ReedlineRawEvent}, +}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use modalkit::keybindings::BindingMachine; + +use super::{ + action::HelixAction, + mode::HelixMachine, +}; + +pub(super) fn parse_event(machine: &mut HelixMachine, event: ReedlineRawEvent) -> ReedlineEvent { + let Some(key_event) = KeyEvent::try_from(event).ok() else { + return ReedlineEvent::None; + }; + + handle_key_event(machine, key_event) +} + +fn is_interrupt_event(key_event: &KeyEvent) -> bool { + matches!( + key_event, + KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + } + ) +} + +fn handle_key_event(machine: &mut HelixMachine, key_event: KeyEvent) -> ReedlineEvent { + if is_interrupt_event(&key_event) { + return ReedlineEvent::CtrlC; + } + + let (action, mode_changed) = apply_key_event(machine, key_event); + + action + .and_then(HelixAction::into_reedline_event) + .unwrap_or_else(|| mode_change_event(mode_changed)) +} + +fn apply_key_event(machine: &mut HelixMachine, key_event: KeyEvent) -> (Option, bool) { + let previous_mode = machine.mode(); + machine.input_key(key_event.into()); + + let mode_changed = machine.mode() != previous_mode; + let action = machine.pop().map(|(action, _ctx)| action); + + (action, mode_changed) +} + +fn mode_change_event(mode_changed: bool) -> ReedlineEvent { + if mode_changed { + ReedlineEvent::Repaint + } else { + ReedlineEvent::None + } +} \ No newline at end of file diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 6c3fe97b..7f3a4af1 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -1,5 +1,6 @@ mod action; mod bindings; +mod event; mod key; mod mode; @@ -8,11 +9,10 @@ use crate::{ enums::{ReedlineEvent, ReedlineRawEvent}, PromptEditMode, PromptViMode, }; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crossterm::event::{KeyCode, KeyModifiers}; use modalkit::keybindings::BindingMachine; use self::{ - action::HelixAction, bindings::HelixBindings, key::HelixKey, mode::{HelixMachine, HelixMode}, @@ -56,56 +56,11 @@ impl Helix { debug_assert_eq!(machine.mode(), mode); } - - fn is_interrupt_event(key_event: &KeyEvent) -> bool { - matches!( - key_event, - KeyEvent { - code: KeyCode::Char('c'), - modifiers: KeyModifiers::CONTROL, - .. - } - ) - } - - fn handle_key_event(&mut self, key_event: KeyEvent) -> ReedlineEvent { - if Self::is_interrupt_event(&key_event) { - return ReedlineEvent::CtrlC; - } - - let (action, mode_changed) = self.apply_key_event(key_event); - - action - .and_then(HelixAction::into_reedline_event) - .unwrap_or_else(|| Self::mode_change_event(mode_changed)) - } - - fn apply_key_event(&mut self, key_event: KeyEvent) -> (Option, bool) { - let previous_mode = self.machine.mode(); - self.machine.input_key(key_event.into()); - - let mode_changed = self.machine.mode() != previous_mode; - let action = self.machine.pop().map(|(action, _ctx)| action); - - (action, mode_changed) - } - - fn mode_change_event(mode_changed: bool) -> ReedlineEvent { - if mode_changed { - ReedlineEvent::Repaint - } else { - ReedlineEvent::None - } - } } impl EditMode for Helix { fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent { - let Some(key_event) = KeyEvent::try_from(event).ok() else { - return ReedlineEvent::None; - }; - - self.handle_key_event(key_event) + event::parse_event(&mut self.machine, event) } fn edit_mode(&self) -> PromptEditMode { @@ -120,7 +75,7 @@ impl EditMode for Helix { mod tests { use super::*; use crate::enums::EditCommand; - use crossterm::event::{Event, KeyEventKind, KeyEventState}; + use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyEventState}; use rstest::rstest; fn key_press(code: KeyCode, modifiers: KeyModifiers) -> ReedlineRawEvent { From e842ad0a7ccff15502fd90230b2be622d6302697 Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 21:15:38 +0000 Subject: [PATCH 15/20] use proptest for insert mode char fallback behavior --- Cargo.lock | 111 ++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/edit_mode/helix/mod.rs | 17 +++--- 3 files changed, 120 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56e78c8a..9c6da19b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,18 @@ dependencies = [ "windows-link", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -440,7 +452,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] @@ -851,6 +863,15 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -880,6 +901,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "quick-xml" version = "0.39.2" @@ -898,6 +934,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -914,6 +956,44 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -937,6 +1017,7 @@ dependencies = [ "modalkit", "nu-ansi-term", "pretty_assertions", + "proptest", "rstest", "rusqlite", "serde", @@ -1235,7 +1316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -1303,6 +1384,12 @@ dependencies = [ "petgraph", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.9.0" @@ -1956,6 +2043,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 3d4e0ba5..99bc5208 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ unicode-width = "0.2" [dev-dependencies] gethostname = "0.4.0" pretty_assertions = "1.4.0" +proptest = { version = "1.7.0", default-features = false, features = ["std"] } rstest = { version = "0.23.0", default-features = false } serde_json = "1.0" tempfile = "3.3.0" diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 7f3a4af1..81806b00 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -76,6 +76,7 @@ mod tests { use super::*; use crate::enums::EditCommand; use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyEventState}; + use proptest::{arbitrary::any, prop_assert_eq, proptest}; use rstest::rstest; fn key_press(code: KeyCode, modifiers: KeyModifiers) -> ReedlineRawEvent { @@ -164,14 +165,16 @@ mod tests { ); } - #[test] - fn typing_in_insert_mode_produces_insert_char_event() { - let mut helix_mode = Helix::new(PromptViMode::Insert); + proptest! { + #[test] + fn typing_in_insert_mode_produces_insert_char_event(character in any::()) { + let mut helix_mode = Helix::new(PromptViMode::Insert); - assert_eq!( - helix_mode.parse_event(key_press(KeyCode::Char('a'), KeyModifiers::NONE)), - ReedlineEvent::Edit(vec![EditCommand::InsertChar('a')]) - ); + prop_assert_eq!( + helix_mode.parse_event(key_press(KeyCode::Char(character), KeyModifiers::NONE)), + ReedlineEvent::Edit(vec![EditCommand::InsertChar(character)]) + ); + } } #[rstest] From fdb6be9acaefa798cb24ac5883fdfa68209b132d Mon Sep 17 00:00:00 2001 From: schlich Date: Mon, 30 Mar 2026 22:41:29 +0000 Subject: [PATCH 16/20] pure format --- src/edit_mode/helix/action.rs | 18 ++++++++---------- src/edit_mode/helix/bindings.rs | 12 +++++++----- src/edit_mode/helix/event.rs | 11 +++-------- src/edit_mode/helix/key.rs | 2 +- src/edit_mode/helix/mod.rs | 2 +- src/edit_mode/helix/mode.rs | 6 ++---- 6 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/edit_mode/helix/action.rs b/src/edit_mode/helix/action.rs index 2d3ba4cb..76b79d5e 100644 --- a/src/edit_mode/helix/action.rs +++ b/src/edit_mode/helix/action.rs @@ -1,6 +1,4 @@ -use crate::{ - enums::{EditCommand, ReedlineEvent}, -}; +use crate::enums::{EditCommand, ReedlineEvent}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub(super) enum HelixAction { @@ -15,13 +13,13 @@ impl HelixAction { pub(super) fn into_reedline_event(self) -> Option { match self { HelixAction::Type(c) => Some(ReedlineEvent::Edit(vec![EditCommand::InsertChar(c)])), - HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveLeft { select: false }, - ])), - HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![ - EditCommand::MoveRight { select: false }, - ])), + HelixAction::MoveCharLeft => Some(ReedlineEvent::Edit(vec![EditCommand::MoveLeft { + select: false, + }])), + HelixAction::MoveCharRight => Some(ReedlineEvent::Edit(vec![EditCommand::MoveRight { + select: false, + }])), HelixAction::NoOp => None, } } -} \ No newline at end of file +} diff --git a/src/edit_mode/helix/bindings.rs b/src/edit_mode/helix/bindings.rs index d2468b14..a502e3cb 100644 --- a/src/edit_mode/helix/bindings.rs +++ b/src/edit_mode/helix/bindings.rs @@ -1,7 +1,5 @@ use crossterm::event::{KeyCode, KeyModifiers}; -use modalkit::keybindings::{ - EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, InputBindings, -}; +use modalkit::keybindings::{EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, InputBindings}; use super::{ key::HelixKey, @@ -25,7 +23,11 @@ impl HelixBindings { machine.add_mapping(mode, path, &step); } - fn add_bindings(machine: &mut HelixMachine, mode: HelixMode, bindings: &[(KeyCode, HelixStep)]) { + fn add_bindings( + machine: &mut HelixMachine, + mode: HelixMode, + bindings: &[(KeyCode, HelixStep)], + ) { for (code, step) in bindings { Self::add_single_keypress_mapping(machine, mode, *code, step.clone()); } @@ -65,4 +67,4 @@ impl InputBindings for HelixBindings { Self::add_bindings(machine, HelixMode::Insert, &insert_bindings); Self::add_bindings(machine, HelixMode::Normal, &normal_bindings); } -} \ No newline at end of file +} diff --git a/src/edit_mode/helix/event.rs b/src/edit_mode/helix/event.rs index 94dc2064..e43fce36 100644 --- a/src/edit_mode/helix/event.rs +++ b/src/edit_mode/helix/event.rs @@ -1,13 +1,8 @@ -use crate::{ - enums::{ReedlineEvent, ReedlineRawEvent}, -}; +use crate::enums::{ReedlineEvent, ReedlineRawEvent}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use modalkit::keybindings::BindingMachine; -use super::{ - action::HelixAction, - mode::HelixMachine, -}; +use super::{action::HelixAction, mode::HelixMachine}; pub(super) fn parse_event(machine: &mut HelixMachine, event: ReedlineRawEvent) -> ReedlineEvent { let Some(key_event) = KeyEvent::try_from(event).ok() else { @@ -56,4 +51,4 @@ fn mode_change_event(mode_changed: bool) -> ReedlineEvent { } else { ReedlineEvent::None } -} \ No newline at end of file +} diff --git a/src/edit_mode/helix/key.rs b/src/edit_mode/helix/key.rs index 234e3153..13595a3f 100644 --- a/src/edit_mode/helix/key.rs +++ b/src/edit_mode/helix/key.rs @@ -43,4 +43,4 @@ impl InputKey for HelixKey { _ => None, } } -} \ No newline at end of file +} diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 81806b00..3f7e4d9a 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -200,4 +200,4 @@ mod tests { ReedlineEvent::Edit(vec![EditCommand::MoveRight { select: false }]) ); } -} \ No newline at end of file +} diff --git a/src/edit_mode/helix/mode.rs b/src/edit_mode/helix/mode.rs index c5a6e6d3..eeb4ad25 100644 --- a/src/edit_mode/helix/mode.rs +++ b/src/edit_mode/helix/mode.rs @@ -1,7 +1,5 @@ use crate::PromptViMode; -use modalkit::keybindings::{ - EmptyKeyState, InputKey, ModalMachine, Mode, ModeKeys, -}; +use modalkit::keybindings::{EmptyKeyState, InputKey, ModalMachine, Mode, ModeKeys}; use super::{action::HelixAction, key::HelixKey}; @@ -44,4 +42,4 @@ impl ModeKeys for HelixMode { pub(super) type HelixStep = (Option, Option); -pub(super) type HelixMachine = ModalMachine; \ No newline at end of file +pub(super) type HelixMachine = ModalMachine; From 09f3ba70b9bd60453c9d08e8c47e05d7cf41247b Mon Sep 17 00:00:00 2001 From: schlich Date: Tue, 31 Mar 2026 00:05:38 +0000 Subject: [PATCH 17/20] Revert "use proptest for insert mode char fallback behavior" This reverts commit e842ad0a7ccff15502fd90230b2be622d6302697. --- Cargo.lock | 111 +------------------------------------ Cargo.toml | 1 - src/edit_mode/helix/mod.rs | 17 +++--- 3 files changed, 9 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c6da19b..56e78c8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,18 +432,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - [[package]] name = "getrandom" version = "0.4.2" @@ -452,7 +440,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi 6.0.0", + "r-efi", "wasip2", "wasip3", ] @@ -863,15 +851,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "pretty_assertions" version = "1.4.1" @@ -901,21 +880,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bitflags", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "unarray", -] - [[package]] name = "quick-xml" version = "0.39.2" @@ -934,12 +898,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "r-efi" version = "6.0.0" @@ -956,44 +914,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -1017,7 +937,6 @@ dependencies = [ "modalkit", "nu-ansi-term", "pretty_assertions", - "proptest", "rstest", "rusqlite", "serde", @@ -1316,7 +1235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -1384,12 +1303,6 @@ dependencies = [ "petgraph", ] -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - [[package]] name = "unicase" version = "2.9.0" @@ -2043,26 +1956,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 99bc5208..3d4e0ba5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ unicode-width = "0.2" [dev-dependencies] gethostname = "0.4.0" pretty_assertions = "1.4.0" -proptest = { version = "1.7.0", default-features = false, features = ["std"] } rstest = { version = "0.23.0", default-features = false } serde_json = "1.0" tempfile = "3.3.0" diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 3f7e4d9a..9df8a302 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -76,7 +76,6 @@ mod tests { use super::*; use crate::enums::EditCommand; use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyEventState}; - use proptest::{arbitrary::any, prop_assert_eq, proptest}; use rstest::rstest; fn key_press(code: KeyCode, modifiers: KeyModifiers) -> ReedlineRawEvent { @@ -165,16 +164,14 @@ mod tests { ); } - proptest! { - #[test] - fn typing_in_insert_mode_produces_insert_char_event(character in any::()) { - let mut helix_mode = Helix::new(PromptViMode::Insert); + #[test] + fn typing_in_insert_mode_produces_insert_char_event() { + let mut helix_mode = Helix::new(PromptViMode::Insert); - prop_assert_eq!( - helix_mode.parse_event(key_press(KeyCode::Char(character), KeyModifiers::NONE)), - ReedlineEvent::Edit(vec![EditCommand::InsertChar(character)]) - ); - } + assert_eq!( + helix_mode.parse_event(key_press(KeyCode::Char('a'), KeyModifiers::NONE)), + ReedlineEvent::Edit(vec![EditCommand::InsertChar('a')]) + ); } #[rstest] From 44fc1223ebf89a2b51b07a97de52a5987c9662b2 Mon Sep 17 00:00:00 2001 From: schlich Date: Wed, 8 Apr 2026 03:16:22 -0500 Subject: [PATCH 18/20] install and import keybindings crate directly; revert MSRV --- Cargo.lock | 237 +++----------------------------- Cargo.toml | 4 +- src/core_editor/line_buffer.rs | 2 +- src/edit_mode/helix/bindings.rs | 2 +- src/edit_mode/helix/event.rs | 2 +- src/edit_mode/helix/key.rs | 2 +- src/edit_mode/helix/mod.rs | 2 +- src/edit_mode/helix/mode.rs | 2 +- src/painting/utils.rs | 2 +- 9 files changed, 26 insertions(+), 229 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56e78c8a..1790f5a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,12 +26,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - [[package]] name = "arboard" version = "3.6.1" @@ -108,12 +102,6 @@ dependencies = [ "error-code", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "convert_case" version = "0.10.0" @@ -185,22 +173,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - [[package]] name = "crossterm" version = "0.29.0" @@ -209,12 +181,12 @@ checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ "bitflags", "crossterm_winapi", - "derive_more 2.1.1", + "derive_more", "document-features", "libc", "mio", "parking_lot", - "rustix 1.1.4", + "rustix", "serde", "signal-hook", "signal-hook-mio", @@ -230,19 +202,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "derive_more" -version = "0.99.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" -dependencies = [ - "convert_case 0.4.0", - "proc-macro2", - "quote", - "rustc_version", - "syn", -] - [[package]] name = "derive_more" version = "2.1.1" @@ -258,7 +217,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", @@ -296,53 +255,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "editor-types" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e99679670f67825fcd24a23cb4eb655a0f92c82bd4d1c1a1357c0cd71e87" -dependencies = [ - "bitflags", - "editor-types-macros", - "keybindings", - "regex", -] - -[[package]] -name = "editor-types-macros" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42680de76cf91f231abd90cc623750d39077f7d2fadb7962325fb082871f4c66" -dependencies = [ - "editor-types-parser", - "nom 7.1.3", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "editor-types-parser" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac4b91fe830fbbe0a60c37ba0264b6e9ffc70e3664c028234dac59e79299ad4" -dependencies = [ - "nom 7.1.3", - "thiserror 1.0.69", -] - [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "equivalent" version = "1.0.2" @@ -390,7 +308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.1.4", + "rustix", "windows-sys 0.59.0", ] @@ -428,7 +346,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix 1.1.4", + "rustix", "windows-link", ] @@ -523,15 +441,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "intervaltree" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "270bc34e57047cab801a8c871c124d9dc7132f6473c6401f645524f4e6edd111" -dependencies = [ - "smallvec", -] - [[package]] name = "itertools" version = "0.13.0" @@ -591,12 +500,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -630,12 +533,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "mio" version = "1.1.1" @@ -648,47 +545,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "modalkit" -version = "0.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbb03c35f23ec7d13f7870049803cd8829f5e60b69d38fa98f5e7876de9f34e" -dependencies = [ - "anymap2", - "bitflags", - "crossterm 0.28.1", - "derive_more 0.99.20", - "editor-types", - "intervaltree", - "keybindings", - "nom 7.1.3", - "radix_trie", - "regex", - "ropey", - "thiserror 1.0.69", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nom" version = "8.0.0" @@ -904,16 +760,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -930,11 +776,11 @@ dependencies = [ "arboard", "chrono", "crossbeam", - "crossterm 0.29.0", + "crossterm", "fd-lock", "gethostname 0.4.3", "itertools", - "modalkit", + "keybindings", "nu-ansi-term", "pretty_assertions", "rstest", @@ -944,7 +790,7 @@ dependencies = [ "strip-ansi-escapes", "strum", "tempfile", - "thiserror 2.0.18", + "thiserror", "unicase", "unicode-segmentation", "unicode-width 0.2.2", @@ -985,16 +831,6 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" -[[package]] -name = "ropey" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" -dependencies = [ - "smallvec", - "str_indices", -] - [[package]] name = "rstest" version = "0.23.0" @@ -1045,19 +881,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.1.4" @@ -1067,7 +890,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -1181,12 +1004,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" -[[package]] -name = "str_indices" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" - [[package]] name = "strip-ansi-escapes" version = "0.2.1" @@ -1237,7 +1054,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -1252,33 +1069,13 @@ dependencies = [ "unicode-width 0.2.2", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1299,7 +1096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" dependencies = [ "memchr", - "nom 8.0.0", + "nom", "petgraph", ] @@ -1471,7 +1268,7 @@ checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.4", + "rustix", "smallvec", "wayland-sys", ] @@ -1483,7 +1280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" dependencies = [ "bitflags", - "rustix 1.1.4", + "rustix", "wayland-backend", "wayland-scanner", ] @@ -1924,8 +1721,8 @@ dependencies = [ "libc", "log", "os_pipe", - "rustix 1.1.4", - "thiserror 2.0.18", + "rustix", + "thiserror", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -1940,7 +1737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "gethostname 1.1.0", - "rustix 1.1.4", + "rustix", "x11rb-protocol", ] diff --git a/Cargo.toml b/Cargo.toml index 3d4e0ba5..87317cf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "MIT" name = "reedline" repository = "https://github.com/nushell/reedline" -rust-version = "1.74.0" +rust-version = "1.63.0" version = "0.46.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -24,7 +24,7 @@ crossbeam = { version = "0.8.2", optional = true } crossterm = { version = "0.29.0", features = ["serde"] } fd-lock = "4.0.2" itertools = "0.13.0" -modalkit = "0.0.24" +keybindings = "0.0.2" nu-ansi-term = "0.50.0" rusqlite = { version = "0.37.0", optional = true } serde = { version = "1.0", features = ["derive"] } diff --git a/src/core_editor/line_buffer.rs b/src/core_editor/line_buffer.rs index 7d34ec6c..416e13fe 100644 --- a/src/core_editor/line_buffer.rs +++ b/src/core_editor/line_buffer.rs @@ -323,7 +323,7 @@ impl LineBuffer { fn at_end_of_line_with_preceding_whitespace(&self) -> bool { !self.is_empty() // No point checking if empty && self.insertion_point == self.lines.len() - && self.lines.chars().last().is_some_and(|c| c.is_whitespace()) + && self.lines.chars().last().map_or(false, |c| c.is_whitespace()) } /// Cursor position at the end of the current whitespace block. diff --git a/src/edit_mode/helix/bindings.rs b/src/edit_mode/helix/bindings.rs index a502e3cb..be7c0515 100644 --- a/src/edit_mode/helix/bindings.rs +++ b/src/edit_mode/helix/bindings.rs @@ -1,5 +1,5 @@ use crossterm::event::{KeyCode, KeyModifiers}; -use modalkit::keybindings::{EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, InputBindings}; +use keybindings::{EdgeEvent, EdgePath, EdgeRepeat, EmptyKeyClass, InputBindings}; use super::{ key::HelixKey, diff --git a/src/edit_mode/helix/event.rs b/src/edit_mode/helix/event.rs index e43fce36..76dad9a2 100644 --- a/src/edit_mode/helix/event.rs +++ b/src/edit_mode/helix/event.rs @@ -1,6 +1,6 @@ use crate::enums::{ReedlineEvent, ReedlineRawEvent}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use modalkit::keybindings::BindingMachine; +use keybindings::BindingMachine; use super::{action::HelixAction, mode::HelixMachine}; diff --git a/src/edit_mode/helix/key.rs b/src/edit_mode/helix/key.rs index 13595a3f..7ff77249 100644 --- a/src/edit_mode/helix/key.rs +++ b/src/edit_mode/helix/key.rs @@ -1,5 +1,5 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use modalkit::keybindings::InputKey; +use keybindings::InputKey; /// A simple `InputKey` implementation around `crossterm` types. /// diff --git a/src/edit_mode/helix/mod.rs b/src/edit_mode/helix/mod.rs index 9df8a302..4b5eac18 100644 --- a/src/edit_mode/helix/mod.rs +++ b/src/edit_mode/helix/mod.rs @@ -10,7 +10,7 @@ use crate::{ PromptEditMode, PromptViMode, }; use crossterm::event::{KeyCode, KeyModifiers}; -use modalkit::keybindings::BindingMachine; +use keybindings::BindingMachine; use self::{ bindings::HelixBindings, diff --git a/src/edit_mode/helix/mode.rs b/src/edit_mode/helix/mode.rs index eeb4ad25..385a27ff 100644 --- a/src/edit_mode/helix/mode.rs +++ b/src/edit_mode/helix/mode.rs @@ -1,5 +1,5 @@ use crate::PromptViMode; -use modalkit::keybindings::{EmptyKeyState, InputKey, ModalMachine, Mode, ModeKeys}; +use keybindings::{EmptyKeyState, InputKey, ModalMachine, Mode, ModeKeys}; use super::{action::HelixAction, key::HelixKey}; diff --git a/src/painting/utils.rs b/src/painting/utils.rs index 4649197a..0c9d8ed5 100644 --- a/src/painting/utils.rs +++ b/src/painting/utils.rs @@ -59,7 +59,7 @@ pub(crate) fn estimate_single_line_wraps(line: &str, terminal_columns: u16) -> u let terminal_columns: usize = terminal_columns.into(); // integer ceiling rounding division for positive divisors - let estimated_line_count = estimated_width.div_ceil(terminal_columns); + let estimated_line_count = (estimated_width + terminal_columns - 1) / terminal_columns; // Any wrapping will add to our overall line count estimated_line_count.saturating_sub(1) From 6a7fc6b5255952e9fb20856c3d53c284af04e706 Mon Sep 17 00:00:00 2001 From: schlich Date: Wed, 8 Apr 2026 03:23:57 -0500 Subject: [PATCH 19/20] revert clippy changes in demo example --- examples/demo.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/demo.rs b/examples/demo.rs index 93818d99..83c6883c 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -39,7 +39,7 @@ fn main() -> reedline::Result<()> { history_session_id, Some(chrono::Utc::now()), ) - .map_err(std::io::Error::other)?, + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, ); #[cfg(not(any(feature = "sqlite", feature = "sqlite-dynlib")))] let history = Box::new(FileBackedHistory::with_file(50, "history.txt".into())?); @@ -195,7 +195,9 @@ fn main() -> reedline::Result<()> { } if buffer.trim() == "clear-history" { let hstry = Box::new(line_editor.history_mut()); - hstry.clear().map_err(std::io::Error::other)?; + hstry + .clear() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; continue; } println!("Our buffer: {buffer}"); From 020ca2fd954c62854f4159d3011b10bf4bc475c2 Mon Sep 17 00:00:00 2001 From: schlich Date: Wed, 8 Apr 2026 04:07:36 -0500 Subject: [PATCH 20/20] add more context to prompt in runnable example --- examples/helix.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/helix.rs b/examples/helix.rs index 224917da..08ee781a 100644 --- a/examples/helix.rs +++ b/examples/helix.rs @@ -8,7 +8,14 @@ use reedline::{DefaultPrompt, Helix, Reedline, Signal}; use std::io; fn main() -> io::Result<()> { - println!("Helix edit mode demo:\nAbort with Ctrl-C"); + println!( + "Helix edit mode demo: +Default mode is insert (`:` prompt), so you can type words. +Press Esc for normal mode. +Press `i` to return to insert mode, or `a` to insert after the current selection. +Only `h`/`l` motions are currently implemented. +Abort with Ctrl-C" + ); let prompt = DefaultPrompt::default(); let mut line_editor = Reedline::create().with_edit_mode(Box::new(Helix::default()));