From 70118f732c974fd953356020c6b3eb9b6ebe1ee0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 14 Sep 2022 21:58:45 +0200 Subject: [PATCH] Basic external printer (#467) Addresses #320, #236 * Adding External printer * Made ExternalPrinter as an optional feature. Clippy is happy, test pass, docs added. * ExternalPrinter: prints multiple messages if available, more on error-handling. * Bug(s) fixed. Prints messages. Working example in examples folder. Code formatted, clippyed, tests pass. * Generic ExternalPrinter where T: Display. * Fixed: Works with buffers larger than a line. * Fixed: Works with buffers larger than a line, refactored. * Different approach, seems to look like what is expected. Gives the "illusion" of one line being entered. Needs more testing, could have some off by one errors ;) Co-authored-by: Gregor Engberding --- Cargo.lock | 361 ++++++++++++++++++++++++----------- Cargo.toml | 5 +- examples/external_printer.rs | 64 +++++++ src/engine.rs | 61 +++++- src/external_printer.rs | 65 +++++++ src/lib.rs | 4 + src/painting/painter.rs | 49 ++++- 7 files changed, 490 insertions(+), 119 deletions(-) create mode 100644 examples/external_printer.rs create mode 100644 src/external_printer.rs diff --git a/Cargo.lock b/Cargo.lock index f05f37d..fd507e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "android_system_properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -46,6 +55,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + [[package]] name = "cc" version = "1.0.73" @@ -60,14 +75,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "time", + "wasm-bindgen", "winapi", ] @@ -93,6 +110,81 @@ dependencies = [ "winapi", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crossbeam" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "crossterm" version = "0.24.0" @@ -121,9 +213,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", @@ -131,15 +223,15 @@ dependencies = [ [[package]] name = "diff" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "errno" @@ -176,22 +268,22 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "fd-lock" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca" +checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.30.0", + "windows-sys", ] [[package]] @@ -206,13 +298,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -239,6 +331,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +[[package]] +name = "iana-time-zone" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "instant" version = "0.1.12" @@ -250,9 +355,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.6.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504" +checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" [[package]] name = "itertools" @@ -265,15 +370,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "js-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libsqlite3-sys" @@ -321,15 +435,24 @@ dependencies = [ ] [[package]] -name = "mio" -version = "0.8.3" +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -392,9 +515,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" [[package]] name = "output_vt100" @@ -431,7 +554,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -454,27 +577,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -485,6 +608,7 @@ version = "0.11.0" dependencies = [ "chrono", "clipboard", + "crossbeam", "crossterm", "fd-lock", "gethostname", @@ -561,29 +685,29 @@ dependencies = [ [[package]] name = "rustix" -version = "0.34.8" +version = "0.35.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c267b8394eb529872c3cf92e181c378b41fea36e68130357b52493701d2e" +checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "winapi", + "windows-sys", ] [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "scopeguard" @@ -593,24 +717,24 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.9" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -619,9 +743,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -660,9 +784,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "strip-ansi-escapes" @@ -675,15 +799,15 @@ dependencies = [ [[package]] name = "strum" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] name = "strum_macros" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", "proc-macro2", @@ -694,9 +818,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -719,18 +843,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" dependencies = [ "proc-macro2", "quote", @@ -750,9 +874,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-segmentation" @@ -817,6 +941,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" + [[package]] name = "winapi" version = "0.3.9" @@ -839,86 +1017,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a" -dependencies = [ - "windows_aarch64_msvc 0.30.0", - "windows_i686_gnu 0.30.0", - "windows_i686_msvc 0.30.0", - "windows_x86_64_gnu 0.30.0", - "windows_x86_64_msvc 0.30.0", -] - [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_i686_gnu" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" - [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" - [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_x86_64_gnu" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" diff --git a/Cargo.toml b/Cargo.toml index 8ce2cfe..a1c4e9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,14 +32,15 @@ rusqlite = { version = "0.28.0", optional = true, features = ["bundled"] } serde_json = { version = "1.0.79", optional = true } gethostname = { version = "0.2.3", optional = true } thiserror = "1.0.31" +crossbeam = { version = "0.8.2", optional = true } [dev-dependencies] tempfile = "3.3.0" pretty_assertions = "1.1.0" -rstest = {version = "0.15.0", default-features = false} +rstest = { version = "0.15.0", default-features = false } [features] system_clipboard = ["clipboard"] bashisms = [] sqlite = ["rusqlite", "serde_json", "gethostname"] - +external_printer = ["crossbeam"] diff --git a/examples/external_printer.rs b/examples/external_printer.rs new file mode 100644 index 0000000..53090e6 --- /dev/null +++ b/examples/external_printer.rs @@ -0,0 +1,64 @@ +// Create a default reedline object to handle user input +// to run: +// cargo run --example external_printer --features=external_printer + +#[cfg(feature = "external_printer")] +use { + reedline::ExternalPrinter, + reedline::{DefaultPrompt, Reedline, Signal}, + std::thread, + std::thread::sleep, + std::time::Duration, +}; + +#[cfg(feature = "external_printer")] +fn main() { + let printer = ExternalPrinter::default(); + // make a clone to use it in a different thread + let p_clone = printer.clone(); + // get the Sender to have full sending control + let p_sender = printer.sender(); + + // external printer that prints a message every second + thread::spawn(move || { + let mut i = 1; + loop { + sleep(Duration::from_secs(1)); + assert!(p_clone.print(format!("Message {} delivered.", i)).is_ok()); + i += 1; + } + }); + + // external printer that prints a bunch of messages after 3 seconds + thread::spawn(move || { + sleep(Duration::from_secs(3)); + for _ in 0..10 { + sleep(Duration::from_millis(1)); + assert!(p_sender.send(format!("Fast Hello !")).is_ok()); + } + }); + + let mut line_editor = Reedline::create().with_external_printer(printer); + let prompt = DefaultPrompt::default(); + + loop { + if let Ok(sig) = line_editor.read_line(&prompt) { + match sig { + Signal::Success(buffer) => { + println!("We processed: {}", buffer); + } + Signal::CtrlD | Signal::CtrlC => { + println!("\nAborted!"); + break; + } + } + continue; + } + break; + } +} + +#[cfg(not(feature = "external_printer"))] +fn main() { + println!("Please enable the feature: ‘external_printer‘") +} diff --git a/src/engine.rs b/src/engine.rs index 2b4c86a..bbad801 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -5,6 +5,12 @@ use crate::{ }; use crate::result::{ReedlineError, ReedlineErrorVariants}; +#[cfg(feature = "external_printer")] +use { + crate::external_printer::ExternalPrinter, + crossbeam::channel::TryRecvError, + std::io::{Error, ErrorKind}, +}; use { crate::{ completion::{Completer, DefaultCompleter}, @@ -86,7 +92,8 @@ pub struct Reedline { // History history: Box, history_cursor: HistoryCursor, - history_session_id: Option, // none if history doesn't support this + history_session_id: Option, + // none if history doesn't support this history_last_run_id: Option, input_mode: InputMode, @@ -119,6 +126,9 @@ pub struct Reedline { // Text editor used to open the line buffer for editing buffer_editor: Option, + + #[cfg(feature = "external_printer")] + external_printer: Option>, } struct BufferEditor { @@ -167,6 +177,8 @@ impl Reedline { use_ansi_coloring: true, menus: Vec::new(), buffer_editor: None, + #[cfg(feature = "external_printer")] + external_printer: None, } } @@ -454,7 +466,22 @@ impl Reedline { loop { let mut paste_enter_state = false; - if event::poll(Duration::from_millis(1000))? { + #[cfg(feature = "external_printer")] + if let Some(ref external_printer) = self.external_printer { + // get messages from printer as crlf separated "lines" + let messages = Self::external_messages(external_printer)?; + if !messages.is_empty() { + // print the message(s) + self.painter.print_external_message( + messages, + self.editor.line_buffer(), + prompt, + )?; + self.repaint(prompt)?; + } + } + + if event::poll(Duration::from_millis(100))? { let mut latest_resize = None; // There could be multiple events queued up! @@ -1384,6 +1411,36 @@ impl Reedline { self.painter .repaint_buffer(prompt, &lines, menu, self.use_ansi_coloring) } + + /// Adds an external printer + #[cfg(feature = "external_printer")] + pub fn with_external_printer(mut self, printer: ExternalPrinter) -> Self { + self.external_printer = Some(printer); + self + } + + #[cfg(feature = "external_printer")] + fn external_messages(external_printer: &ExternalPrinter) -> Result> { + let mut messages = Vec::new(); + loop { + let result = external_printer.receiver().try_recv(); + match result { + Ok(line) => { + messages.push(line); + } + Err(TryRecvError::Empty) => { + break; + } + Err(TryRecvError::Disconnected) => { + return Err(Error::new( + ErrorKind::NotConnected, + TryRecvError::Disconnected, + )); + } + } + } + Ok(messages) + } } #[test] diff --git a/src/external_printer.rs b/src/external_printer.rs new file mode 100644 index 0000000..b1da634 --- /dev/null +++ b/src/external_printer.rs @@ -0,0 +1,65 @@ +//! To print messages while editing a line +//! See example: +//! cargo run --example external_printer --features=external_printer +#[cfg(feature = "external_printer")] +use { + crossbeam::channel::{bounded, Receiver, SendError, Sender}, + std::fmt::Display, +}; + +#[cfg(feature = "external_printer")] +pub const EXTERNAL_PRINTER_DEFAULT_CAPACITY: usize = 20; + +/// An ExternalPrinter allows to print messages of text while editing a line. +/// The message is printed as a new line, the line-edit will continue below the +/// output. +#[cfg(feature = "external_printer")] +#[derive(Debug, Clone)] +pub struct ExternalPrinter +where + T: Display, +{ + sender: Sender, + receiver: Receiver, +} + +#[cfg(feature = "external_printer")] +impl ExternalPrinter +where + T: Display, +{ + /// Creates an ExternalPrinter to store lines with a max_cap + pub fn new(max_cap: usize) -> Self { + let (sender, receiver) = bounded::(max_cap); + Self { sender, receiver } + } + /// Gets a Sender to use the printer externally by sending lines to it + pub fn sender(&self) -> Sender { + self.sender.clone() + } + /// Receiver to get messages if any + pub fn receiver(&self) -> &Receiver { + &self.receiver + } + + /// Convenience method if the whole Printer is cloned, blocks if max_cap is reached. + /// + pub fn print(&self, line: T) -> Result<(), SendError> { + self.sender.send(line) + } + + /// Convenience method to get a line if any, doesn´t block. + pub fn get_line(&self) -> Option { + self.receiver.try_recv().ok() + } +} + +#[cfg(feature = "external_printer")] +impl Default for ExternalPrinter +where + T: Display, +{ + fn default() -> Self { + Self::new(EXTERNAL_PRINTER_DEFAULT_CAPACITY) + } +} diff --git a/src/lib.rs b/src/lib.rs index f8e371d..9c16226 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -259,6 +259,8 @@ pub use menu::{ }; mod utils; + +mod external_printer; pub use utils::{ get_reedline_default_keybindings, get_reedline_edit_commands, get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes, @@ -267,3 +269,5 @@ pub use utils::{ // Reexport the key types to be independent from an explicit crossterm dependency. pub use crossterm::event::{KeyCode, KeyModifiers}; +#[cfg(feature = "external_printer")] +pub use external_printer::ExternalPrinter; diff --git a/src/painting/painter.rs b/src/painting/painter.rs index 97df0a8..072eae1 100644 --- a/src/painting/painter.rs +++ b/src/painting/painter.rs @@ -3,10 +3,10 @@ use { crate::{ menu::{Menu, ReedlineMenu}, painting::PromptLines, - Prompt, + LineBuffer, Prompt, }, crossterm::{ - cursor::{self, MoveTo, RestorePosition, SavePosition}, + cursor::{self, MoveTo, MoveUp, RestorePosition, SavePosition}, style::{Attribute, Print, ResetColor, SetAttribute, SetForegroundColor}, terminal::{self, Clear, ClearType, ScrollUp}, QueueableCommand, Result, @@ -452,6 +452,51 @@ impl Painter { self.stdout.flush() } + + /// Prints an external message + pub fn print_external_message( + &mut self, + messages: Vec, + line_buffer: &LineBuffer, + prompt: &dyn Prompt, + ) -> Result<()> { + // adding 3 seems to be right for first line-wrap + let prompt_len = prompt.render_prompt_right().len() + 3; + let mut buffer_num_lines = 0_u16; + for (i, line) in line_buffer.get_buffer().lines().enumerate() { + let screen_lines = match i { + 0 => { + // the first line has to deal with the prompt + let first_line_len = line.len() + prompt_len; + // at least, it is one line + ((first_line_len as u16) / (self.screen_width())) + 1 + } + _ => { + // the n-th line, no prompt, at least, it is one line + ((line.len() as u16) / self.screen_width()) + 1 + } + }; + // count up screen-lines + buffer_num_lines = buffer_num_lines.saturating_add(screen_lines); + } + // move upward to start print if the line-buffer is more than one screen-line + if buffer_num_lines > 1 { + self.stdout.queue(MoveUp(buffer_num_lines - 1))?; + } + let erase_line = format!("\r{}\r", " ".repeat(self.screen_width().into())); + for line in messages { + self.stdout.queue(Print(&erase_line))?; + self.paint_line(&line)?; + let new_start = self.prompt_start_row.saturating_add(1); + let height = self.screen_height(); + if new_start >= height { + self.prompt_start_row = height - 1; + } else { + self.prompt_start_row = new_start; + } + } + Ok(()) + } } #[cfg(test)]