diff --git a/Cargo.lock b/Cargo.lock index 869c7fb..a94b028 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -53,7 +59,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -64,7 +70,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -73,6 +79,27 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -163,6 +190,12 @@ version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "castaway" version = "0.2.4" @@ -234,6 +267,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "color-ansi" version = "0.1.0" @@ -268,7 +310,7 @@ checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" dependencies = [ "encode_unicode", "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -289,6 +331,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.29.0" @@ -316,6 +367,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -417,6 +474,16 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + [[package]] name = "document-features" version = "0.2.12" @@ -426,6 +493,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dumpster" version = "2.1.0" @@ -473,9 +546,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "euclid" version = "0.22.13" @@ -501,6 +580,35 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "filedescriptor" version = "0.8.3" @@ -530,6 +638,22 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -573,6 +697,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -598,6 +732,17 @@ dependencies = [ "wasip3", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -642,6 +787,20 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png", + "tiff", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -721,7 +880,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -833,14 +992,23 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logparse" -version = "0.2.0" +version = "0.3.0" dependencies = [ "insta", + "logparse-pretty-print", "proptest", "proptest-derive", "winnow", ] +[[package]] +name = "logparse-pretty-print" +version = "0.1.0" +dependencies = [ + "color-ansi", + "unicode-segmentation", +] + [[package]] name = "loom" version = "0.7.2" @@ -909,6 +1077,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.1.1" @@ -918,7 +1096,17 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", ] [[package]] @@ -956,13 +1144,22 @@ 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.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1000,6 +1197,79 @@ dependencies = [ "libc", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1021,6 +1291,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -1044,6 +1324,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pest" version = "2.8.6" @@ -1087,6 +1373,17 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset 0.5.7", + "hashbrown 0.15.5", + "indexmap", +] + [[package]] name = "phf" version = "0.11.3" @@ -1145,6 +1442,25 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -1186,20 +1502,12 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "pretty-print" -version = "0.1.8" -dependencies = [ - "color-ansi", - "unicode-segmentation", -] - [[package]] name = "pretty-test" version = "0.0.0" dependencies = [ + "logparse-pretty-print", "pretty", - "pretty-print", ] [[package]] @@ -1251,12 +1559,33 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.44" @@ -1462,6 +1791,7 @@ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" name = "rustc-logviz" version = "0.1.0" dependencies = [ + "arboard", "clap", "crossterm", "dumpster", @@ -1469,7 +1799,6 @@ dependencies = [ "jiff", "logparse", "nix 0.31.1", - "pretty-print", "ratatui", "ratatui-themes", "regex", @@ -1498,7 +1827,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1514,7 +1843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", - "quick-error", + "quick-error 1.2.3", "tempfile", "wait-timeout", ] @@ -1643,6 +1972,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "similar" version = "2.7.0" @@ -1726,7 +2061,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1736,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" dependencies = [ "fnv", - "nom", + "nom 7.1.3", "phf", "phf_codegen", ] @@ -1762,7 +2097,7 @@ dependencies = [ "fancy-regex", "filedescriptor", "finl_unicode", - "fixedbitset", + "fixedbitset 0.4.2", "hex", "lazy_static", "libc", @@ -1841,6 +2176,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error 2.0.1", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.47" @@ -1911,6 +2260,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom 8.0.0", + "petgraph", +] + [[package]] name = "tui-widget-list" version = "0.15.0" @@ -2131,6 +2491,82 @@ dependencies = [ "semver", ] +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.11.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.11.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -2240,6 +2676,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -2249,6 +2694,71 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[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.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[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" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "1.0.1" @@ -2346,6 +2856,41 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix", + "thiserror 2.0.18", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "zerocopy" version = "0.8.48" @@ -2371,3 +2916,18 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index fbf57f7..c269860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,5 +25,5 @@ nix = {version = "0.31", features = ["process", "signal"]} regex = "1" crossterm = "*" dumpster = "2.1" -logparse = {path = "./logparse/", version="0.2.0"} -pretty-print = {path = "./pretty-print/projects/pretty-print"} +logparse = {path = "./logparse/", version="0.3.0"} +arboard = {version="3", features=["wayland-data-control"]} diff --git a/logparse/Cargo.toml b/logparse/Cargo.toml index 83b9e4c..91c631d 100644 --- a/logparse/Cargo.toml +++ b/logparse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logparse" -version = "0.2.0" +version = "0.3.0" edition = "2024" description = "parse arbitrary messages containing rust-like debug output to syntax highlight them" authors = ["Jana Dönszelmann "] @@ -12,6 +12,9 @@ readme = "README.md" [dependencies] winnow = {version="1", features=["parser"]} +logparse-pretty-print = {path = "../pretty-print/projects/pretty-print", version="0.1.0"} + +[dev-dependencies] proptest = "1" proptest-derive = "0.8" insta = "1" diff --git a/logparse/src/ast.rs b/logparse/src/ast.rs index c413531..0039ea0 100644 --- a/logparse/src/ast.rs +++ b/logparse/src/ast.rs @@ -1,3 +1,4 @@ +#[cfg(test)] use proptest_derive::Arbitrary; use std::borrow::Cow; @@ -11,7 +12,8 @@ pub enum Separator { } /// See [`Token::String`]. -#[derive(Copy, Clone, Debug, Arbitrary, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(test, derive(Arbitrary))] #[allow(missing_docs)] pub enum QuoteType { Single, @@ -20,7 +22,8 @@ pub enum QuoteType { } /// See [`Token::Delimited`]. -#[derive(Clone, Debug, Arbitrary, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(test, derive(Arbitrary))] #[allow(missing_docs)] pub enum Delimiter { Paren, @@ -50,7 +53,8 @@ pub struct AnyString<'a> { pub struct Space<'a>(pub Cow<'a, str>); /// See [`Token::Path`]. -#[derive(Copy, Clone, Debug, PartialEq, Arbitrary)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(test, derive(Arbitrary))] #[allow(missing_docs)] pub enum PathSep { /// Happens at the start of paths, for the no leading / case diff --git a/logparse/src/lib.rs b/logparse/src/lib.rs index 957b657..ca254e8 100644 --- a/logparse/src/lib.rs +++ b/logparse/src/lib.rs @@ -1,5 +1,5 @@ #![deny(missing_docs)] -#![deny(warnings)] +// #![deny(warnings)] #![doc=include_str!("../README.md")] /// The structure of parsed log lines diff --git a/logparse/src/proptesting.rs b/logparse/src/proptesting.rs index cbc164d..d8a6c54 100644 --- a/logparse/src/proptesting.rs +++ b/logparse/src/proptesting.rs @@ -23,7 +23,7 @@ proptest! { #[test] fn proptest_into_spans(original in Segments::arb(Token::arb())) { let stringified = original.to_string(); - let spans = into_spans(original, Config {collapse_space: false}); + let spans = into_spans(original, Config {collapse_space: false, ..Default::default()}); let spans_concatenated = spans.into_iter().map(|i| i.text).collect::(); assert_eq!(stringified, spans_concatenated); } diff --git a/logparse/src/spans.rs b/logparse/src/spans.rs index ee7ecb6..b98cf77 100644 --- a/logparse/src/spans.rs +++ b/logparse/src/spans.rs @@ -47,12 +47,11 @@ pub struct Span<'a> { /// Configuration options for [`into_spans`] #[derive(Default)] -#[non_exhaustive] pub struct Config { /// Turn sequences of more than 1 space into exactly 1 space. pub collapse_space: bool, - /// Pretty print: wrap at braces etc - pub pretty_print: bool, + /// Pretty print: wrap at braces etc to the given width + pub pretty_print: Option, } pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {} @@ -63,18 +62,27 @@ pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec> config, res: Vec::new(), depth: 0, + ignore_next_space: false, }; - ast.into_spans(&mut cx); + if let Some(width) = cx.config.pretty_print { + let res = ast.into_pretty_spans(&mut cx); + let _ = res.render_vec(width, &mut cx.res); + } else { + ast.into_spans(&mut cx) + } cx.res } mod private { + use logparse_pretty_print::{PrettyBuilder, PrettyTree, Text}; + use super::*; pub struct Context<'a> { pub config: Config, pub res: Vec>, pub depth: usize, + pub ignore_next_space: bool, } impl<'a> Context<'a> { @@ -86,8 +94,22 @@ mod private { } } + impl<'a> Text<'a> for Span<'a> { + fn from_static_str(s: &'static str) -> Self { + Span { + text: Cow::Borrowed(s), + kind: SpanKind::Text, + } + } + + fn as_str(&self) -> Cow<'_, str> { + self.text.clone() + } + } + pub trait IntoSpansImpl<'a> { fn into_spans(self, cx: &mut Context<'a>); + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>>; } impl<'a, T> IntoSpans<'a> for T where T: IntoSpansImpl<'a> {} @@ -100,14 +122,48 @@ mod private { Separator::DoubleColon => cx.push("::", SpanKind::Separator), } } + + fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + match self { + Separator::Eq => PrettyTree::text(Span { + text: Cow::Borrowed("="), + kind: SpanKind::Separator, + }), + Separator::Colon => PrettyTree::text(Span { + text: Cow::Borrowed(":"), + kind: SpanKind::Separator, + }), + Separator::DoubleColon => PrettyTree::text(Span { + text: Cow::Borrowed("::"), + kind: SpanKind::Separator, + }), + } + } } impl<'a> IntoSpansImpl<'a> for QuoteType { fn into_spans(self, cx: &mut Context<'a>) { match self { - QuoteType::Single => cx.push("'", SpanKind::Separator), - QuoteType::Double => cx.push("\"", SpanKind::Separator), - QuoteType::Backtick => cx.push("`", SpanKind::Separator), + QuoteType::Single => cx.push("'", SpanKind::Surroundings), + QuoteType::Double => cx.push("\"", SpanKind::Surroundings), + QuoteType::Backtick => cx.push("`", SpanKind::Surroundings), + } + } + + fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + match self { + QuoteType::Single => PrettyTree::text(Span { + text: Cow::Borrowed("'"), + kind: SpanKind::Surroundings, + }), + QuoteType::Double => PrettyTree::text(Span { + text: Cow::Borrowed("\""), + kind: SpanKind::Surroundings, + }), + QuoteType::Backtick => PrettyTree::text(Span { + text: Cow::Borrowed("`"), + kind: SpanKind::Surroundings, + }), } } } @@ -135,12 +191,55 @@ mod private { } cx.push(suffix, SpanKind::Surroundings); } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + let Self { + prefix, + ty, + contents, + num_hashtags, + suffix, + } = self; + + let hashtags = "#".repeat(num_hashtags); + + PrettyTree::text(Span { + text: prefix, + kind: SpanKind::Surroundings, + }) + .append(PrettyTree::text(Span { + text: Cow::Owned(hashtags.clone()), + kind: SpanKind::Surroundings, + })) + .group() + .append(ty.into_pretty_spans(cx)) + .group() + .append(PrettyTree::text(Span { + text: contents, + kind: SpanKind::String, + })) + .group() + .append(ty.into_pretty_spans(cx)) + .group() + .append(PrettyTree::text(Span { + text: suffix, + kind: SpanKind::Surroundings, + })) + .group() + } } impl<'a> IntoSpansImpl<'a> for Path<'a> { fn into_spans(self, cx: &mut Context<'a>) { cx.push(self.to_string(), SpanKind::Path) } + + fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + PrettyTree::text(Span { + text: Cow::Owned(self.to_string()), + kind: SpanKind::Path, + }) + } } impl<'a> IntoSpansImpl<'a> for Number<'a> { @@ -152,6 +251,29 @@ mod private { cx.push(suffix, SpanKind::Surroundings); } } + + fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + let main = PrettyTree::text(Span { + text: self.number, + kind: SpanKind::Number, + }); + + let suffix = if let Some(suffix) = self.suffix_without_underscore { + PrettyTree::text(Span { + text: Cow::Borrowed("_"), + kind: SpanKind::Surroundings, + }) + .append(PrettyTree::text(Span { + text: suffix, + kind: SpanKind::Surroundings, + })) + .group() + } else { + PrettyTree::Nil + }; + + main.append(suffix).group() + } } impl<'a> IntoSpansImpl<'a> for Atom<'a> { @@ -160,6 +282,15 @@ mod private { Atom::Text(text) => cx.push(text, SpanKind::Text), } } + + fn into_pretty_spans(self, _cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + match self { + Atom::Text(text) => PrettyTree::text(Span { + text, + kind: SpanKind::Text, + }), + } + } } impl<'a> IntoSpansImpl<'a> for Space<'a> { @@ -171,6 +302,23 @@ mod private { n => cx.push(self.0, SpanKind::Space(n)), } } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + if cx.ignore_next_space { + cx.ignore_next_space = false; + return PrettyTree::Nil; + } + + match self.0.len() { + 0 => PrettyTree::Nil, + 1 => PrettyTree::line_or_space(), + n if cx.config.collapse_space => PrettyTree::line_or_space(), + n => PrettyTree::text(Span { + text: self.0, + kind: SpanKind::Space(n), + }), + } + } } impl<'a> IntoSpansImpl<'a> for Token<'a> { @@ -205,6 +353,50 @@ mod private { } } } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + match self { + Token::True => PrettyTree::text(Span { + text: Cow::Borrowed("true"), + kind: SpanKind::Literal, + }), + Token::False => PrettyTree::text(Span { + text: Cow::Borrowed("true"), + kind: SpanKind::Literal, + }), + Token::None => PrettyTree::text(Span { + text: Cow::Borrowed("None"), + kind: SpanKind::Literal, + }), + Token::Lifetime(lifetime) => PrettyTree::Text(Span { + text: Cow::Borrowed("'"), + kind: SpanKind::Surroundings, + }) + .append(PrettyTree::text(Span { + text: lifetime, + kind: SpanKind::Lifetime, + })) + .group(), + Token::Path(path) => path.into_pretty_spans(cx), + Token::String(string) => string.into_pretty_spans(cx), + Token::Number(number) => number.into_pretty_spans(cx), + Token::Separated { + before, + space_before, + separator, + after, + } => before + .into_pretty_spans(cx) + .append(space_before.into_pretty_spans(cx)) + .group() + .append(separator.into_pretty_spans(cx)) + .group() + .append(after.into_pretty_spans(cx)) + .group(), + Token::Delimited(delimited) => delimited.into_pretty_spans(cx), + Token::Atom(atom) => atom.into_pretty_spans(cx), + } + } } impl<'a> IntoSpansImpl<'a> for Segment<'a> { @@ -216,6 +408,18 @@ mod private { leading_space.into_spans(cx); token.into_spans(cx); } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + let Self { + leading_space, + token, + } = self; + + leading_space + .into_pretty_spans(cx) + .append(token.into_pretty_spans(cx)) + .group() + } } impl<'a> IntoSpansImpl<'a> for Segments<'a> { @@ -229,6 +433,36 @@ mod private { } trailing_space.into_spans(cx); } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + let ignore_start = cx.ignore_next_space; + + let Self { + segments, + trailing_space, + } = self; + let mut res = PrettyTree::Nil; + for segment in segments { + res = res.append(segment.into_pretty_spans(cx)).group(); + } + + // if we ignore the first space, also ignore the last one. + if ignore_start { + cx.ignore_next_space = true; + } + res.append(trailing_space.into_pretty_spans(cx)).group() + } + } + + fn prefix_kind(delimiter: Delimiter, space: &Space<'_>) -> SpanKind { + match delimiter { + Delimiter::Brace => SpanKind::Constructor, + Delimiter::Paren if space.0.is_empty() => SpanKind::Constructor, + Delimiter::Paren => SpanKind::Text, + Delimiter::Bracket => SpanKind::Text, + Delimiter::Angle if space.0.is_empty() => SpanKind::Constructor, + Delimiter::Angle => SpanKind::Text, + } } impl<'a> IntoSpansImpl<'a> for Delimited<'a> { @@ -241,17 +475,7 @@ mod private { match prefix { Some((Atom::Text(text), space)) => { - cx.push( - text, - match delimiter { - Delimiter::Brace => SpanKind::Constructor, - Delimiter::Paren if space.0.is_empty() => SpanKind::Constructor, - Delimiter::Paren => SpanKind::Text, - Delimiter::Bracket => SpanKind::Text, - Delimiter::Angle if space.0.is_empty() => SpanKind::Constructor, - Delimiter::Angle => SpanKind::Text, - }, - ); + cx.push(text, prefix_kind(delimiter, &space)); space.into_spans(cx); } None => {} @@ -275,12 +499,105 @@ mod private { Delimiter::Angle => cx.push(">", SpanKind::Delimiter(cx.depth)), } } + + fn into_pretty_spans(self, cx: &mut Context<'a>) -> PrettyTree<'a, Span<'a>> { + let Self { + prefix, + delimiter, + contents, + } = self; + + let group = contents + .segments + .iter() + .filter(|i| !matches!(i.token, Token::Atom(..))) + .count() + <= 1; + + let prefix = match prefix { + Some((Atom::Text(text), space)) => PrettyTree::text(Span { + text, + kind: prefix_kind(delimiter, &space), + }) + .append(space.into_pretty_spans(cx)) + .group(), + None => PrettyTree::Nil, + }; + + let open = match delimiter { + Delimiter::Paren => Span { + text: Cow::Borrowed("("), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Bracket => Span { + text: Cow::Borrowed("["), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Brace => Span { + text: Cow::Borrowed("{"), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Angle => Span { + text: Cow::Borrowed("<"), + kind: SpanKind::Delimiter(cx.depth), + }, + }; + + let close = match delimiter { + Delimiter::Paren => Span { + text: Cow::Borrowed(")"), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Bracket => Span { + text: Cow::Borrowed("]"), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Brace => Span { + text: Cow::Borrowed("}"), + kind: SpanKind::Delimiter(cx.depth), + }, + Delimiter::Angle => Span { + text: Cow::Borrowed(">"), + kind: SpanKind::Delimiter(cx.depth), + }, + }; + + cx.depth += 1; + if !group { + cx.ignore_next_space = true; + } + let contents = contents.into_pretty_spans(cx); + cx.depth -= 1; + + if group { + prefix + .append(PrettyTree::text(open)) + .group() + .append(contents) + .group() + .append(PrettyTree::text(close)) + } else { + prefix + .append(PrettyTree::text(open)) + .group() + .append(PrettyTree::Hardline) + .group() + .append(contents) + .group() + .nest(4) + .group() + .append(PrettyTree::Hardline) + .group() + .append(PrettyTree::text(close)) + .group() + } + } } } #[cfg(test)] mod tests { - use insta::assert_debug_snapshot; + use insta::{assert_debug_snapshot, assert_snapshot}; use super::SpanKind; use crate::{Config, into_spans, parse_input}; @@ -291,6 +608,7 @@ mod tests { res, Config { collapse_space: true, + ..Default::default() }, ) .into_iter() @@ -298,6 +616,73 @@ mod tests { .collect() } + fn pretty_print(input: &str, n: usize) -> String { + let res = parse_input(input).unwrap(); + into_spans( + res, + Config { + pretty_print: Some(n), + ..Default::default() + }, + ) + .into_iter() + .map(|i| i.text.into_owned()) + .collect::() + } + + #[test] + fn pretty_print_ex1() { + assert_snapshot!(pretty_print( + r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 60 + ), @" + def_id=DefId( + 0:3 ~ unsized_coercion[10fa]::Trait + ) + "); + assert_snapshot!(pretty_print( + r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 30 + ), @" + def_id=DefId( + 0:3 ~ + unsized_coercion[10fa]::Trait + ) + "); + assert_snapshot!(pretty_print( + r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#, 10 + ), @" + def_id=DefId( + 0:3 ~ + unsized_coercion[10fa]::Trait + ) + "); + } + + #[test] + fn pretty_print_ex2() { + assert_snapshot!(pretty_print( + r#"stability: inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:16:16: 16:30 (#0) of stability=Some(Stability { level: Stable { since: Version(RustcVersion { major: 1, minor: 0, patch: 0 }), allowed_through_unstable_modules: None }, feature: "rust1" })"#, 60 + ), @r#" + stability: inspecting + def_id=DefId( + 3:662 ~ alloc[ef11]::boxed::Box + ) span=tests/ui/impl-trait/unsized_coercion.rs:16:16: 16:30 + (#0) of + stability=Some(Stability { + + level: + Stable { + + since: + Version(RustcVersion { + major: 1, minor: 0, patch: 0 + }), allowed_through_unstable_modules: None + + }, feature: "rust1" + + }) + "#); + } + #[test] fn spans_ex1() { assert_debug_snapshot!(spans( @@ -418,7 +803,7 @@ mod tests { ), ( "\"", - Separator, + Surroundings, ), ( "MetaSized", @@ -426,7 +811,7 @@ mod tests { ), ( "\"", - Separator, + Surroundings, ), ( "", diff --git a/pretty-print/projects/pretty-print/Cargo.toml b/pretty-print/projects/pretty-print/Cargo.toml index 3cfa4ca..b46cea4 100644 --- a/pretty-print/projects/pretty-print/Cargo.toml +++ b/pretty-print/projects/pretty-print/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "pretty-print" -version = "0.1.8" -authors = ["Aster <192607617@qq.com>"] +name = "logparse-pretty-print" +version = "0.1.0" +authors = ["Aster <192607617@qq.com>", "Jana Dönszelmann "] description = "pretty print tree" -repository = "https://github.com/oovm/pretty-print" -documentation = "https://docs.rs/pretty-print" -readme = "Readme.md" +repository = "https://git.donsz.nl/jana/logviewer" +documentation = "https://docs.rs/logparse-pretty-print" +readme = "../../Readme.md" license = "MPL-2.0" edition = "2021" @@ -14,12 +14,9 @@ unicode-segmentation = "1.10.1" [dependencies.color-ansi] version = "0.1.0" -#default-features = false -#path = 'C:\Users\Dell\CLionProjects\color-rs\projects\color-ansi' [dev-dependencies] - [features] default = ["std"] -std = [] \ No newline at end of file +std = [] diff --git a/pretty-print/projects/pretty-print/src/helpers/hard_block.rs b/pretty-print/projects/pretty-print/src/helpers/hard_block.rs index a005f98..9b956a7 100644 --- a/pretty-print/projects/pretty-print/src/helpers/hard_block.rs +++ b/pretty-print/projects/pretty-print/src/helpers/hard_block.rs @@ -1,3 +1,7 @@ +use std::fmt::Debug; + +use crate::Text; + use super::*; /// A soft block is a block that is not required to be on a new line. @@ -10,8 +14,8 @@ use super::*; /// b, /// } /// ``` -#[derive(Clone, Debug)] -pub struct HardBlock { +#[derive(Clone)] +pub struct HardBlock<'a, T> { /// The indentation of the soft block pub indent: usize, /// The left hand side of the soft block @@ -19,10 +23,21 @@ pub struct HardBlock { /// The right hand side of the soft block pub rhs: &'static str, /// The joint node of the soft block - pub joint: PrettyTree, + pub joint: PrettyTree<'a, T>, } -impl HardBlock { +impl<'a, T: Text<'a> + Debug> Debug for HardBlock<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HardBlock") + .field("indent", &self.indent) + .field("lhs", &self.lhs) + .field("rhs", &self.rhs) + .field("joint", &self.joint) + .finish() + } +} + +impl<'a, T: Text<'a>> HardBlock<'a, T> { /// Build a new soft block pub fn new(lhs: &'static str, rhs: &'static str) -> Self { Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space() } @@ -40,14 +55,14 @@ impl HardBlock { Self::new("{", "}") } /// Set the joint node of the soft block - pub fn with_joint(self, joint: PrettyTree) -> Self { + pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self { Self { joint, ..self } } } -impl HardBlock { +impl<'a, T: Clone + Text<'a>> HardBlock<'a, T> { /// Join a slice of pretty printables with the soft block - pub fn join_slice(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree { + pub fn join_slice>(&self, slice: &[P], theme: &PrettyProvider) -> PrettyTree<'a, T> { let mut outer = PrettySequence::new(5); outer += self.lhs; outer += PrettyTree::Hardline; diff --git a/pretty-print/projects/pretty-print/src/helpers/k_and_r_bracket.rs b/pretty-print/projects/pretty-print/src/helpers/k_and_r_bracket.rs index 8bf51fa..8f6c2d8 100644 --- a/pretty-print/projects/pretty-print/src/helpers/k_and_r_bracket.rs +++ b/pretty-print/projects/pretty-print/src/helpers/k_and_r_bracket.rs @@ -1,5 +1,5 @@ use super::*; -use crate::PrettyBuilder; +use crate::{PrettyBuilder, Text}; /// `K & R` style brackets /// @@ -28,15 +28,15 @@ impl KAndRBracket { Self { head_space: true, bracket_l: "{", bracket_r: "}" } } /// Build a bracketed block - pub fn build<'a, I>( + pub fn build<'b, 'a, I, T: Text<'a>>( &self, items: &[I], - allocator: &'a PrettyProvider, - inline_join: PrettyTree, - block_join: PrettyTree, - ) -> PrettyTree + allocator: &'b PrettyProvider, + inline_join: PrettyTree<'a, T>, + block_join: PrettyTree<'a, T>, + ) -> PrettyTree<'a, T> where - I: PrettyPrint, + I: PrettyPrint<'a, T>, { let mut output = PrettySequence::new(5); if self.head_space { diff --git a/pretty-print/projects/pretty-print/src/helpers/sequence.rs b/pretty-print/projects/pretty-print/src/helpers/sequence.rs index 246eae0..4a3530c 100644 --- a/pretty-print/projects/pretty-print/src/helpers/sequence.rs +++ b/pretty-print/projects/pretty-print/src/helpers/sequence.rs @@ -1,60 +1,70 @@ +use std::fmt::Debug; + +use crate::Text; + use super::*; /// The document sequence type. -#[derive(Clone, Debug, Default)] -pub struct PrettySequence { - items: Vec, +#[derive(Clone, Default)] +pub struct PrettySequence<'a, T> { + items: Vec>, } -impl PrettySequence { +impl<'a, T: Text<'a> + Debug> Debug for PrettySequence<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PrettySequence").field("items", &self.items).finish() + } +} + +impl<'a, T> PrettySequence<'a, T> { /// Create a new sequence with the given capacity. pub fn new(capacity: usize) -> Self { Self { items: Vec::with_capacity(capacity) } } /// Create a new sequence with the given capacity. - pub fn push(&mut self, item: T) + pub fn push(&mut self, item: U) where - T: Into, + U: Into>, { self.items.push(item.into()); } /// Create a new sequence with the given capacity. - pub fn extend(&mut self, items: I) + pub fn extend(&mut self, items: I) where - I: IntoIterator, - T: Into, + I: IntoIterator, + S: Into>, { self.items.extend(items.into_iter().map(|x| x.into())); } } -impl PrettyBuilder for PrettySequence { - fn flat_alt(self, flat: E) -> PrettyTree +impl<'a, T: Text<'a>> PrettyBuilder<'a, T> for PrettySequence<'a, T> { + fn flat_alt(self, flat: E) -> PrettyTree<'a, T> where - E: Into, + E: Into>, { PrettyTree::from(self).flat_alt(flat) } - fn indent(self, indent: usize) -> PrettyTree { + fn indent(self, indent: usize) -> PrettyTree<'a, T> { PrettyTree::from(self).indent(indent) } - fn nest(self, offset: isize) -> PrettyTree { + fn nest(self, offset: isize) -> PrettyTree<'a, T> { PrettyTree::from(self).nest(offset) } } -impl AddAssign for PrettySequence +impl<'a, U, T> AddAssign for PrettySequence<'a, T> where - T: Into, + U: Into>, { - fn add_assign(&mut self, rhs: T) { + fn add_assign(&mut self, rhs: U) { self.push(rhs); } } -impl From for PrettyTree { - fn from(value: PrettySequence) -> Self { +impl<'a, T: Text<'a>> From> for PrettyTree<'a, T> { + fn from(value: PrettySequence<'a, T>) -> Self { Self::concat(value.items) } } diff --git a/pretty-print/projects/pretty-print/src/helpers/soft_block.rs b/pretty-print/projects/pretty-print/src/helpers/soft_block.rs index d53f716..094a019 100644 --- a/pretty-print/projects/pretty-print/src/helpers/soft_block.rs +++ b/pretty-print/projects/pretty-print/src/helpers/soft_block.rs @@ -1,3 +1,7 @@ +use std::fmt::Debug; + +use crate::Text; + use super::*; /// A soft block is a block that is not required to be on a new line. @@ -10,8 +14,8 @@ use super::*; /// b, /// } /// ``` -#[derive(Clone, Debug)] -pub struct SoftBlock { +#[derive(Clone)] +pub struct SoftBlock<'a, T> { /// The indentation of the soft block pub indent: usize, /// The left hand side of the soft block @@ -19,12 +23,24 @@ pub struct SoftBlock { /// The right hand side of the soft block pub rhs: &'static str, /// The joint node of the soft block - pub joint: PrettyTree, + pub joint: PrettyTree<'a, T>, /// The tail node of the soft block - pub tail: PrettyTree, + pub tail: PrettyTree<'a, T>, } -impl SoftBlock { +impl<'a, T: Text<'a> + Debug> Debug for SoftBlock<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SoftBlock") + .field("indent", &self.indent) + .field("lhs", &self.lhs) + .field("rhs", &self.rhs) + .field("joint", &self.joint) + .field("tail", &self.tail) + .finish() + } +} + +impl<'a, T: Text<'a>> SoftBlock<'a, T> { /// Build a new soft block pub fn new(lhs: &'static str, rhs: &'static str) -> Self { Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space(), tail: PrettyTree::Nil } @@ -46,14 +62,14 @@ impl SoftBlock { Self::new("{", "}") } /// Set the joint node of the soft block - pub fn with_joint(self, joint: PrettyTree) -> Self { + pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self { Self { joint, ..self } } } -impl SoftBlock { +impl<'a, T: Text<'a> + Clone> SoftBlock<'a, T> { /// Join a slice of pretty printables with the soft block - pub fn join_slice(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree { + pub fn join_slice>(&self, slice: &[U], theme: &PrettyProvider) -> PrettyTree<'a, T> { let mut outer = PrettySequence::new(5); outer += self.lhs; outer += PrettyTree::line_or_space(); diff --git a/pretty-print/projects/pretty-print/src/lib.rs b/pretty-print/projects/pretty-print/src/lib.rs index 616dc58..c681f60 100644 --- a/pretty-print/projects/pretty-print/src/lib.rs +++ b/pretty-print/projects/pretty-print/src/lib.rs @@ -11,21 +11,23 @@ extern crate core; pub mod helpers; mod providers; mod render; +mod text; mod traits; mod tree; pub use self::render::{ - write_fmt::{BufferWrite, FmtWrite}, PrettyFormatter, Render, RenderAnnotated, + write_fmt::{BufferWrite, FmtWrite}, }; #[cfg(feature = "std")] -pub use crate::render::write_io::{IoWrite, TerminalWriter}; +pub use crate::render::write_io::{IoWrite, TerminalWriter, VecWrite}; pub use crate::{ providers::PrettyProvider, - traits::{printer::PrettyPrint, PrettyBuilder}, + traits::{PrettyBuilder, printer::PrettyPrint}, tree::PrettyTree, }; pub use color_ansi::*; +pub use text::Text; /// Concatenates a number of documents (or values that can be converted into a document via the /// `Pretty` trait, like `&str`) diff --git a/pretty-print/projects/pretty-print/src/providers/mod.rs b/pretty-print/projects/pretty-print/src/providers/mod.rs index 91ea91e..c167253 100644 --- a/pretty-print/projects/pretty-print/src/providers/mod.rs +++ b/pretty-print/projects/pretty-print/src/providers/mod.rs @@ -1,5 +1,5 @@ -use crate::{PrettyPrint, PrettyTree}; -use alloc::{borrow::Cow, rc::Rc}; +use crate::{PrettyPrint, PrettyTree, Text}; +use alloc::rc::Rc; use color_ansi::AnsiStyle; use core::fmt::{Debug, Formatter}; @@ -57,112 +57,110 @@ impl PrettyProvider { self.width = width; } /// Gets the width of the document. - pub fn text(&self, text: S) -> PrettyTree + pub fn text<'a, S, T>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text) } /// Gets the width of the document. - pub fn custom(&self, text: S, style: Rc) -> PrettyTree + pub fn custom<'a, S, T: Text<'a>>(&self, text: S, style: Rc) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(style) } /// Allocate a document containing the given text. - pub fn keyword(&self, text: S) -> PrettyTree + pub fn keyword<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.keyword.clone()) } /// Allocate a document containing the given text. - pub fn identifier(&self, text: S) -> PrettyTree + pub fn identifier<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.operator.clone()) } /// Allocate a document containing the given text. - pub fn generic(&self, text: S) -> PrettyTree + pub fn generic<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.macros.clone()) } /// Allocate a document containing the given text. - pub fn variable(&self, text: S, mutable: bool) -> PrettyTree + pub fn variable<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { if mutable { PrettyTree::text(text).annotate(self.local_mut.clone()) - } - else { + } else { PrettyTree::text(text).annotate(self.local.clone()) } } /// Allocate a document containing the given text. - pub fn argument(&self, text: S, mutable: bool) -> PrettyTree + pub fn argument<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { if mutable { PrettyTree::text(text).annotate(self.argument_mut.clone()) - } - else { + } else { PrettyTree::text(text).annotate(self.argument.clone()) } } /// Allocate a document containing the given text. - pub fn operator(&self, text: S) -> PrettyTree + pub fn operator<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.operator.clone()) } /// Allocate a document containing the given text. - pub fn string(&self, text: S) -> PrettyTree + pub fn string<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.string.clone()) } /// Allocate a document containing the given text. - pub fn annotation(&self, text: S) -> PrettyTree + pub fn annotation<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.macros.clone()) } /// Allocate a document containing the given text. - pub fn number(&self, text: S) -> PrettyTree + pub fn number<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.number.clone()) } /// Allocate a document containing the given text. - pub fn structure(&self, text: S) -> PrettyTree + pub fn structure<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.structure.clone()) } /// Allocate a document containing the given text. - pub fn variant(&self, text: S) -> PrettyTree + pub fn variant<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.variant.clone()) } /// Allocate a document containing the given text. - pub fn interface(&self, text: S) -> PrettyTree + pub fn interface<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T> where - S: Into>, + S: Into, { PrettyTree::text(text).annotate(self.interface.clone()) } @@ -178,11 +176,11 @@ impl PrettyProvider { /// let theme = PrettyProvider::new(80); /// theme.join(vec!["a", "b", "c"], ", "); /// ``` - pub fn join(&self, iter: I, joint: T2) -> PrettyTree + pub fn join<'a, I, T1, T2, T: Text<'a>>(&self, iter: I, joint: T2) -> PrettyTree<'a, T> where I: IntoIterator, - T1: PrettyPrint, - T2: PrettyPrint, + T1: PrettyPrint<'a, T>, + T2: PrettyPrint<'a, T>, { PrettyTree::join(iter.into_iter().map(|x| x.pretty(self)), joint.pretty(self)) } @@ -195,10 +193,10 @@ impl PrettyProvider { /// let theme = PrettyProvider::new(80); /// theme.join(&["a", "b", "c"], ", "); /// ``` - pub fn join_slice(&self, iter: &[I], joint: T) -> PrettyTree + pub fn join_slice<'a, I, U, T: Text<'a>>(&self, iter: &[I], joint: U) -> PrettyTree<'a, T> where - I: PrettyPrint, - T: PrettyPrint, + I: PrettyPrint<'a, T>, + U: PrettyPrint<'a, T>, { PrettyTree::join(iter.iter().map(|s| s.pretty(self)), joint.pretty(self)) } @@ -211,10 +209,10 @@ impl PrettyProvider { /// let theme = PrettyProvider::new(80); /// theme.concat(vec!["1", "2", "3"]); /// ``` - pub fn concat(&self, iter: I) -> PrettyTree + pub fn concat<'a, I, U, T: Text<'a>>(&self, iter: I) -> PrettyTree<'a, T> where - I: IntoIterator, - T: PrettyPrint, + I: IntoIterator, + U: PrettyPrint<'a, T>, { PrettyTree::concat(iter.into_iter().map(|x| x.pretty(self))) } @@ -227,9 +225,9 @@ impl PrettyProvider { /// let theme = PrettyProvider::new(80); /// theme.concat_slice(&["1", "2", "3"]); /// ``` - pub fn concat_slice(&self, iter: &[T]) -> PrettyTree + pub fn concat_slice<'a, U, T: Text<'a>>(&self, iter: &[U]) -> PrettyTree<'a, T> where - T: PrettyPrint, + U: PrettyPrint<'a, T>, { PrettyTree::concat(iter.iter().map(|s| s.pretty(self))) } diff --git a/pretty-print/projects/pretty-print/src/render/mod.rs b/pretty-print/projects/pretty-print/src/render/mod.rs index e92e0b2..f05e2e2 100644 --- a/pretty-print/projects/pretty-print/src/render/mod.rs +++ b/pretty-print/projects/pretty-print/src/render/mod.rs @@ -1,7 +1,10 @@ -use crate::{BufferWrite, PrettyTree}; +use crate::{BufferWrite, PrettyTree, Text}; use alloc::{rc::Rc, vec, vec::Vec}; use color_ansi::AnsiStyle; -use core::fmt::{Debug, Display, Formatter}; +use core::{ + fmt::{Debug, Display, Formatter}, + slice, +}; #[cfg(feature = "std")] pub mod write_io; @@ -9,40 +12,36 @@ pub mod write_io; pub mod write_fmt; /// Trait representing the operations necessary to render a document -pub trait Render { +pub trait Render<'a, T> { /// The type of the output type Error; - /// Write a string to the output - fn write_str(&mut self, s: &str) -> Result; + /// Write to the output + fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error>; - /// Write a character to the output - fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> { - while !s.is_empty() { - let count = self.write_str(s)?; - s = &s[count..]; - } - Ok(()) - } - - /// Write a character to the output + /// Emit an error fn fail_doc(&self) -> Self::Error; } /// The given text, which must not contain line breaks. -#[derive(Debug)] -pub struct PrettyFormatter<'a> { - tree: &'a PrettyTree, +pub struct PrettyFormatter<'b, 'a, T> { + tree: &'b PrettyTree<'a, T>, width: usize, } -impl<'a> Display for PrettyFormatter<'a> { +impl<'a, 'b, T: Text<'a> + Debug> Debug for PrettyFormatter<'b, 'a, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PrettyFormatter").field("tree", &self.tree).field("width", &self.width).finish() + } +} + +impl<'a, 'b, T: Text<'a>> Display for PrettyFormatter<'b, 'a, T> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { self.tree.render_fmt(self.width, f) } } -impl PrettyTree { +impl<'a, T> PrettyTree<'a, T> { /// Returns a value which implements `std::fmt::Display` /// /// ``` @@ -52,13 +51,13 @@ impl PrettyTree { /// assert_eq!(format!("{}", doc.pretty(80)), "hello world"); /// ``` #[inline] - pub fn pretty(&self, width: usize) -> PrettyFormatter<'_> { + pub fn pretty(&self, width: usize) -> PrettyFormatter<'_, 'a, T> { PrettyFormatter { tree: self, width } } } /// Trait representing the operations necessary to write an annotated document. -pub trait RenderAnnotated: Render { +pub trait RenderAnnotated<'a, T>: Render<'a, T> { /// Push an annotation onto the stack fn push_annotation(&mut self, annotation: Rc) -> Result<(), Self::Error>; /// Pop an annotation from the stack @@ -78,13 +77,20 @@ macro_rules! make_spaces { pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,); -fn append_docs2(ldoc: Rc, rdoc: Rc, mut consumer: impl FnMut(Rc)) -> Rc { +fn append_docs2<'a, T>( + ldoc: Rc>, + rdoc: Rc>, + mut consumer: impl FnMut(Rc>), +) -> Rc> { let d = append_docs(rdoc, &mut consumer); consumer(d); append_docs(ldoc, &mut consumer) } -fn append_docs(mut doc: Rc, consumer: &mut impl FnMut(Rc)) -> Rc { +fn append_docs<'a, T>( + mut doc: Rc>, + consumer: &mut impl FnMut(Rc>), +) -> Rc> { loop { // Since appended documents often appear in sequence on the left side we // gain a slight performance increase by batching these pushes (avoiding @@ -100,9 +106,9 @@ fn append_docs(mut doc: Rc, consumer: &mut impl FnMut(Rc } } -pub fn best(doc: Rc, width: usize, out: &mut W) -> Result<(), W::Error> +pub fn best<'a, W, T: Text<'a>>(doc: Rc>, width: usize, out: &mut W) -> Result<(), W::Error> where - W: RenderAnnotated, + W: RenderAnnotated<'a, T>, W: ?Sized, { Best { @@ -123,43 +129,44 @@ enum Mode { Flat, } -struct RenderCommand { +struct RenderCommand<'a, T> { indent: usize, mode: Mode, - node: Rc, + node: Rc>, } -fn write_newline(ind: usize, out: &mut W) -> Result<(), W::Error> +fn write_newline<'a, W, T: Text<'a>>(ind: usize, out: &mut W) -> Result<(), W::Error> where - W: ?Sized + Render, + W: ?Sized + Render<'a, T>, { - out.write_str_all("\n")?; + out.write_all(&[T::newline()])?; write_spaces(ind, out) } -fn write_spaces(spaces: usize, out: &mut W) -> Result<(), W::Error> +fn write_spaces<'a, W, T: Text<'a>>(spaces: usize, out: &mut W) -> Result<(), W::Error> where - W: ?Sized + Render, + W: ?Sized + Render<'a, T>, { let mut inserted = 0; while inserted < spaces { let insert = core::cmp::min(SPACES.len(), spaces - inserted); - inserted += out.write_str(&SPACES[..insert])?; + out.write_all(&[T::from_static_spaces(&SPACES[..insert])])?; + inserted += insert; } Ok(()) } -struct Best { +struct Best<'a, T> { pos: usize, - back_cmds: Vec, - front_cmds: Vec>, + back_cmds: Vec>, + front_cmds: Vec>>, annotation_levels: Vec, width: usize, } -impl Best { - fn fitting(&mut self, next: Rc, mut pos: usize, ind: usize) -> bool { +impl<'a, T: Text<'a>> Best<'a, T> { + fn fitting(&mut self, next: Rc>, mut pos: usize, ind: usize) -> bool { let mut bidx = self.back_cmds.len(); self.front_cmds.clear(); // clear from previous calls from best self.front_cmds.push(next); @@ -196,14 +203,8 @@ impl Best { return false; } } - PrettyTree::StaticText(str) => { - pos += str.len(); - if pos > self.width { - return false; - } - } - PrettyTree::Text(ref str) => { - pos += str.len(); + PrettyTree::Text(ref s) => { + pos += s.len(); if pos > self.width { return false; } @@ -240,7 +241,7 @@ impl Best { fn best(&mut self, top: usize, out: &mut W) -> Result where - W: RenderAnnotated, + W: RenderAnnotated<'a, T>, W: ?Sized, { let mut fits = true; @@ -302,24 +303,14 @@ impl Best { } PrettyTree::RenderLength { length: len, body: doc } => match doc.as_ref() { PrettyTree::Text(s) => { - out.write_str_all(s)?; - self.pos += len; - fits &= self.pos <= self.width; - } - PrettyTree::StaticText(s) => { - out.write_str_all(s)?; + out.write_all(slice::from_ref(s))?; self.pos += len; fits &= self.pos <= self.width; } _ => unreachable!(), }, PrettyTree::Text(ref s) => { - out.write_str_all(s)?; - self.pos += s.len(); - fits &= self.pos <= self.width; - } - PrettyTree::StaticText(s) => { - out.write_str_all(s)?; + out.write_all(slice::from_ref(s))?; self.pos += s.len(); fits &= self.pos <= self.width; } diff --git a/pretty-print/projects/pretty-print/src/render/write_fmt.rs b/pretty-print/projects/pretty-print/src/render/write_fmt.rs index 89868a7..3afb176 100644 --- a/pretty-print/projects/pretty-print/src/render/write_fmt.rs +++ b/pretty-print/projects/pretty-print/src/render/write_fmt.rs @@ -1,7 +1,7 @@ -use crate::{render::Annotation, Render, RenderAnnotated}; +use crate::{Render, RenderAnnotated, Text, render::Annotation}; use alloc::rc::Rc; use color_ansi::AnsiStyle; -use core::fmt::{Debug, Formatter}; +use core::fmt::{Debug, Error, Formatter}; /// Writes to something implementing `std::fmt::Write` pub struct FmtWrite { @@ -9,27 +9,27 @@ pub struct FmtWrite { } /// Represents a terminal writer. #[derive(Debug)] -pub struct BufferWrite { - buffer: String, +pub struct BufferWrite { + buffer: Vec, annotations: Vec<(usize, Annotation)>, } -impl BufferWrite { +impl<'a, T: Text<'a>> BufferWrite { /// Creates a new terminal writer. pub fn new(capacity: usize) -> Self { - BufferWrite { buffer: String::with_capacity(capacity), annotations: Vec::new() } + BufferWrite { buffer: Vec::with_capacity(capacity), annotations: Vec::new() } } /// Creates a new terminal writer. pub fn render(&mut self, render: &mut W) -> Result<(), W::Error> where - W: RenderAnnotated, + W: RenderAnnotated<'a, T>, W: ?Sized, { let mut start = 0; for (end, annotation) in &self.annotations { let s = &self.buffer[start..*end]; if !s.is_empty() { - render.write_str_all(s)?; + render.write_all(s)?; } start = *end; match annotation { @@ -39,31 +39,26 @@ impl BufferWrite { } let s = &self.buffer[start..]; if !s.is_empty() { - render.write_str_all(s)?; + render.write_all(s)?; } Ok(()) } } -impl Render for BufferWrite { - type Error = core::fmt::Error; +impl<'a, T: Clone> Render<'a, T> for BufferWrite { + type Error = Error; - fn write_str(&mut self, s: &str) -> Result { - self.buffer.push_str(s); - Ok(s.len()) - } - - fn write_str_all(&mut self, s: &str) -> Result<(), Self::Error> { - self.buffer.push_str(s); + fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error> { + self.buffer.extend(s.iter().cloned()); Ok(()) } fn fail_doc(&self) -> Self::Error { - core::fmt::Error::default() + Error } } -impl RenderAnnotated for FmtWrite +impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for FmtWrite where W: core::fmt::Write, { @@ -76,7 +71,7 @@ where } } -impl RenderAnnotated for BufferWrite { +impl<'a, T: Clone> RenderAnnotated<'a, T> for BufferWrite { fn push_annotation(&mut self, annotation: Rc) -> Result<(), Self::Error> { self.annotations.push((self.buffer.len(), Annotation::Push(annotation))); Ok(()) @@ -101,21 +96,20 @@ impl FmtWrite { } } -impl Render for FmtWrite +impl<'a, W, T: Text<'a>> Render<'a, T> for FmtWrite where W: core::fmt::Write, { - type Error = core::fmt::Error; + type Error = Error; - fn write_str(&mut self, s: &str) -> Result { - self.write_str_all(s).map(|_| s.len()) - } - - fn write_str_all(&mut self, s: &str) -> core::fmt::Result { - self.upstream.write_str(s) + fn write_all(&mut self, s: &[T]) -> core::fmt::Result { + for i in s { + self.upstream.write_str(i.as_str().as_ref())? + } + Ok(()) } fn fail_doc(&self) -> Self::Error { - core::fmt::Error + Error } } diff --git a/pretty-print/projects/pretty-print/src/render/write_io.rs b/pretty-print/projects/pretty-print/src/render/write_io.rs index e9c25a4..a662806 100644 --- a/pretty-print/projects/pretty-print/src/render/write_io.rs +++ b/pretty-print/projects/pretty-print/src/render/write_io.rs @@ -1,8 +1,13 @@ +#[cfg(feature = "std")] +use crate::Text; use crate::{Render, RenderAnnotated}; use alloc::rc::Rc; use color_ansi::{AnsiAbility, AnsiStyle, AnsiWriter}; use core::fmt::{Debug, Formatter}; -use std::io::{Error, ErrorKind, Write}; + +#[cfg(feature = "std")] +use std::io::{Error, Write}; + /// Represents a terminal writer. pub struct TerminalWriter { color_stack: Vec>, @@ -28,29 +33,29 @@ impl IoWrite { } #[cfg(feature = "std")] -impl Render for IoWrite - where - W: std::io::Write, +impl<'a, W, T: Text<'a>> Render<'a, T> for IoWrite +where + W: std::io::Write, { type Error = std::io::Error; - fn write_str(&mut self, s: &str) -> std::io::Result { - self.upstream.write(s.as_bytes()) - } + fn write_all(&mut self, s: &[T]) -> std::io::Result<()> { + for i in s { + self.upstream.write_all(i.as_str().as_bytes())?; + } - fn write_str_all(&mut self, s: &str) -> std::io::Result<()> { - self.upstream.write_all(s.as_bytes()) + Ok(()) } fn fail_doc(&self) -> Self::Error { - std::io::Error::new(std::io::ErrorKind::Other, "Document failed to render") + std::io::Error::other("Document failed to render") } } #[cfg(feature = "std")] -impl RenderAnnotated for IoWrite - where - W: std::io::Write, +impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for IoWrite +where + W: std::io::Write, { fn push_annotation(&mut self, _: Rc) -> Result<(), Self::Error> { Ok(()) @@ -79,26 +84,26 @@ impl TerminalWriter { } } -impl Render for TerminalWriter +impl<'a, W, T: Text<'a>> Render<'a, T> for TerminalWriter where W: Write, { type Error = Error; - fn write_str(&mut self, s: &str) -> std::io::Result { - self.upstream.write(s.as_bytes()) - } + fn write_all(&mut self, s: &[T]) -> std::io::Result<()> { + for i in s { + self.upstream.write_all(i.as_str().as_bytes())?; + } - fn write_str_all(&mut self, s: &str) -> std::io::Result<()> { - self.upstream.write_all(s.as_bytes()) + Ok(()) } fn fail_doc(&self) -> Self::Error { - Error::new(ErrorKind::Other, "Document failed to render") + Error::other("Document failed to render") } } -impl RenderAnnotated for TerminalWriter { +impl<'a, W: Write, T: Text<'a>> RenderAnnotated<'a, T> for TerminalWriter { fn push_annotation(&mut self, color: Rc) -> Result<(), Self::Error> { self.color_stack.push(color.clone()); self.upstream.set_style(&color) @@ -112,3 +117,49 @@ impl RenderAnnotated for TerminalWriter { } } } + +/// Writes to something implementing `std::io::Write` +pub struct VecWrite<'v, T> { + upstream: &'v mut Vec, +} + +impl<'v, W> Debug for VecWrite<'v, W> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("VecWrite").finish() + } +} + +impl<'v, T> VecWrite<'v, T> { + /// Creates a new terminal writer. + pub fn new(upstream: &'v mut Vec) -> VecWrite<'v, T> { + VecWrite { upstream } + } +} + +#[cfg(feature = "std")] +impl<'a, T: Text<'a>> Render<'a, T> for VecWrite<'_, T> { + type Error = &'static str; + + fn write_all(&mut self, s: &[T]) -> Result<(), &'static str> { + for i in s { + self.upstream.push(i.clone()); + } + + Ok(()) + } + + fn fail_doc(&self) -> Self::Error { + "Document failed to render" + } +} + +#[cfg(feature = "std")] +impl<'a, T: Text<'a>> RenderAnnotated<'a, T> for VecWrite<'_, T> { + fn push_annotation(&mut self, _: Rc) -> Result<(), Self::Error> { + Ok(()) + } + + fn pop_annotation(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/pretty-print/projects/pretty-print/src/text.rs b/pretty-print/projects/pretty-print/src/text.rs new file mode 100644 index 0000000..a8a3db2 --- /dev/null +++ b/pretty-print/projects/pretty-print/src/text.rs @@ -0,0 +1,58 @@ +use std::borrow::Cow; + +/// Pretty printing can work with arbitrary text-like objects, +/// as long as they implement this trait. +#[allow(clippy::len_without_is_empty)] +pub trait Text<'a>: Sized + Clone + 'a { + /// Turn a &'static str into Self, without any further knowledge about it. + /// + /// Used for a bunch of functions to give a default. + /// Often not the most optimal or even desired implementation. + fn from_static_str(s: &'static str) -> Self; + + /// We foten insert spaces as a static str. + /// + /// By default this is implemented with [`from_static_str`]. + fn from_static_spaces(s: &'static str) -> Self { + Self::from_static_str(s) + } + + /// Turn custom text into a `Cow<'a, str>`. + fn as_str(&self) -> Cow<'_, str>; + + /// Get the length of the custom text. + fn len(&self) -> usize { + str::len(&self.as_str()) + } + + /// A space character + /// + /// By default this is implemented with [`from_static_str`]. + fn space() -> Self { + Self::from_static_str(" ") + } + + /// A newline character + /// + /// By default this is implemented with [`from_static_str`]. + fn newline() -> Self { + Self::from_static_str("\n") + } + + /// A comma and space character + /// + /// By default this is implemented with [`from_static_str`]. + fn comma_space() -> Self { + Self::from_static_str(", ") + } +} + +impl<'a> Text<'a> for Cow<'a, str> { + fn from_static_str(s: &'static str) -> Self { + Cow::Borrowed(s) + } + + fn as_str(&self) -> Cow<'a, str> { + self.clone() + } +} diff --git a/pretty-print/projects/pretty-print/src/traits/mod.rs b/pretty-print/projects/pretty-print/src/traits/mod.rs index 2f458b1..d24db42 100644 --- a/pretty-print/projects/pretty-print/src/traits/mod.rs +++ b/pretty-print/projects/pretty-print/src/traits/mod.rs @@ -1,10 +1,10 @@ -use crate::{providers::PrettyProvider, PrettyTree}; +use crate::{PrettyTree, providers::PrettyProvider}; use alloc::string::String; pub mod printer; /// The `PrettyPrint` trait is implemented by types that can be pretty-printed. -pub trait PrettyBuilder { +pub trait PrettyBuilder<'a, T> { /// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line. /// /// ``` @@ -23,12 +23,12 @@ pub trait PrettyBuilder { /// assert_eq!(doc.1.pretty(100).to_string(), "let x in x"); /// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx"); /// ``` - fn flat_alt(self, inline: E) -> PrettyTree + fn flat_alt(self, inline: E) -> PrettyTree<'a, T> where - E: Into; + E: Into>; /// Acts as `self` when laid out on a single line and acts as `that` when laid out on multiple lines. - fn indent(self, indent: usize) -> PrettyTree; + fn indent(self, indent: usize) -> PrettyTree<'a, T>; /// Increase the indentation level of this document. - fn nest(self, offset: isize) -> PrettyTree; + fn nest(self, offset: isize) -> PrettyTree<'a, T>; } diff --git a/pretty-print/projects/pretty-print/src/traits/printer.rs b/pretty-print/projects/pretty-print/src/traits/printer.rs index ce40a89..d71dc1e 100644 --- a/pretty-print/projects/pretty-print/src/traits/printer.rs +++ b/pretty-print/projects/pretty-print/src/traits/printer.rs @@ -1,13 +1,15 @@ +use crate::Text; + use super::*; /// Marker trait for types that can be pretty printed. -pub trait PrettyPrint { +pub trait PrettyPrint<'a, T: Text<'a>> { /// Build a pretty tree for this type. - fn pretty(&self, theme: &PrettyProvider) -> PrettyTree; + fn pretty(&self, theme: &PrettyProvider) -> PrettyTree<'a, T>; /// Get a pretty string for this type. fn pretty_string(&self, theme: &PrettyProvider) -> String { let mut buffer = String::new(); - if let Err(e) = self.pretty(&theme).render_fmt(theme.get_width(), &mut buffer) { + if let Err(e) = self.pretty(theme).render_fmt(theme.get_width(), &mut buffer) { panic!("Error: {}", e); } buffer @@ -15,7 +17,7 @@ pub trait PrettyPrint { /// Print a pretty string for this type. fn pretty_colorful(&self, theme: &PrettyProvider) -> String { let mut buffer = vec![]; - if let Err(e) = self.pretty(&theme).render_colored(theme.get_width(), &mut buffer) { + if let Err(e) = self.pretty(theme).render_colored(theme.get_width(), &mut buffer) { panic!("Error: {}", e); } match String::from_utf8(buffer) { @@ -25,13 +27,13 @@ pub trait PrettyPrint { } } -impl PrettyPrint for PrettyTree { - fn pretty(&self, _: &PrettyProvider) -> PrettyTree { +impl<'a, T: Text<'a>> PrettyPrint<'a, T> for PrettyTree<'a, T> { + fn pretty(&self, _: &PrettyProvider) -> PrettyTree<'a, T> { self.clone() } } -impl PrettyPrint for &'static str { - fn pretty(&self, _: &PrettyProvider) -> PrettyTree { - PrettyTree::StaticText(*self) +impl<'a, T: Text<'a>> PrettyPrint<'a, T> for &'static str { + fn pretty(&self, _: &PrettyProvider) -> PrettyTree<'a, T> { + PrettyTree::Text(T::from_static_str(self)) } } diff --git a/pretty-print/projects/pretty-print/src/tree/display.rs b/pretty-print/projects/pretty-print/src/tree/display.rs index 44b2745..8645490 100644 --- a/pretty-print/projects/pretty-print/src/tree/display.rs +++ b/pretty-print/projects/pretty-print/src/tree/display.rs @@ -1,18 +1,11 @@ use super::*; -impl Default for PrettyTree { - fn default() -> Self { - Self::Nil - } -} - -impl Clone for PrettyTree { +impl<'a, T: Clone> Clone for PrettyTree<'a, T> { fn clone(&self) -> Self { match self { Self::Nil => Self::Nil, Self::Hardline => Self::Hardline, Self::Text(s) => Self::Text(s.clone()), - Self::StaticText(s) => Self::StaticText(*s), Self::Annotated { style: color, body: doc } => Self::Annotated { style: color.clone(), body: doc.clone() }, Self::Append { lhs, rhs } => Self::Append { lhs: lhs.clone(), rhs: rhs.clone() }, Self::Group { items } => Self::Group { items: items.clone() }, @@ -27,15 +20,18 @@ impl Clone for PrettyTree { } } -impl Debug for PrettyTree { +impl<'a, T> Debug for PrettyTree<'a, T> +where + T: Text<'a> + Debug, +{ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let is_line = |doc: &PrettyTree| match doc { + let is_line = |doc: &PrettyTree<'a, T>| match doc { PrettyTree::MaybeInline { block: flat, inline: alt } => { - matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::StaticText(" "))) + matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Text(t)) if t.as_str() == " ") } _ => false, }; - let is_line_ = |doc: &PrettyTree| match doc { + let is_line_ = |doc: &PrettyTree<'a, T>| match doc { PrettyTree::MaybeInline { block: flat, inline: alt } => { matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Nil)) } @@ -66,7 +62,6 @@ impl Debug for PrettyTree { PrettyTree::Hardline => f.debug_tuple("Hardline").finish(), PrettyTree::RenderLength { body: doc, .. } => doc.fmt(f), PrettyTree::Text(s) => Debug::fmt(s, f), - PrettyTree::StaticText(s) => Debug::fmt(s, f), PrettyTree::Annotated { style: color, body: doc } => f.debug_tuple("Annotated").field(color).field(doc).finish(), PrettyTree::Union { lhs: left, rhs: right } => f.debug_tuple("Union").field(left).field(right).finish(), PrettyTree::Column { .. } => f.debug_tuple("Column(..)").finish(), @@ -76,7 +71,7 @@ impl Debug for PrettyTree { } } -fn append_docs(mut doc: &PrettyTree, consumer: &mut impl FnMut(&PrettyTree)) { +fn append_docs<'a, T>(mut doc: &PrettyTree<'a, T>, consumer: &mut impl FnMut(&PrettyTree<'a, T>)) { loop { match doc { PrettyTree::Append { lhs, rhs } => { diff --git a/pretty-print/projects/pretty-print/src/tree/into.rs b/pretty-print/projects/pretty-print/src/tree/into.rs index 8afff13..a44cdcd 100644 --- a/pretty-print/projects/pretty-print/src/tree/into.rs +++ b/pretty-print/projects/pretty-print/src/tree/into.rs @@ -1,29 +1,29 @@ use super::*; -impl Add for PrettyTree +impl<'a, U, T: Text<'a>> Add for PrettyTree<'a, T> where - T: Into, + U: Into, { type Output = Self; - fn add(self, rhs: T) -> Self::Output { + fn add(self, rhs: U) -> Self::Output { self.append(rhs.into()) } } -impl AddAssign for PrettyTree +impl<'a, U, T: Text<'a>> AddAssign for PrettyTree<'a, T> where - T: Into, + U: Into, { - fn add_assign(&mut self, rhs: T) { + fn add_assign(&mut self, rhs: U) { *self = self.clone().append(rhs.into()); } } -impl From> for PrettyTree +impl<'a, U, T> From> for PrettyTree<'a, T> where - Self: From, + Self: From, { - fn from(x: Option) -> Self { + fn from(x: Option) -> Self { match x { Some(x) => x.into(), None => Self::Nil, @@ -31,20 +31,20 @@ where } } -impl From<()> for PrettyTree { +impl<'a, T> From<()> for PrettyTree<'a, T> { fn from(_: ()) -> Self { Self::Nil } } -impl From<&'static str> for PrettyTree { +impl<'a, T: Text<'a>> From<&'static str> for PrettyTree<'a, T> { fn from(s: &'static str) -> Self { - Self::StaticText(s) + Self::Text(T::from_static_str(s)) } } -impl From for PrettyTree { - fn from(s: String) -> Self { - Self::Text(Rc::from(s)) +impl<'a, T: Text<'a>> From for PrettyTree<'a, T> { + fn from(s: T) -> Self { + Self::Text(s) } } diff --git a/pretty-print/projects/pretty-print/src/tree/mod.rs b/pretty-print/projects/pretty-print/src/tree/mod.rs index 77cfbce..6679349 100644 --- a/pretty-print/projects/pretty-print/src/tree/mod.rs +++ b/pretty-print/projects/pretty-print/src/tree/mod.rs @@ -1,12 +1,11 @@ -use crate::{helpers::PrettySequence, render, FmtWrite, PrettyBuilder, RenderAnnotated}; -use alloc::{borrow::Cow, rc::Rc, string::String}; +use crate::{FmtWrite, PrettyBuilder, RenderAnnotated, Text, helpers::PrettySequence, render}; +use alloc::rc::Rc; use color_ansi::AnsiStyle; use core::{ fmt::{Debug, Formatter}, ops::{Add, AddAssign}, }; -use std::io::Write; -use unicode_segmentation::UnicodeSegmentation; +use std::{borrow::Cow, io::Write}; mod display; mod into; @@ -16,15 +15,15 @@ mod into; /// /// The `T` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how /// it is used -pub enum PrettyTree { +#[derive(Default)] +pub enum PrettyTree<'a, T = Cow<'a, str>> { /// Nothing to show + #[default] Nil, /// A hard line break Hardline, /// A dynamic text document, all newlines are hard line breaks - Text(Rc), - /// A static text document, all newlines are hard line breaks - StaticText(&'static str), + Text(T), /// A document with ansi styles Annotated { /// The style to use for the text @@ -75,51 +74,45 @@ pub enum PrettyTree { /// Concatenates two documents with a line in between Column { /// The first document - invoke: Rc Self>, + invoke: Rc Self + 'a>, }, /// Concatenates two documents with a line in between Nesting { /// The first document - invoke: Rc Self>, + invoke: Rc Self + 'a>, }, /// Concatenates two documents with a line in between Fail, } #[allow(non_upper_case_globals)] -impl PrettyTree { - /// A hard line break - pub const Space: Self = PrettyTree::StaticText(" "); +impl<'a, T: Text<'a>> PrettyTree<'a, T> { /// A line acts like a `\n` but behaves like `space` if it is grouped on a single line. #[inline] pub fn line_or_space() -> Self { - Self::Hardline.flat_alt(Self::Space).into() + Self::Hardline.flat_alt(T::space()) } /// A line acts like `\n` but behaves like `nil` if it is grouped on a single line. #[inline] pub fn line_or_comma() -> Self { - Self::Hardline.flat_alt(PrettyTree::StaticText(", ")).into() + Self::Hardline.flat_alt(PrettyTree::Text(T::comma_space())) } /// Acts like `line` but behaves like `nil` if grouped on a single line #[inline] pub fn line_or_nil() -> Self { - Self::Hardline.flat_alt(Self::Nil).into() + Self::Hardline.flat_alt(Self::Nil) } } -impl PrettyTree { +impl<'a, T> PrettyTree<'a, T> { /// The given text, which must not contain line breaks. #[inline] - pub fn text>>(data: U) -> Self { - match data.into() { - Cow::Borrowed(s) => PrettyTree::StaticText(s), - Cow::Owned(s) => PrettyTree::Text(Rc::from(s)), - } - .with_utf8_len() + pub fn text(data: impl Into) -> Self { + Self::Text(data.into()) } } -impl PrettyTree { +impl<'a, T: Text<'a>> PrettyTree<'a, T> { /// Writes a rendered document to a `std::io::Write` object. #[inline] #[cfg(feature = "std")] @@ -130,6 +123,13 @@ impl PrettyTree { self.render_raw(width, &mut crate::IoWrite::new(out)) } + /// Writes a rendered document to a `std::io::Write` object. + #[inline] + #[cfg(feature = "std")] + pub fn render_vec(&self, width: usize, out: &mut Vec) -> Result<(), &'static str> { + self.render_raw(width, &mut crate::VecWrite::new(out)) + } + /// Writes a rendered document to a `std::fmt::Write` object. #[inline] pub fn render_fmt(&self, width: usize, out: &mut W) -> core::fmt::Result @@ -143,14 +143,14 @@ impl PrettyTree { #[inline] pub fn render_raw(&self, width: usize, out: &mut W) -> Result<(), W::Error> where - W: RenderAnnotated, + W: RenderAnnotated<'a, T>, W: ?Sized, { render::best(Rc::new(self.clone()), width, out) } } -impl PrettyTree { +impl<'a, T: Text<'a>> PrettyTree<'a, T> { /// The given text, which must not contain line breaks. #[inline] #[cfg(feature = "std")] @@ -159,7 +159,7 @@ impl PrettyTree { } } -impl PrettyBuilder for PrettyTree { +impl<'a, T: Text<'a>> PrettyBuilder<'a, T> for PrettyTree<'a, T> { /// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line. /// /// ``` @@ -181,7 +181,7 @@ impl PrettyBuilder for PrettyTree { #[inline] fn flat_alt(self, flat: E) -> Self where - E: Into, + E: Into>, { Self::MaybeInline { block: Rc::new(self), inline: Rc::new(flat.into()) } } @@ -217,7 +217,7 @@ impl PrettyBuilder for PrettyTree { while remaining != 0 { let i = SPACES.len().min(remaining); remaining -= i; - doc = doc.append(PrettyTree::text(&SPACES[..i])) + doc = doc.append(PrettyTree::text(T::from_static_spaces(&SPACES[..i]))) } doc }; @@ -237,28 +237,12 @@ impl PrettyBuilder for PrettyTree { } } -impl PrettyTree { - fn with_utf8_len(self) -> Self { - let s = match &self { - Self::Text(s) => s.as_ref(), - Self::StaticText(s) => s, - // Doc::SmallText(s) => s, - _ => return self, - }; - if s.is_ascii() { - self - } - else { - let grapheme_len = s.graphemes(true).count(); - Self::RenderLength { length: grapheme_len, body: Rc::new(self) } - } - } - +impl<'a, T: Text<'a>> PrettyTree<'a, T> { /// Append the given document after this document. #[inline] pub fn append(self, follow: E) -> Self where - E: Into, + E: Into>, { let rhs = follow.into(); match (&self, &rhs) { @@ -275,11 +259,11 @@ impl PrettyTree { /// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr /// like `RefDoc` or `RcDoc` #[inline] - pub fn join(terms: I, joint: T2) -> PrettyTree + pub fn join(terms: I, joint: T2) -> PrettyTree<'a, T> where I: IntoIterator, - T1: Into, - T2: Into, + T1: Into>, + T2: Into>, { let joint = joint.into(); let mut iter = terms.into_iter().map(|s| s.into()); @@ -295,7 +279,7 @@ impl PrettyTree { pub fn concat(docs: I) -> Self where I: IntoIterator, - I::Item: Into, + I::Item: Into>, { let mut head = Self::Nil; for item in docs.into_iter() { @@ -313,7 +297,7 @@ impl PrettyTree { #[inline] pub fn group(self) -> Self { match self { - Self::Group { .. } | Self::Text(_) | Self::StaticText(_) | Self::Nil => self, + Self::Group { .. } | Self::Text(_) | Self::Nil => self, _ => Self::Group { items: Rc::new(self) }, } } @@ -321,13 +305,13 @@ impl PrettyTree { /// Mark this document as a comment. #[inline] pub fn annotate(self, style: Rc) -> Self { - Self::Annotated { style: style, body: Rc::new(self) } + Self::Annotated { style, body: Rc::new(self) } } /// Mark this document as a hard line break. #[inline] pub fn union(self, other: E) -> Self where - E: Into, + E: Into>, { Self::Union { lhs: Rc::new(self), rhs: Rc::new(other.into()) } } @@ -402,6 +386,7 @@ impl PrettyTree { pub fn width(self, f: F) -> Self where F: Fn(isize) -> Self + Clone + 'static, + T: Clone, { Self::Column { invoke: Rc::new(move |start| { diff --git a/pretty-print/projects/pretty-print/tests/readme.rs b/pretty-print/projects/pretty-print/tests/readme.rs index 978426e..8275503 100644 --- a/pretty-print/projects/pretty-print/tests/readme.rs +++ b/pretty-print/projects/pretty-print/tests/readme.rs @@ -1,5 +1,6 @@ -use pretty_print::*; +#![allow(unused)] use SExp::*; +use pretty_print::*; enum SExp { Atom(u32), List(Vec), @@ -7,11 +8,11 @@ enum SExp { impl SExp { /// Return a pretty printed format of self. - pub fn to_doc(&self) -> PrettyTree { + pub fn to_doc<'a>(&self) -> PrettyTree<'a> { match self { Atom(x) => PrettyTree::text(x.to_string()), List(xs) => PrettyTree::text("(") - .append(PrettyTree::join(xs.into_iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group()) + .append(PrettyTree::join(xs.iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group()) .append(PrettyTree::text(")")), } } diff --git a/pretty-print/projects/pretty-test/Cargo.toml b/pretty-print/projects/pretty-test/Cargo.toml index e786146..dd83772 100644 --- a/pretty-print/projects/pretty-test/Cargo.toml +++ b/pretty-print/projects/pretty-test/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Aster <192607617@qq.com>"] description = "..." repository = "https://github.com/oovm/sub_projects" documentation = "https://docs.rs/sub_projects" -readme = "Readme.md" +readme = "../../Readme.md" license = "MPL-2.0" edition = "2021" exclude = ["package.json", "tests/**"] @@ -14,11 +14,12 @@ exclude = ["package.json", "tests/**"] [dependencies] pretty = "0.12.1" -[dependencies.pretty-print] +[dependencies.logparse-pretty-print] version = "*" default-features = false features = ["std"] path = "../pretty-print" +name = "pretty-print" [dev-dependencies] diff --git a/pretty-print/projects/pretty-test/tests/main.rs b/pretty-print/projects/pretty-test/tests/main.rs index cd09e47..d62977a 100644 --- a/pretty-print/projects/pretty-test/tests/main.rs +++ b/pretty-print/projects/pretty-test/tests/main.rs @@ -1,6 +1,8 @@ #![allow(dead_code, unused)] -use pretty_print::{AnsiColor, AnsiStyle, PrettyBuilder, PrettyTree}; -use std::{io::stdout, rc::Rc}; +use pretty_print::{AnsiColor, AnsiStyle, PrettyBuilder, PrettyTree as RawPrettyTree}; +use std::{borrow::Cow, io::stdout, rc::Rc}; + +type PrettyTree<'a> = RawPrettyTree<'a, Cow<'a, str>>; #[test] fn ready() { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 7e07d71..cd150dc 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,3 +1,5 @@ +#[cfg(target_os = "linux")] +use arboard::SetExtLinux; use crossterm::{ event::{ DisableMouseCapture, EnableMouseCapture, KeyEventKind, KeyEventState, MouseButton, @@ -8,6 +10,8 @@ use crossterm::{ use itertools::Itertools; use logparse::{self as lp, Config, into_spans, parse_input}; use ratatui_themes::{Color, Theme, ThemeName}; +#[cfg(target_os = "linux")] +use std::time::Instant; use std::{ borrow::Cow, fs::{self, DirEntry}, @@ -40,7 +44,7 @@ use ratatui::{ prelude::CrosstermBackend, style::Style, symbols::merge::MergeStrategy, - text::{Line, Span}, + text::{Line, Span, Text}, widgets::{ Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap, }, @@ -121,6 +125,11 @@ pub fn run(logs_dir: PathBuf, compiler_root: Option, theme: ThemeName) } } +enum ZoomTarget { + Field { span: SpanDescriptor, name: String }, + Log, +} + enum Tab { FileChooser { files: Vec, @@ -132,8 +141,7 @@ enum Tab { Empty, Help, Zoom { - span: SpanDescriptor, - name: String, + target: ZoomTarget, value: String, }, } @@ -146,10 +154,20 @@ impl Tab { (Tab::LogViewer(_), Some(path)) => format!("logs of {}", path.display()), (Tab::LogViewer(_), None) => "logs".to_string(), (Tab::Help, _) => "help".to_string(), - (Tab::Zoom { span, name, .. }, _) => match span { - SpanDescriptor::Main => format!("{name}"), - SpanDescriptor::Span(s) => format!("{name} in {s}"), - SpanDescriptor::Numbered(n) => format!("{name} in span #{n}"), + (Tab::Zoom { target, .. }, _) => match target { + ZoomTarget::Log => "value of log".to_string(), + ZoomTarget::Field { + span: SpanDescriptor::Main, + name, + } => format!("{name}"), + ZoomTarget::Field { + span: SpanDescriptor::Span(s), + name, + } => format!("{name} in {s}"), + ZoomTarget::Field { + span: SpanDescriptor::Numbered(n), + name, + } => format!("{name} in span #{n}"), }, } } @@ -256,9 +274,9 @@ impl App { fn handle_current_tab_keycode(&mut self, key: KeyEvent) { match self.tabs.last_mut().unwrap() { - Tab::Help => {} + Tab::Help => self.pop_tab(), Tab::Empty => {} - Tab::Zoom { .. } => {} + Tab::Zoom { .. } => self.pop_tab(), Tab::FileChooser { files, state, @@ -344,7 +362,36 @@ impl App { KeyCode::Char('r') => lv.redo(), KeyCode::Char('z') => { if let Some((span, name, value)) = lv.get_selected_field() { - self.push_tab(Tab::Zoom { span, name, value }); + if let InputState::Target(InputTarget::Fields(..)) = lv.input_state { + self.push_tab(Tab::Zoom { + target: ZoomTarget::Field { span, name }, + value, + }); + } else if let Some((l, _)) = lv.selected() { + let message = l.line_text("".to_string(), &lv.filters).message; + self.push_tab(Tab::Zoom { + target: ZoomTarget::Log, + value: message, + }); + } + } + } + KeyCode::Char('y') => { + if let Some((_, _, value)) = lv.get_selected_field() { + match arboard::Clipboard::new() { + Ok(mut cb) => { + #[allow(unused_mut)] + let mut set = cb.set(); + #[cfg(target_os = "linux")] + let set = set.wait_until( + Instant::now() + .checked_add(Duration::from_millis(200)) + .unwrap(), + ); + set.text(value).unwrap(); + } + Err(e) => panic!("{e}"), + } } } @@ -605,7 +652,7 @@ impl Widget for &mut App { .areas(area); let [_, popup_area, _] = Layout::horizontal([ Constraint::Fill(1), - Constraint::Min(40), + Constraint::Min(80), Constraint::Fill(1), ]) .areas(popup_area); @@ -750,25 +797,44 @@ impl Widget for &mut App { inner }; - if let Ok(parsed) = parse_input(&value) { + if let Ok(parsed) = parse_input(value) { let spans = into_spans( parsed, Config { collapse_space: true, + pretty_print: Some(popup_area.width as usize), }, ); let spans = spans .into_iter() .map(|lp::Span { text, kind }| { - let span = Span::from(text.into_owned()); + let lines = text.split("\n").map(ToString::to_string).collect_vec(); + let styles = &styles; + lines.into_iter().map(move |text| { + let span = Span::from(text.to_string()); - let style = style_span(kind, styles.default, &styles); - span.style(style) + let style = style_span(kind, styles.default, styles); + span.style(style) + }) }) .collect_vec(); - Paragraph::new(Line::from(spans)).render(popup_area, buf); + let mut lines = Vec::new(); + let mut line = Vec::new(); + for mut spans in spans { + if let Some(i) = spans.next() { + line.push(i); + } + + for i in spans { + lines.push(Line::from(line)); + line = vec![i]; + } + } + lines.push(Line::from(line)); + + Text::from(lines).render(popup_area, buf); } else { Paragraph::new(value.clone()).render(popup_area, buf); } diff --git a/src/tui/widgets/fieldtree.rs b/src/tui/widgets/fieldtree.rs index 7e81d95..1f4b9ef 100644 --- a/src/tui/widgets/fieldtree.rs +++ b/src/tui/widgets/fieldtree.rs @@ -15,7 +15,7 @@ use tui_widget_list::{ListBuilder, ListState, ListView}; use crate::tui::{ block_around, log_viewer::LogViewer, - model::{SpanDescriptor, LogEntry, LogFields}, + model::{LogEntry, LogFields, SpanDescriptor}, widgets::{line_text::style_span, styled::Styled}, }; @@ -91,7 +91,10 @@ impl<'a> FieldTree<'a> { self.lv.selected().map(|(s, _)| s) } - fn current_span_fields(&mut self, spans: &[(SpanDescriptor, &LogFields)]) -> Vec<(String, String)> { + fn current_span_fields( + &mut self, + spans: &[(SpanDescriptor, &LogFields)], + ) -> Vec<(String, String)> { let mut selected = self.state().current_span.selected.unwrap_or(0); if selected > spans.len() { selected = spans.len().saturating_sub(1); @@ -120,7 +123,12 @@ impl<'a> FieldTree<'a> { } impl Styled<'_, &mut FieldTree<'_>> { - fn areas(&self, area: Rect, buf: &mut Buffer, name: Option<&SpanDescriptor>) -> (Rect, Rect, Rect) { + fn areas( + &self, + area: Rect, + buf: &mut Buffer, + name: Option<&SpanDescriptor>, + ) -> (Rect, Rect, Rect) { let [spans_area, fields_area] = Layout::horizontal([ Constraint::Length(if self.spans_focussed() || !self.focussed { 25 @@ -220,6 +228,7 @@ impl Widget for Styled<'_, &mut FieldTree<'_>> { i, Config { collapse_space: true, + ..Default::default() }, ) }) diff --git a/src/tui/widgets/items.rs b/src/tui/widgets/items.rs index 0d1d64e..a13ed38 100644 --- a/src/tui/widgets/items.rs +++ b/src/tui/widgets/items.rs @@ -9,7 +9,7 @@ use crate::tui::{ filters::Filters, input::{FieldMatcher, InputState, InputTarget}, }, - model::{SpanDescriptor, LogEntry}, + model::{LogEntry, SpanDescriptor}, widgets::{ last_error::LastError, line_text::Highlighted, diff --git a/src/tui/widgets/line_text.rs b/src/tui/widgets/line_text.rs index 12c67a9..b5e09b9 100644 --- a/src/tui/widgets/line_text.rs +++ b/src/tui/widgets/line_text.rs @@ -189,6 +189,7 @@ impl Into> for Styled<'_, LineText> { parsed, Config { collapse_space: true, + ..Default::default() }, );