Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
radicle-term: Depend on crossterm instead of termion
Merged lorenz opened 10 months ago

When asked about Windows support, I was wondering what we might have to do to get radicle-term to work…

Ditching the pager implementation, and getting rid of a lot of maintenance burden, is the big step here.

Tested on my machine, runs nicely. Please test locally before merging, especially:

  • rad sync, which makes heavy use of the spinner.
  • Something involving editing, like rad comment edit or rad id update --edit.
  • The fact that rad patch diff and rad diff still work.
8 files changed +276 -304 570bfc3b 84427a56
modified Cargo.lock
@@ -273,9 +273,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "bitflags"
-
version = "2.5.0"
+
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"

[[package]]
name = "block-buffer"
@@ -417,7 +417,7 @@ dependencies = [
 "android-tzdata",
 "iana-time-zone",
 "num-traits",
-
 "windows-targets 0.52.5",
+
 "windows-targets 0.52.6",
]

[[package]]
@@ -454,6 +454,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"

[[package]]
+
name = "convert_case"
+
version = "0.7.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
+
dependencies = [
+
 "unicode-segmentation",
+
]
+

+
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -493,6 +502,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"

[[package]]
+
name = "crossterm"
+
version = "0.25.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
+
dependencies = [
+
 "bitflags 1.3.2",
+
 "crossterm_winapi",
+
 "libc",
+
 "mio 0.8.11",
+
 "parking_lot",
+
 "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 2.9.1",
+
 "crossterm_winapi",
+
 "derive_more",
+
 "document-features",
+
 "mio 1.0.4",
+
 "parking_lot",
+
 "rustix 1.0.7",
+
 "signal-hook",
+
 "signal-hook-mio",
+
 "winapi",
+
]
+

+
[[package]]
+
name = "crossterm_winapi"
+
version = "0.9.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+
dependencies = [
+
 "winapi",
+
]
+

+
[[package]]
name = "crypto-bigint"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -603,6 +655,27 @@ dependencies = [
]

[[package]]
+
name = "derive_more"
+
version = "2.0.1"
+
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"
+
dependencies = [
+
 "convert_case",
+
 "proc-macro2",
+
 "quote",
+
 "syn 2.0.89",
+
]
+

+
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -632,6 +705,15 @@ dependencies = [
]

[[package]]
+
name = "document-features"
+
version = "0.2.11"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
+
dependencies = [
+
 "litrs",
+
]
+

+
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -722,9 +804,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"

[[package]]
name = "errno"
-
version = "0.3.9"
+
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
 "libc",
 "windows-sys 0.52.0",
@@ -910,7 +992,7 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
 "libc",
 "libgit2-sys",
 "log",
@@ -972,7 +1054,7 @@ version = "0.14.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc2c844c4cf141884678cabef736fd91dd73068b9146e6f004ba1a0457944b6"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
 "bstr",
 "gix-path",
 "libc",
@@ -1087,7 +1169,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d27f830a16405386e9c83b9d5be8261fe32bbd6b3caf15bd1b284c6b2b7ef1a8"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
 "gix-commitgraph",
 "gix-date",
 "gix-hash",
@@ -1194,7 +1276,7 @@ dependencies = [
 "gix-command",
 "gix-config-value",
 "parking_lot",
-
 "rustix",
+
 "rustix 0.38.34",
 "thiserror 2.0.12",
]

@@ -1306,7 +1388,7 @@ version = "0.10.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aeb0f13de9ef2f3033f5ff218de30f44db827ac9f1286f9ef050aacddd5888"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
 "gix-path",
 "libc",
 "windows-sys 0.52.0",
@@ -1365,7 +1447,7 @@ version = "0.43.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ed47d648619e23e93f971d2bba0d10c1100e54ef95d2981d609907a8cabac89"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
 "gix-commitgraph",
 "gix-date",
 "gix-hash",
@@ -1633,13 +1715,13 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
+
 "crossterm 0.25.0",
 "dyn-clone",
 "fxhash",
 "newline-converter",
 "once_cell",
 "tempfile",
-
 "termion 2.0.3",
 "unicode-segmentation",
 "unicode-width",
]
@@ -1762,9 +1844,9 @@ checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"

[[package]]
name = "libc"
-
version = "0.2.155"
+
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"

[[package]]
name = "libgit2-sys"
@@ -1785,17 +1867,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"

[[package]]
-
name = "libredox"
-
version = "0.0.2"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
-
dependencies = [
-
 "bitflags 2.5.0",
-
 "libc",
-
 "redox_syscall",
-
]
-

-
[[package]]
name = "libz-sys"
version = "1.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1814,12 +1885,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"

[[package]]
+
name = "linux-raw-sys"
+
version = "0.9.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+

+
[[package]]
name = "litemap"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"

[[package]]
+
name = "litrs"
+
version = "0.4.1"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+

+
[[package]]
name = "localtime"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1880,6 +1963,30 @@ dependencies = [
]

[[package]]
+
name = "mio"
+
version = "0.8.11"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+
dependencies = [
+
 "libc",
+
 "log",
+
 "wasi",
+
 "windows-sys 0.48.0",
+
]
+

+
[[package]]
+
name = "mio"
+
version = "1.0.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+
dependencies = [
+
 "libc",
+
 "log",
+
 "wasi",
+
 "windows-sys 0.59.0",
+
]
+

+
[[package]]
name = "multibase"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2043,12 +2150,6 @@ dependencies = [
]

[[package]]
-
name = "numtoa"
-
version = "0.1.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
-

-
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2631,6 +2732,7 @@ dependencies = [
 "anstyle-query",
 "anyhow",
 "crossbeam-channel",
+
 "crossterm 0.29.0",
 "git2",
 "inquire",
 "libc",
@@ -2638,7 +2740,6 @@ dependencies = [
 "radicle-signals",
 "shlex",
 "tempfile",
-
 "termion 3.0.0",
 "thiserror 1.0.69",
 "unicode-display-width",
 "unicode-segmentation",
@@ -2685,12 +2786,6 @@ dependencies = [
]

[[package]]
-
name = "redox_termios"
-
version = "0.1.3"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
-

-
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2790,10 +2885,23 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
-
 "bitflags 2.5.0",
+
 "bitflags 2.9.1",
+
 "errno",
+
 "libc",
+
 "linux-raw-sys 0.4.13",
+
 "windows-sys 0.52.0",
+
]
+

+
[[package]]
+
name = "rustix"
+
version = "1.0.7"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+
dependencies = [
+
 "bitflags 2.9.1",
 "errno",
 "libc",
-
 "linux-raw-sys",
+
 "linux-raw-sys 0.9.4",
 "windows-sys 0.52.0",
]

@@ -2979,6 +3087,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"

[[package]]
+
name = "signal-hook"
+
version = "0.3.18"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+
dependencies = [
+
 "libc",
+
 "signal-hook-registry",
+
]
+

+
[[package]]
+
name = "signal-hook-mio"
+
version = "0.2.4"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
+
dependencies = [
+
 "libc",
+
 "mio 0.8.11",
+
 "mio 1.0.4",
+
 "signal-hook",
+
]
+

+
[[package]]
+
name = "signal-hook-registry"
+
version = "1.4.5"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
+
dependencies = [
+
 "libc",
+
]
+

+
[[package]]
name = "signals_receipts"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3237,35 +3376,11 @@ checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
 "cfg-if",
 "fastrand",
-
 "rustix",
+
 "rustix 0.38.34",
 "windows-sys 0.52.0",
]

[[package]]
-
name = "termion"
-
version = "2.0.3"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c4648c7def6f2043b2568617b9f9b75eae88ca185dbc1f1fda30e95a85d49d7d"
-
dependencies = [
-
 "libc",
-
 "libredox",
-
 "numtoa",
-
 "redox_termios",
-
]
-

-
[[package]]
-
name = "termion"
-
version = "3.0.0"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "417813675a504dfbbf21bfde32c03e5bf9f2413999962b479023c02848c1c7a5"
-
dependencies = [
-
 "libc",
-
 "libredox",
-
 "numtoa",
-
 "redox_termios",
-
]
-

-
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3679,6 +3794,22 @@ dependencies = [
]

[[package]]
+
name = "winapi"
+
version = "0.3.9"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+
dependencies = [
+
 "winapi-i686-pc-windows-gnu",
+
 "winapi-x86_64-pc-windows-gnu",
+
]
+

+
[[package]]
+
name = "winapi-i686-pc-windows-gnu"
+
version = "0.4.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+

+
[[package]]
name = "winapi-util"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3688,12 +3819,18 @@ dependencies = [
]

[[package]]
+
name = "winapi-x86_64-pc-windows-gnu"
+
version = "0.4.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+

+
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
-
 "windows-targets 0.52.5",
+
 "windows-targets 0.52.6",
]

[[package]]
@@ -3711,7 +3848,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
-
 "windows-targets 0.52.5",
+
 "windows-targets 0.52.6",
+
]
+

+
[[package]]
+
name = "windows-sys"
+
version = "0.59.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+
dependencies = [
+
 "windows-targets 0.52.6",
]

[[package]]
@@ -3731,18 +3877,18 @@ dependencies = [

[[package]]
name = "windows-targets"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
-
 "windows_aarch64_gnullvm 0.52.5",
-
 "windows_aarch64_msvc 0.52.5",
-
 "windows_i686_gnu 0.52.5",
+
 "windows_aarch64_gnullvm 0.52.6",
+
 "windows_aarch64_msvc 0.52.6",
+
 "windows_i686_gnu 0.52.6",
 "windows_i686_gnullvm",
-
 "windows_i686_msvc 0.52.5",
-
 "windows_x86_64_gnu 0.52.5",
-
 "windows_x86_64_gnullvm 0.52.5",
-
 "windows_x86_64_msvc 0.52.5",
+
 "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]]
@@ -3753,9 +3899,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"

[[package]]
name = "windows_aarch64_gnullvm"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"

[[package]]
name = "windows_aarch64_msvc"
@@ -3765,9 +3911,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"

[[package]]
name = "windows_aarch64_msvc"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"

[[package]]
name = "windows_i686_gnu"
@@ -3777,15 +3923,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"

[[package]]
name = "windows_i686_gnu"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"

[[package]]
name = "windows_i686_gnullvm"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"

[[package]]
name = "windows_i686_msvc"
@@ -3795,9 +3941,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"

[[package]]
name = "windows_i686_msvc"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"

[[package]]
name = "windows_x86_64_gnu"
@@ -3807,9 +3953,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"

[[package]]
name = "windows_x86_64_gnu"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"

[[package]]
name = "windows_x86_64_gnullvm"
@@ -3819,9 +3965,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"

[[package]]
name = "windows_x86_64_gnullvm"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"

[[package]]
name = "windows_x86_64_msvc"
@@ -3831,9 +3977,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

[[package]]
name = "windows_x86_64_msvc"
-
version = "0.52.5"
+
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

[[package]]
name = "winnow"
@@ -3863,8 +4009,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
 "libc",
-
 "linux-raw-sys",
-
 "rustix",
+
 "linux-raw-sys 0.4.13",
+
 "rustix 0.38.34",
]

[[package]]
modified crates/radicle-cli/src/commands/diff.rs
@@ -145,7 +145,7 @@ pub fn run(options: Options, _ctx: impl term::Context) -> anyhow::Result<()> {
    let mut hi = Highlighter::default();
    let pretty = diff.pretty(&mut hi, &(), &repo);

-
    term::pager::page(pretty)?;
+
    crate::pager::run(pretty)?;

    Ok(())
}
modified crates/radicle-term/Cargo.toml
@@ -16,10 +16,10 @@ default = ["git2"]
anyhow = { workspace = true }
anstyle-query = "1.0.0"
crossbeam-channel = { workspace = true }
-
inquire = { version = "0.7.4", default-features = false, features = ["termion", "editor"] }
+
crossterm = "0.29.0"
+
inquire = { version = "0.7.4", default-features = false, features = ["crossterm", "editor"] }
libc = { workspace = true }
shlex = { workspace = true }
-
termion = "3"
thiserror = { workspace = true }
unicode-display-width = "0.3.0"
unicode-segmentation = "1.7.1"
modified crates/radicle-term/src/editor.rs
@@ -133,11 +133,19 @@ impl Editor {
        let stderr = unsafe { libc::dup(stderr) };
        let stdin = if io::stdin().is_terminal() {
            process::Stdio::inherit()
-
        } else {
-
            let tty = termion::get_tty()?;
+
        } else if cfg!(unix) {
            // If standard input is not a terminal device, the editor won't work correctly.
            // In that case, we use the terminal device, eg. `/dev/tty` as standard input.
+
            let tty = fs::OpenOptions::new()
+
                .read(true)
+
                .write(true)
+
                .open("/dev/tty")?;
            process::Stdio::from(tty)
+
        } else {
+
            return Err(io::Error::new(
+
                io::ErrorKind::Unsupported,
+
                format!("standard input is not a terminal, refusing to execute editor {cmd:?}"),
+
            ));
        };

        process::Command::new(program)
modified crates/radicle-term/src/io.rs
@@ -103,15 +103,19 @@ pub fn notice_args<W: io::Write>(w: &mut W, args: fmt::Arguments) {
}

pub fn columns() -> Option<usize> {
-
    termion::terminal_size().map(|(cols, _)| cols as usize).ok()
+
    crossterm::terminal::size()
+
        .map(|(cols, _)| cols as usize)
+
        .ok()
}

pub fn rows() -> Option<usize> {
-
    termion::terminal_size().map(|(_, rows)| rows as usize).ok()
+
    crossterm::terminal::size()
+
        .map(|(_, rows)| rows as usize)
+
        .ok()
}

pub fn viewport() -> Option<Size> {
-
    termion::terminal_size()
+
    crossterm::terminal::size()
        .map(|(cols, rows)| Size::new(cols as usize, rows as usize))
        .ok()
}
modified crates/radicle-term/src/lib.rs
@@ -8,7 +8,6 @@ pub mod format;
pub mod hstack;
pub mod io;
pub mod label;
-
pub mod pager;
pub mod spinner;
pub mod table;
pub mod textarea;
deleted crates/radicle-term/src/pager.rs
@@ -1,186 +0,0 @@
-
use std::io::{IsTerminal, Write};
-
use std::{io, thread};
-

-
use crate::element::Size;
-
use crate::{Constraint, Element, Line, Paint};
-

-
use crossbeam_channel as chan;
-
use radicle_signals as signals;
-
use termion::event::{Event, Key, MouseButton, MouseEvent};
-
use termion::{input::TermRead, raw::IntoRawMode, screen::IntoAlternateScreen};
-

-
/// How many lines to scroll when the mouse wheel is used.
-
const MOUSE_SCROLL_LINES: usize = 3;
-

-
/// Pager error.
-
#[derive(Debug, thiserror::Error)]
-
pub enum Error {
-
    #[error(transparent)]
-
    Io(#[from] io::Error),
-
    #[error(transparent)]
-
    Channel(#[from] chan::RecvError),
-
}
-

-
/// A pager for the given element. Re-renders the element when the terminal is resized so that
-
/// it doesn't wrap. If the output device is not a TTY, just prints the element via
-
/// [`Element::print`].
-
///
-
/// # Signal Handling
-
///
-
/// This will install handlers for the pager until finished by the user, with there
-
/// being only one element handling signals at a time. If the pager cannot install
-
/// handlers, then it will return with an error.
-
pub fn page<E: Element + Send + 'static>(element: E) -> Result<(), Error> {
-
    let (events_tx, events_rx) = chan::unbounded();
-
    let (signals_tx, signals_rx) = chan::unbounded();
-

-
    signals::install(signals_tx)?;
-

-
    thread::spawn(move || {
-
        for e in io::stdin().events() {
-
            events_tx.send(e).ok();
-
        }
-
    });
-
    let result = thread::spawn(move || main(element, signals_rx, events_rx))
-
        .join()
-
        .unwrap();
-

-
    signals::uninstall()?;
-

-
    result
-
}
-

-
fn main<E: Element>(
-
    element: E,
-
    signals_rx: chan::Receiver<signals::Signal>,
-
    events_rx: chan::Receiver<Result<Event, io::Error>>,
-
) -> Result<(), Error> {
-
    let stdout = io::stdout();
-
    if !stdout.is_terminal() {
-
        element.print();
-
        return Ok(());
-
    }
-
    let raw = stdout.into_raw_mode()?;
-
    let mut stdout = termion::input::MouseTerminal::from(raw).into_alternate_screen()?;
-
    let (mut width, mut height) = termion::terminal_size()?;
-
    let mut lines = element.render(Constraint::max(Size::new(width as usize, height as usize)));
-
    let mut line = 0;
-

-
    render(&mut stdout, lines.as_slice(), line, (width, height))?;
-

-
    loop {
-
        chan::select! {
-
            recv(signals_rx) -> signal => {
-
                match signal? {
-
                    signals::Signal::WindowChanged => {
-
                        let (w, h) = termion::terminal_size()?;
-

-
                        lines = element.render(Constraint::max(Size::new(w as usize, h as usize)));
-
                        width = w;
-
                        height = h;
-
                    }
-
                    signals::Signal::Interrupt | signals::Signal::Terminate => {
-
                        break;
-
                    }
-
                    _ => continue,
-
                }
-
            }
-
            recv(events_rx) -> event => {
-
                let event = event??;
-
                let page = height as usize - 1; // Don't count the status bar.
-
                let end = if page > lines.len() { 0 } else { lines.len() - page };
-
                let prev = line;
-

-
                match event {
-
                    Event::Key(key) => match key {
-
                        Key::Up | Key::Char('k') => {
-
                            line = line.saturating_sub(1);
-
                        }
-
                        Key::Home => {
-
                            line = 0;
-
                        }
-
                        Key::End | Key::Char('G') => {
-
                            line = end;
-
                        }
-
                        Key::PageUp | Key::Char('b') => {
-
                            line = line.saturating_sub(page);
-
                        }
-
                        Key::PageDown | Key::Char(' ') => {
-
                            line = (line + page).min(end);
-
                        }
-
                        Key::Down | Key::Char('j') => {
-
                            if line < end {
-
                                line += 1;
-
                            }
-
                        }
-
                        Key::Char('q') => break,
-

-
                        _ => continue,
-
                    }
-
                    Event::Mouse(MouseEvent::Press(MouseButton::WheelDown, _, _)) => {
-
                        if line < end {
-
                            line += MOUSE_SCROLL_LINES;
-
                        }
-
                    }
-
                    Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, _, _)) => {
-
                        line = line.saturating_sub(MOUSE_SCROLL_LINES);
-
                    }
-
                    _ => continue,
-
                }
-
                // Don't re-render if there's no change in line.
-
                if line == prev {
-
                    continue;
-
                }
-
            }
-
        }
-
        render(&mut stdout, &lines, line, (width, height))?;
-
    }
-
    Ok(())
-
}
-

-
fn render<W: Write>(
-
    out: &mut W,
-
    lines: &[Line],
-
    start_line: usize,
-
    (width, height): (u16, u16),
-
) -> io::Result<()> {
-
    write!(
-
        out,
-
        "{}{}",
-
        termion::clear::All,
-
        termion::cursor::Goto(1, 1)
-
    )?;
-

-
    let content_length = lines.len();
-
    let window_size = height as usize - 1;
-
    let end_line = if start_line + window_size > content_length {
-
        content_length
-
    } else {
-
        start_line + window_size
-
    };
-
    // Render content.
-
    for (ix, line) in lines[start_line..end_line].iter().enumerate() {
-
        write!(out, "{}{}", termion::cursor::Goto(1, ix as u16 + 1), line)?;
-
    }
-
    // Render progress meter.
-
    write!(
-
        out,
-
        "{}{}",
-
        termion::cursor::Goto(width - 3, height),
-
        Paint::new(format!(
-
            "{:.0}%",
-
            end_line as f64 / lines.len() as f64 * 100.
-
        ))
-
        .dim()
-
    )?;
-
    // Render cursor input area.
-
    write!(
-
        out,
-
        "{}{}",
-
        termion::cursor::Goto(1, height),
-
        Paint::new(":").dim()
-
    )?;
-
    out.flush()?;
-

-
    Ok(())
-
}
modified crates/radicle-term/src/spinner.rs
@@ -1,4 +1,4 @@
-
use std::io::{IsTerminal, Write};
+
use std::io::IsTerminal;
use std::mem::ManuallyDrop;
use std::sync::{Arc, Mutex};
use std::{fmt, io, thread, time};
@@ -21,6 +21,9 @@ pub const DEFAULT_STYLE: [Paint<&'static str>; 4] = [
    Paint::blue("◥"),
];

+
const CLEAR_UNTIL_NEWLINE: crossterm::terminal::Clear =
+
    crossterm::terminal::Clear(crossterm::terminal::ClearType::UntilNewLine);
+

struct Progress {
    state: State,
    message: Paint<String>,
@@ -129,7 +132,7 @@ pub fn spinner(message: impl ToString) -> Spinner {
pub fn spinner_to(
    message: impl ToString,
    mut completion: impl io::Write + Send + 'static,
-
    animation: impl io::Write + Send + 'static,
+
    mut animation: impl io::Write + Send + 'static,
) -> Spinner {
    let message = message.to_string();
    let progress = Arc::new(Mutex::new(Progress::new(Paint::new(message))));
@@ -141,7 +144,7 @@ pub fn spinner_to(
            let progress = progress.clone();

            move || {
-
                let mut animation = termion::cursor::HideCursor::from(animation);
+
                write!(animation, "{}", crossterm::cursor::Hide).ok();

                loop {
                    let Ok(mut progress) = progress.lock() else {
@@ -151,7 +154,7 @@ pub fn spinner_to(
                    if sig_result.is_ok() {
                        match sig_rx.try_recv() {
                            Ok(sig) if sig == Signal::Interrupt || sig == Signal::Terminate => {
-
                                write!(animation, "\r{}", termion::clear::UntilNewline).ok();
+
                                write!(animation, "\r{CLEAR_UNTIL_NEWLINE}").ok();
                                writeln!(
                                    completion,
                                    "{ERROR_PREFIX} {} {}",
@@ -173,12 +176,7 @@ pub fn spinner_to(
                        } => {
                            let spinner = DEFAULT_STYLE[*cursor];

-
                            write!(
-
                                animation,
-
                                "\r{}{spinner} {message}",
-
                                termion::clear::UntilNewline,
-
                            )
-
                            .ok();
+
                            write!(animation, "\r{}{spinner} {message}", CLEAR_UNTIL_NEWLINE,).ok();

                            *cursor += 1;
                            *cursor %= DEFAULT_STYLE.len();
@@ -187,7 +185,7 @@ pub fn spinner_to(
                            state: State::Done,
                            message,
                        } => {
-
                            write!(animation, "\r{}", termion::clear::UntilNewline).ok();
+
                            write!(animation, "\r{CLEAR_UNTIL_NEWLINE}").ok();
                            writeln!(completion, "{} {message}", Paint::green("✓")).ok();
                            break;
                        }
@@ -195,7 +193,7 @@ pub fn spinner_to(
                            state: State::Canceled,
                            message,
                        } => {
-
                            write!(animation, "\r{}", termion::clear::UntilNewline).ok();
+
                            write!(animation, "\r{CLEAR_UNTIL_NEWLINE}").ok();
                            writeln!(
                                completion,
                                "{ERROR_PREFIX} {message} {}",
@@ -208,7 +206,7 @@ pub fn spinner_to(
                            state: State::Warn,
                            message,
                        } => {
-
                            write!(animation, "\r{}", termion::clear::UntilNewline).ok();
+
                            write!(animation, "\r{CLEAR_UNTIL_NEWLINE}").ok();
                            writeln!(completion, "{WARNING_PREFIX} {message}").ok();
                            break;
                        }
@@ -216,7 +214,7 @@ pub fn spinner_to(
                            state: State::Error,
                            message,
                        } => {
-
                            write!(animation, "\r{}", termion::clear::UntilNewline).ok();
+
                            write!(animation, "\r{CLEAR_UNTIL_NEWLINE}").ok();
                            writeln!(completion, "{ERROR_PREFIX} {message}").ok();
                            break;
                        }
@@ -224,6 +222,9 @@ pub fn spinner_to(
                    drop(progress);
                    thread::sleep(DEFAULT_TICK);
                }
+

+
                write!(animation, "{}", crossterm::cursor::Show).ok();
+

                if sig_result.is_ok() {
                    let _ = signals::uninstall();
                }