Compare commits
4 commits
bb8f6bedf6
...
0018b27209
| Author | SHA1 | Date | |
|---|---|---|---|
| 0018b27209 | |||
| c867ad379e | |||
| 52a80cfb0e | |||
| 4a6c1020f4 |
54 changed files with 4075 additions and 77 deletions
632
Cargo.lock
generated
632
Cargo.lock
generated
|
|
@ -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,33 @@ 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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.6.1"
|
||||
|
|
@ -157,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"
|
||||
|
|
@ -228,6 +267,21 @@ 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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e56460573dab12b4bb6dfdb0abbb021e35d6a2bbd925acd2f5a9accff5654a8e"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
|
|
@ -256,7 +310,7 @@ checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
|
|||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -277,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"
|
||||
|
|
@ -304,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"
|
||||
|
|
@ -405,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"
|
||||
|
|
@ -414,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"
|
||||
|
|
@ -461,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"
|
||||
|
|
@ -489,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"
|
||||
|
|
@ -518,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"
|
||||
|
|
@ -561,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"
|
||||
|
|
@ -586,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"
|
||||
|
|
@ -630,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"
|
||||
|
|
@ -709,7 +880,7 @@ dependencies = [
|
|||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -821,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"
|
||||
|
|
@ -897,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"
|
||||
|
|
@ -906,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]]
|
||||
|
|
@ -944,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]]
|
||||
|
|
@ -988,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"
|
||||
|
|
@ -1009,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"
|
||||
|
|
@ -1032,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"
|
||||
|
|
@ -1075,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"
|
||||
|
|
@ -1133,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"
|
||||
|
|
@ -1163,6 +1491,25 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"typed-arena",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty-test"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"logparse-pretty-print",
|
||||
"pretty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
|
|
@ -1212,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"
|
||||
|
|
@ -1423,6 +1791,7 @@ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
|||
name = "rustc-logviz"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"dumpster",
|
||||
|
|
@ -1458,7 +1827,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1474,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",
|
||||
]
|
||||
|
|
@ -1603,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"
|
||||
|
|
@ -1686,7 +2061,7 @@ dependencies = [
|
|||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1696,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",
|
||||
]
|
||||
|
|
@ -1722,7 +2097,7 @@ dependencies = [
|
|||
"fancy-regex",
|
||||
"filedescriptor",
|
||||
"finl_unicode",
|
||||
"fixedbitset",
|
||||
"fixedbitset 0.4.2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
|
@ -1801,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"
|
||||
|
|
@ -1871,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"
|
||||
|
|
@ -1881,6 +2281,12 @@ dependencies = [
|
|||
"ratatui-widgets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
|
|
@ -2085,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"
|
||||
|
|
@ -2194,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"
|
||||
|
|
@ -2203,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"
|
||||
|
|
@ -2300,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"
|
||||
|
|
@ -2325,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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ name = "lv"
|
|||
path = "./src/main.rs"
|
||||
|
||||
[workspace]
|
||||
members = [".", "./logparse/"]
|
||||
default-members = [".", "./logparse/"]
|
||||
members = [".", "./logparse/", "./pretty-print/projects/*"]
|
||||
default-members = [".", "./logparse/", "./pretty-print/projects/pretty-print"]
|
||||
|
||||
[dependencies]
|
||||
clap = {version="4.5", features=["derive", "string"]}
|
||||
|
|
@ -25,4 +25,5 @@ nix = {version = "0.31", features = ["process", "signal"]}
|
|||
regex = "1"
|
||||
crossterm = "*"
|
||||
dumpster = "2.1"
|
||||
logparse = {path = "./logparse/", version="0.2.0"}
|
||||
logparse = {path = "./logparse/", version="0.3.0"}
|
||||
arboard = {version="3", features=["wayland-data-control"]}
|
||||
|
|
|
|||
|
|
@ -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 <cratesio@donsz.nl>"]
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
// #![deny(warnings)]
|
||||
#![doc=include_str!("../README.md")]
|
||||
|
||||
/// The structure of parsed log lines
|
||||
|
|
|
|||
|
|
@ -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::<String>();
|
||||
assert_eq!(stringified, spans_concatenated);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,12 @@ pub struct Span<'a> {
|
|||
}
|
||||
|
||||
/// Configuration options for [`into_spans`]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
/// Turn sequences of more than 1 space into exactly 1 space.
|
||||
pub collapse_space: bool,
|
||||
/// Pretty print: wrap at braces etc to the given width
|
||||
pub pretty_print: Option<usize>,
|
||||
}
|
||||
|
||||
pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {}
|
||||
|
|
@ -59,18 +62,27 @@ pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec<Span<'a>>
|
|||
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<Span<'a>>,
|
||||
pub depth: usize,
|
||||
pub ignore_next_space: bool,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
|
|
@ -82,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> {}
|
||||
|
|
@ -96,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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,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> {
|
||||
|
|
@ -148,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> {
|
||||
|
|
@ -156,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> {
|
||||
|
|
@ -167,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> {
|
||||
|
|
@ -201,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> {
|
||||
|
|
@ -212,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> {
|
||||
|
|
@ -225,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> {
|
||||
|
|
@ -237,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 => {}
|
||||
|
|
@ -271,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};
|
||||
|
|
@ -287,6 +608,7 @@ mod tests {
|
|||
res,
|
||||
Config {
|
||||
collapse_space: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.into_iter()
|
||||
|
|
@ -294,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::<String>()
|
||||
}
|
||||
|
||||
#[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(
|
||||
|
|
@ -414,7 +803,7 @@ mod tests {
|
|||
),
|
||||
(
|
||||
"\"",
|
||||
Separator,
|
||||
Surroundings,
|
||||
),
|
||||
(
|
||||
"MetaSized",
|
||||
|
|
@ -422,7 +811,7 @@ mod tests {
|
|||
),
|
||||
(
|
||||
"\"",
|
||||
Separator,
|
||||
Surroundings,
|
||||
),
|
||||
(
|
||||
"",
|
||||
|
|
|
|||
11
pretty-print/.editorconfig
Normal file
11
pretty-print/.editorconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[*]
|
||||
charset = utf-8
|
||||
|
||||
|
||||
[*.pest]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.toml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
27
pretty-print/.github/workflows/rust.yml
vendored
Normal file
27
pretty-print/.github/workflows/rust.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v2
|
||||
- name: Rust Nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Build
|
||||
run: cargo build --release
|
||||
# - name: Tests
|
||||
# run: cargo test --release
|
||||
14
pretty-print/.gitignore
vendored
Normal file
14
pretty-print/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# OS
|
||||
.DS_Store/
|
||||
thumbs.db
|
||||
time-travel.*
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.vs/
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Rust
|
||||
target/
|
||||
Cargo.lock
|
||||
19
pretty-print/.run/Test All.run.xml
Normal file
19
pretty-print/.run/Test All.run.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Test All" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="test" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/projects/pretty-print" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="true" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
356
pretty-print/License.md
Normal file
356
pretty-print/License.md
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
### 1. Definitions
|
||||
|
||||
**1.1. “Contributor”**
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
**1.2. “Contributor Version”**
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
**1.3. “Contribution”**
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
**1.4. “Covered Software”**
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
**1.5. “Incompatible With Secondary Licenses”**
|
||||
means
|
||||
|
||||
* **(a)** that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
* **(b)** that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
**1.6. “Executable Form”**
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
**1.7. “Larger Work”**
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
**1.8. “License”**
|
||||
means this document.
|
||||
|
||||
**1.9. “Licensable”**
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
**1.10. “Modifications”**
|
||||
means any of the following:
|
||||
|
||||
* **(a)** any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
* **(b)** any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
**1.11. “Patent Claims” of a Contributor**
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
**1.12. “Secondary License”**
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
**1.13. “Source Code Form”**
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
**1.14. “You” (or “Your”)**
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, “control” means **(a)** the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or **(b)** ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
|
||||
### 2. License Grants and Conditions
|
||||
|
||||
#### 2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
* **(a)** under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
#### 2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
#### 2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
* **(a)** for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
* **(b)** for infringements caused by: **(i)** Your and any other third party's
|
||||
modifications of Covered Software, or **(ii)** the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
* **(c)** under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
#### 2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
#### 2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
#### 2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
#### 2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
|
||||
### 3. Responsibilities
|
||||
|
||||
#### 3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
#### 3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
* **(a)** such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
* **(b)** You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
#### 3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
#### 3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
#### 3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
|
||||
### 4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: **(a)** comply with
|
||||
the terms of this License to the maximum extent possible; and **(b)**
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
|
||||
### 5. Termination
|
||||
|
||||
**5.1.** The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated **(a)** provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and **(b)** on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
**5.2.** If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
|
||||
### 6. Disclaimer of Warranty
|
||||
|
||||
> Covered Software is provided under this License on an “as is”
|
||||
> basis, without warranty of any kind, either expressed, implied, or
|
||||
> statutory, including, without limitation, warranties that the
|
||||
> Covered Software is free of defects, merchantable, fit for a
|
||||
> particular purpose or non-infringing. The entire risk as to the
|
||||
> quality and performance of the Covered Software is with You.
|
||||
> Should any Covered Software prove defective in any respect, You
|
||||
> (not any Contributor) assume the cost of any necessary servicing,
|
||||
> repair, or correction. This disclaimer of warranty constitutes an
|
||||
> essential part of this License. No use of any Covered Software is
|
||||
> authorized under this License except under this disclaimer.
|
||||
|
||||
### 7. Limitation of Liability
|
||||
|
||||
> Under no circumstances and under no legal theory, whether tort
|
||||
> (including negligence), contract, or otherwise, shall any
|
||||
> Contributor, or anyone who distributes Covered Software as
|
||||
> permitted above, be liable to You for any direct, indirect,
|
||||
> special, incidental, or consequential damages of any character
|
||||
> including, without limitation, damages for lost profits, loss of
|
||||
> goodwill, work stoppage, computer failure or malfunction, or any
|
||||
> and all other commercial damages or losses, even if such party
|
||||
> shall have been informed of the possibility of such damages. This
|
||||
> limitation of liability shall not apply to liability for death or
|
||||
> personal injury resulting from such party's negligence to the
|
||||
> extent applicable law prohibits such limitation. Some
|
||||
> jurisdictions do not allow the exclusion or limitation of
|
||||
> incidental or consequential damages, so this exclusion and
|
||||
> limitation may not apply to You.
|
||||
|
||||
|
||||
### 8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
|
||||
### 9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
### 10. Versions of the License
|
||||
|
||||
#### 10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
#### 10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
#### 10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
## Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
## Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
68
pretty-print/Readme.md
Normal file
68
pretty-print/Readme.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Pretty Printer
|
||||
|
||||
This crate defines a
|
||||
[Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
|
||||
pretty-printing API.
|
||||
|
||||
Start with the static functions of [PrettyTree](enum.Doc.html).
|
||||
|
||||
## Quick start
|
||||
|
||||
Let's pretty-print simple sexps! We want to pretty print sexps like
|
||||
|
||||
```lisp
|
||||
(1 2 3)
|
||||
```
|
||||
or, if the line would be too long, like
|
||||
|
||||
```lisp
|
||||
((1)
|
||||
(2 3)
|
||||
(4 5 6))
|
||||
```
|
||||
|
||||
A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
|
||||
symbolic expression children.
|
||||
|
||||
|
||||
|
||||
```rust
|
||||
use pretty_print::*;
|
||||
use SExp::*;
|
||||
enum SExp {
|
||||
Atom(u32),
|
||||
List(Vec<SExp>),
|
||||
}
|
||||
|
||||
impl SExp {
|
||||
/// Return a pretty printed format of self.
|
||||
pub fn to_doc(&self) -> PrettyTree {
|
||||
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::text(")")),
|
||||
}
|
||||
}
|
||||
/// Return a pretty printed format of self.
|
||||
pub fn to_pretty(&self, width: usize) -> String {
|
||||
let mut w = Vec::new();
|
||||
self.to_doc().render(width, &mut w).unwrap();
|
||||
String::from_utf8(w).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let atom = Atom(5);
|
||||
assert_eq!("5", atom.to_pretty(10));
|
||||
let list = List(vec![Atom(1), Atom(2), Atom(3)]);
|
||||
assert_eq!("(1 2 3)", list.to_pretty(10));
|
||||
assert_eq!(
|
||||
"\
|
||||
(1
|
||||
2
|
||||
3)",
|
||||
list.to_pretty(5)
|
||||
);
|
||||
}
|
||||
```
|
||||
11
pretty-print/package.json
Normal file
11
pretty-print/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"doc": "cargo doc --no-deps --all-features",
|
||||
"test": "cargo test --release",
|
||||
"fmt": "cargo fmt --all",
|
||||
"p": "git push && git push --tags --prune",
|
||||
"reset": "git reset Head~ --soft",
|
||||
"u": "cargo upgrade --workspace"
|
||||
}
|
||||
}
|
||||
22
pretty-print/projects/pretty-print/Cargo.toml
Normal file
22
pretty-print/projects/pretty-print/Cargo.toml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "logparse-pretty-print"
|
||||
version = "0.1.0"
|
||||
authors = ["Aster <192607617@qq.com>", "Jana Dönszelmann <cratesio@donsz.nl>"]
|
||||
description = "pretty print tree"
|
||||
repository = "https://git.donsz.nl/jana/logviewer"
|
||||
documentation = "https://docs.rs/logparse-pretty-print"
|
||||
readme = "../../Readme.md"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
unicode-segmentation = "1.10.1"
|
||||
|
||||
[dependencies.color-ansi]
|
||||
version = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
6
pretty-print/projects/pretty-print/package.json
Normal file
6
pretty-print/projects/pretty-print/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"p": "cargo publish --allow-dirty"
|
||||
}
|
||||
}
|
||||
24
pretty-print/projects/pretty-print/readme.md
Normal file
24
pretty-print/projects/pretty-print/readme.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
This crate defines a
|
||||
[Wadler-style](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
|
||||
pretty-printing API.
|
||||
|
||||
Start with the static functions of [Doc](enum.Doc.html).
|
||||
|
||||
## Quick start
|
||||
|
||||
Let's pretty-print simple sexps! We want to pretty print sexps like
|
||||
|
||||
```lisp
|
||||
(1 2 3)
|
||||
```
|
||||
or, if the line would be too long, like
|
||||
|
||||
```lisp
|
||||
((1)
|
||||
(2 3)
|
||||
(4 5 6))
|
||||
```
|
||||
|
||||
A _simple symbolic expression_ consists of a numeric _atom_ or a nested ordered _list_ of
|
||||
symbolic expression children.
|
||||
|
||||
193
pretty-print/projects/pretty-print/src/helpers/affixes.rs
Normal file
193
pretty-print/projects/pretty-print/src/helpers/affixes.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//! Document formatting of "helpers" such as where some number of prefixes and suffixes would
|
||||
//! ideally be layed out onto a single line instead of breaking them up into multiple lines. See
|
||||
//! `BlockDoc` for an example
|
||||
|
||||
use crate::{docs, DocumentTree, DocumentTree};
|
||||
|
||||
pub struct Affixes<'doc, D, A>
|
||||
{
|
||||
prefix: DocumentTree<'doc, D, A>,
|
||||
suffix: DocumentTree<'doc, D, A>,
|
||||
nest: bool,
|
||||
}
|
||||
|
||||
impl<'a, D> Clone for Affixes<'a, D>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Affixes {
|
||||
prefix: self.prefix.clone(),
|
||||
suffix: self.suffix.clone(),
|
||||
nest: self.nest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc, D, A> Affixes<'doc, D, A>
|
||||
{
|
||||
pub fn new(prefix: DocumentTree<'doc, D, A>, suffix: DocumentTree<'doc, D, A>) -> Self {
|
||||
Affixes {
|
||||
prefix,
|
||||
suffix,
|
||||
nest: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nest(mut self) -> Self {
|
||||
self.nest = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats a set of `prefix` and `suffix` documents around a `body`
|
||||
///
|
||||
/// The following document split into the prefixes [\x y ->, \z ->, {], suffixes [nil, nil, }] and
|
||||
/// body [result: x + y - z] will try to be formatted
|
||||
///
|
||||
/// ```gluon
|
||||
/// \x y -> \z -> { result: x + y - z }
|
||||
/// ```
|
||||
///
|
||||
/// ```gluon
|
||||
/// \x y -> \z -> {
|
||||
/// result: x + y - z
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```gluon
|
||||
/// \x y -> \z ->
|
||||
/// {
|
||||
/// result: x + y - z
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```gluon
|
||||
/// \x y ->
|
||||
/// \z ->
|
||||
/// {
|
||||
/// result: x + y - z
|
||||
/// }
|
||||
/// ```
|
||||
pub struct BlockDoc<'doc, D, A>
|
||||
where
|
||||
D: DocAllocator<'doc, A>,
|
||||
{
|
||||
pub affixes: Vec<Affixes<'doc, D, A>>,
|
||||
pub body: DocumentTree<'doc, D, A>,
|
||||
}
|
||||
|
||||
impl<'doc, D, A> BlockDoc<'doc, D, A>
|
||||
where
|
||||
D: DocAllocator<'doc, A>,
|
||||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
{
|
||||
pub fn format(self, nest: isize) -> DocumentTree<'doc, D, A> {
|
||||
let arena = self.body.0;
|
||||
|
||||
let fail_on_multi_line = arena.fail().flat_alt(arena.nil());
|
||||
|
||||
(1..self.affixes.len() + 1)
|
||||
.rev()
|
||||
.map(|split| {
|
||||
let (before, after) = self.affixes.split_at(split);
|
||||
let last = before.len() == 1;
|
||||
docs![
|
||||
arena,
|
||||
docs![
|
||||
arena,
|
||||
arena.concat(before.iter().map(|affixes| affixes.prefix.clone())),
|
||||
if last {
|
||||
arena.nil()
|
||||
} else {
|
||||
fail_on_multi_line.clone()
|
||||
}
|
||||
]
|
||||
.group(),
|
||||
docs![
|
||||
arena,
|
||||
after.iter().rev().cloned().fold(
|
||||
docs![
|
||||
arena,
|
||||
self.body.clone(),
|
||||
// If there is no prefix then we must not allow the body to laid out on multiple
|
||||
// lines without nesting
|
||||
if !last
|
||||
&& before
|
||||
.iter()
|
||||
.all(|affixes| matches!(&*affixes.prefix.1, DocumentTree::Nil))
|
||||
{
|
||||
fail_on_multi_line.clone()
|
||||
} else {
|
||||
arena.nil()
|
||||
},
|
||||
]
|
||||
.nest(nest)
|
||||
.append(
|
||||
arena.concat(after.iter().map(|affixes| affixes.suffix.clone()))
|
||||
),
|
||||
|acc, affixes| {
|
||||
let mut doc = affixes.prefix.append(acc);
|
||||
if affixes.nest {
|
||||
doc = doc.nest(nest);
|
||||
}
|
||||
doc.group()
|
||||
},
|
||||
),
|
||||
arena.concat(before.iter().map(|affixes| affixes.suffix.clone())),
|
||||
]
|
||||
.group(),
|
||||
]
|
||||
})
|
||||
.fold(None::<DocumentTree<_, _>>, |acc, doc| {
|
||||
Some(match acc {
|
||||
None => doc,
|
||||
Some(acc) => acc.union(doc),
|
||||
})
|
||||
})
|
||||
.unwrap_or(self.body)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::Arena;
|
||||
|
||||
#[test]
|
||||
fn format_block() {
|
||||
let arena = &Arena::<()>::new();
|
||||
let mk_doc = || BlockDoc {
|
||||
affixes: vec![
|
||||
Affixes::new(docs![arena, "\\x y ->"], arena.nil()).nest(),
|
||||
Affixes::new(docs![arena, arena.line(), "\\z ->"], arena.nil()).nest(),
|
||||
Affixes::new(
|
||||
docs![arena, arena.line(), "{"],
|
||||
docs![arena, arena.line(), "}"],
|
||||
)
|
||||
.nest(),
|
||||
],
|
||||
body: docs![arena, arena.line(), "result"],
|
||||
};
|
||||
expect_test::expect![[r#"\x y -> \z -> { result }"#]]
|
||||
.assert_eq(&mk_doc().format(4).1.pretty(40).to_string());
|
||||
expect_test::expect![[r#"
|
||||
\x y -> \z -> {
|
||||
result
|
||||
}"#]]
|
||||
.assert_eq(&mk_doc().format(4).1.pretty(15).to_string());
|
||||
expect_test::expect![[r#"
|
||||
\x y -> \z ->
|
||||
{
|
||||
result
|
||||
}"#]]
|
||||
.assert_eq(&mk_doc().format(4).1.pretty(14).to_string());
|
||||
expect_test::expect![[r#"
|
||||
\x y ->
|
||||
\z ->
|
||||
{
|
||||
result
|
||||
}"#]]
|
||||
.assert_eq(&mk_doc().format(4).1.pretty(12).to_string());
|
||||
}
|
||||
}
|
||||
81
pretty-print/projects/pretty-print/src/helpers/hard_block.rs
Normal file
81
pretty-print/projects/pretty-print/src/helpers/hard_block.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
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.
|
||||
///
|
||||
/// ```vk
|
||||
/// {a, b, c}
|
||||
///
|
||||
/// {
|
||||
/// a,
|
||||
/// b,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct HardBlock<'a, T> {
|
||||
/// The indentation of the soft block
|
||||
pub indent: usize,
|
||||
/// The left hand side of the soft block
|
||||
pub lhs: &'static str,
|
||||
/// The right hand side of the soft block
|
||||
pub rhs: &'static str,
|
||||
/// The joint node of the soft block
|
||||
pub joint: PrettyTree<'a, T>,
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
/// Build a new soft block with the parentheses syntax
|
||||
pub fn parentheses() -> Self {
|
||||
Self::new("(", ")")
|
||||
}
|
||||
/// Build a new soft block with the brackets syntax
|
||||
pub fn brackets() -> Self {
|
||||
Self::new("[", "]")
|
||||
}
|
||||
/// Build a new soft block with the curly braces syntax
|
||||
pub fn curly_braces() -> Self {
|
||||
Self::new("{", "}")
|
||||
}
|
||||
/// Set the joint node of the soft block
|
||||
pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self {
|
||||
Self { joint, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone + Text<'a>> HardBlock<'a, T> {
|
||||
/// Join a slice of pretty printables with the soft block
|
||||
pub fn join_slice<P: PrettyPrint<'a, T>>(&self, slice: &[P], theme: &PrettyProvider) -> PrettyTree<'a, T> {
|
||||
let mut outer = PrettySequence::new(5);
|
||||
outer += self.lhs;
|
||||
outer += PrettyTree::Hardline;
|
||||
let mut inner = PrettySequence::new(slice.len() * 2);
|
||||
for (idx, term) in slice.iter().enumerate() {
|
||||
if idx != 0 {
|
||||
inner += self.joint.clone();
|
||||
}
|
||||
inner += term.pretty(theme);
|
||||
}
|
||||
outer += inner.indent(self.indent);
|
||||
outer += PrettyTree::Hardline;
|
||||
outer += self.rhs;
|
||||
outer.into()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
use super::*;
|
||||
use crate::{PrettyBuilder, Text};
|
||||
|
||||
/// `K & R` style brackets
|
||||
///
|
||||
/// ```vk
|
||||
/// a {}
|
||||
/// ```
|
||||
///
|
||||
/// ```vk
|
||||
/// a {
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct KAndRBracket {
|
||||
/// Whether to add a space after the keyword
|
||||
pub head_space: bool,
|
||||
/// The left bracket
|
||||
pub bracket_l: &'static str,
|
||||
/// The right bracket
|
||||
pub bracket_r: &'static str,
|
||||
}
|
||||
|
||||
impl KAndRBracket {
|
||||
/// Build a bracketed block
|
||||
pub fn curly_braces() -> Self {
|
||||
Self { head_space: true, bracket_l: "{", bracket_r: "}" }
|
||||
}
|
||||
/// Build a bracketed block
|
||||
pub fn build<'b, 'a, I, T: Text<'a>>(
|
||||
&self,
|
||||
items: &[I],
|
||||
allocator: &'b PrettyProvider,
|
||||
inline_join: PrettyTree<'a, T>,
|
||||
block_join: PrettyTree<'a, T>,
|
||||
) -> PrettyTree<'a, T>
|
||||
where
|
||||
I: PrettyPrint<'a, T>,
|
||||
{
|
||||
let mut output = PrettySequence::new(5);
|
||||
if self.head_space {
|
||||
output.push(" ");
|
||||
}
|
||||
output.push(self.bracket_l);
|
||||
// inline
|
||||
let mut inline = PrettySequence::new(3);
|
||||
inline.push(" ");
|
||||
inline.push(allocator.join_slice(items, inline_join));
|
||||
inline.push(" ");
|
||||
// block
|
||||
let mut block = PrettySequence::new(3);
|
||||
block.push(PrettyTree::Hardline);
|
||||
block.push(allocator.join_slice(items, block_join).indent(4));
|
||||
block.push(PrettyTree::Hardline);
|
||||
//
|
||||
output.push(block.flat_alt(inline));
|
||||
output.push(self.bracket_r);
|
||||
output.into()
|
||||
}
|
||||
}
|
||||
13
pretty-print/projects/pretty-print/src/helpers/mod.rs
Normal file
13
pretty-print/projects/pretty-print/src/helpers/mod.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#![doc = include_str!("readme.md")]
|
||||
|
||||
use crate::{PrettyBuilder, PrettyPrint, PrettyProvider, PrettyTree};
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::AddAssign;
|
||||
|
||||
mod hard_block;
|
||||
mod k_and_r_bracket;
|
||||
mod sequence;
|
||||
mod soft_block;
|
||||
// mod affixes;
|
||||
|
||||
pub use self::{hard_block::HardBlock, k_and_r_bracket::KAndRBracket, sequence::PrettySequence, soft_block::SoftBlock};
|
||||
0
pretty-print/projects/pretty-print/src/helpers/readme.md
Normal file
0
pretty-print/projects/pretty-print/src/helpers/readme.md
Normal file
70
pretty-print/projects/pretty-print/src/helpers/sequence.rs
Normal file
70
pretty-print/projects/pretty-print/src/helpers/sequence.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::Text;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// The document sequence type.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PrettySequence<'a, T> {
|
||||
items: Vec<PrettyTree<'a, T>>,
|
||||
}
|
||||
|
||||
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<U>(&mut self, item: U)
|
||||
where
|
||||
U: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
self.items.push(item.into());
|
||||
}
|
||||
/// Create a new sequence with the given capacity.
|
||||
pub fn extend<I, S>(&mut self, items: I)
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
self.items.extend(items.into_iter().map(|x| x.into()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> PrettyBuilder<'a, T> for PrettySequence<'a, T> {
|
||||
fn flat_alt<E>(self, flat: E) -> PrettyTree<'a, T>
|
||||
where
|
||||
E: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
PrettyTree::from(self).flat_alt(flat)
|
||||
}
|
||||
fn indent(self, indent: usize) -> PrettyTree<'a, T> {
|
||||
PrettyTree::from(self).indent(indent)
|
||||
}
|
||||
|
||||
fn nest(self, offset: isize) -> PrettyTree<'a, T> {
|
||||
PrettyTree::from(self).nest(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U, T> AddAssign<U> for PrettySequence<'a, T>
|
||||
where
|
||||
U: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: U) {
|
||||
self.push(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> From<PrettySequence<'a, T>> for PrettyTree<'a, T> {
|
||||
fn from(value: PrettySequence<'a, T>) -> Self {
|
||||
Self::concat(value.items)
|
||||
}
|
||||
}
|
||||
88
pretty-print/projects/pretty-print/src/helpers/soft_block.rs
Normal file
88
pretty-print/projects/pretty-print/src/helpers/soft_block.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
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.
|
||||
///
|
||||
/// ```vk
|
||||
/// {a, b, c}
|
||||
///
|
||||
/// {
|
||||
/// a,
|
||||
/// b,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SoftBlock<'a, T> {
|
||||
/// The indentation of the soft block
|
||||
pub indent: usize,
|
||||
/// The left hand side of the soft block
|
||||
pub lhs: &'static str,
|
||||
/// The right hand side of the soft block
|
||||
pub rhs: &'static str,
|
||||
/// The joint node of the soft block
|
||||
pub joint: PrettyTree<'a, T>,
|
||||
/// The tail node of the soft block
|
||||
pub tail: PrettyTree<'a, T>,
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
/// Build a new soft block with the tuple syntax
|
||||
pub fn tuple() -> Self {
|
||||
Self::new("(", ")")
|
||||
}
|
||||
/// Build a new soft block with the parentheses syntax
|
||||
pub fn parentheses() -> Self {
|
||||
Self::new("(", ")")
|
||||
}
|
||||
/// Build a new soft block with the brackets syntax
|
||||
pub fn brackets() -> Self {
|
||||
Self::new("[", "]")
|
||||
}
|
||||
/// Build a new soft block with the curly braces syntax
|
||||
pub fn curly_braces() -> Self {
|
||||
Self::new("{", "}")
|
||||
}
|
||||
/// Set the joint node of the soft block
|
||||
pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self {
|
||||
Self { joint, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a> + Clone> SoftBlock<'a, T> {
|
||||
/// Join a slice of pretty printables with the soft block
|
||||
pub fn join_slice<U: PrettyPrint<'a, T>>(&self, slice: &[U], theme: &PrettyProvider) -> PrettyTree<'a, T> {
|
||||
let mut outer = PrettySequence::new(5);
|
||||
outer += self.lhs;
|
||||
outer += PrettyTree::line_or_space();
|
||||
let mut inner = PrettySequence::new(slice.len() * 2);
|
||||
for (idx, term) in slice.iter().enumerate() {
|
||||
if idx != 0 {
|
||||
inner += self.joint.clone();
|
||||
}
|
||||
inner += term.pretty(theme);
|
||||
}
|
||||
outer += inner.indent(self.indent);
|
||||
outer += PrettyTree::line_or_space();
|
||||
outer += self.rhs;
|
||||
outer.into()
|
||||
}
|
||||
}
|
||||
53
pretty-print/projects/pretty-print/src/lib.rs
Normal file
53
pretty-print/projects/pretty-print/src/lib.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||
#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
|
||||
#![doc = include_str!("../readme.md")]
|
||||
#![doc(html_logo_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||
#![doc(html_favicon_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate core;
|
||||
|
||||
pub mod helpers;
|
||||
mod providers;
|
||||
mod render;
|
||||
mod text;
|
||||
mod traits;
|
||||
mod tree;
|
||||
|
||||
pub use self::render::{
|
||||
PrettyFormatter, Render, RenderAnnotated,
|
||||
write_fmt::{BufferWrite, FmtWrite},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::render::write_io::{IoWrite, TerminalWriter, VecWrite};
|
||||
pub use crate::{
|
||||
providers::PrettyProvider,
|
||||
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`)
|
||||
///
|
||||
/// ```
|
||||
/// use pretty_print::docs;
|
||||
/// let doc =
|
||||
/// docs!["let", arena.softline(), "x", arena.softline(), "=", arena.softline(), Some("123"),];
|
||||
/// assert_eq!(doc.1.pretty(80).to_string(), "let x = 123");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! docs {
|
||||
($alloc: expr, $first: expr $(,)?) => {
|
||||
$crate::Pretty::pretty($first, $alloc)
|
||||
};
|
||||
($alloc: expr, $first: expr $(, $rest: expr)+ $(,)?) => {{
|
||||
let mut doc = $crate::Pretty::pretty($first, $alloc);
|
||||
$(
|
||||
doc = doc.append($rest);
|
||||
)*
|
||||
doc
|
||||
}}
|
||||
}
|
||||
234
pretty-print/projects/pretty-print/src/providers/mod.rs
Normal file
234
pretty-print/projects/pretty-print/src/providers/mod.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
use crate::{PrettyPrint, PrettyTree, Text};
|
||||
use alloc::rc::Rc;
|
||||
use color_ansi::AnsiStyle;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
|
||||
/// Represents a pretty-printable tree provider.
|
||||
pub struct PrettyProvider {
|
||||
width: usize,
|
||||
keyword: Rc<AnsiStyle>,
|
||||
string: Rc<AnsiStyle>,
|
||||
number: Rc<AnsiStyle>,
|
||||
macros: Rc<AnsiStyle>,
|
||||
argument: Rc<AnsiStyle>,
|
||||
argument_mut: Rc<AnsiStyle>,
|
||||
local: Rc<AnsiStyle>,
|
||||
local_mut: Rc<AnsiStyle>,
|
||||
operator: Rc<AnsiStyle>,
|
||||
structure: Rc<AnsiStyle>,
|
||||
variant: Rc<AnsiStyle>,
|
||||
interface: Rc<AnsiStyle>,
|
||||
}
|
||||
|
||||
impl Debug for PrettyProvider {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("PrettyProvider").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyProvider {
|
||||
/// Creates a new pretty-printable tree provider.
|
||||
pub fn new(width: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
keyword: AnsiStyle::rgb(197, 119, 207).into(),
|
||||
string: AnsiStyle::rgb(152, 195, 121).into(),
|
||||
number: AnsiStyle::rgb(206, 153, 100).into(),
|
||||
macros: AnsiStyle::rgb(87, 182, 194).into(),
|
||||
argument: AnsiStyle::rgb(239, 112, 117).into(),
|
||||
argument_mut: AnsiStyle::rgb(239, 112, 117).with_underline().into(),
|
||||
local: AnsiStyle::rgb(152, 195, 121).into(),
|
||||
local_mut: AnsiStyle::rgb(152, 195, 121).with_underline().into(),
|
||||
operator: AnsiStyle::rgb(90, 173, 238).into(),
|
||||
structure: AnsiStyle::rgb(197, 119, 207).into(),
|
||||
variant: AnsiStyle::rgb(239, 112, 117).into(),
|
||||
interface: AnsiStyle::rgb(197, 119, 207).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyProvider {
|
||||
/// Gets the width of the document.
|
||||
pub fn get_width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
/// Sets the width of the document.
|
||||
pub fn set_width(&mut self, width: usize) {
|
||||
self.width = width;
|
||||
}
|
||||
/// Gets the width of the document.
|
||||
pub fn text<'a, S, T>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text)
|
||||
}
|
||||
/// Gets the width of the document.
|
||||
pub fn custom<'a, S, T: Text<'a>>(&self, text: S, style: Rc<AnsiStyle>) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(style)
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn keyword<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.keyword.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn identifier<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.operator.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn generic<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.macros.clone())
|
||||
}
|
||||
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn variable<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
if mutable {
|
||||
PrettyTree::text(text).annotate(self.local_mut.clone())
|
||||
} else {
|
||||
PrettyTree::text(text).annotate(self.local.clone())
|
||||
}
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn argument<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
if mutable {
|
||||
PrettyTree::text(text).annotate(self.argument_mut.clone())
|
||||
} else {
|
||||
PrettyTree::text(text).annotate(self.argument.clone())
|
||||
}
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn operator<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.operator.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn string<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.string.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn annotation<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.macros.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn number<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.number.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn structure<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.structure.clone())
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn variant<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.variant.clone())
|
||||
}
|
||||
|
||||
/// Allocate a document containing the given text.
|
||||
pub fn interface<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
PrettyTree::text(text).annotate(self.interface.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyProvider {
|
||||
/// Allocate a document containing the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pretty_print::PrettyProvider;
|
||||
/// let theme = PrettyProvider::new(80);
|
||||
/// theme.join(vec!["a", "b", "c"], ", ");
|
||||
/// ```
|
||||
pub fn join<'a, I, T1, T2, T: Text<'a>>(&self, iter: I, joint: T2) -> PrettyTree<'a, T>
|
||||
where
|
||||
I: IntoIterator<Item = T1>,
|
||||
T1: PrettyPrint<'a, T>,
|
||||
T2: PrettyPrint<'a, T>,
|
||||
{
|
||||
PrettyTree::join(iter.into_iter().map(|x| x.pretty(self)), joint.pretty(self))
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pretty_print::PrettyProvider;
|
||||
/// let theme = PrettyProvider::new(80);
|
||||
/// theme.join(&["a", "b", "c"], ", ");
|
||||
/// ```
|
||||
pub fn join_slice<'a, I, U, T: Text<'a>>(&self, iter: &[I], joint: U) -> PrettyTree<'a, T>
|
||||
where
|
||||
I: PrettyPrint<'a, T>,
|
||||
U: PrettyPrint<'a, T>,
|
||||
{
|
||||
PrettyTree::join(iter.iter().map(|s| s.pretty(self)), joint.pretty(self))
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pretty_print::PrettyProvider;
|
||||
/// let theme = PrettyProvider::new(80);
|
||||
/// theme.concat(vec!["1", "2", "3"]);
|
||||
/// ```
|
||||
pub fn concat<'a, I, U, T: Text<'a>>(&self, iter: I) -> PrettyTree<'a, T>
|
||||
where
|
||||
I: IntoIterator<Item = U>,
|
||||
U: PrettyPrint<'a, T>,
|
||||
{
|
||||
PrettyTree::concat(iter.into_iter().map(|x| x.pretty(self)))
|
||||
}
|
||||
/// Allocate a document containing the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use pretty_print::PrettyProvider;
|
||||
/// let theme = PrettyProvider::new(80);
|
||||
/// theme.concat_slice(&["1", "2", "3"]);
|
||||
/// ```
|
||||
pub fn concat_slice<'a, U, T: Text<'a>>(&self, iter: &[U]) -> PrettyTree<'a, T>
|
||||
where
|
||||
U: PrettyPrint<'a, T>,
|
||||
{
|
||||
PrettyTree::concat(iter.iter().map(|s| s.pretty(self)))
|
||||
}
|
||||
}
|
||||
363
pretty-print/projects/pretty-print/src/render/mod.rs
Normal file
363
pretty-print/projects/pretty-print/src/render/mod.rs
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
use crate::{BufferWrite, PrettyTree, Text};
|
||||
use alloc::{rc::Rc, vec, vec::Vec};
|
||||
use color_ansi::AnsiStyle;
|
||||
use core::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
slice,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod write_io;
|
||||
|
||||
pub mod write_fmt;
|
||||
|
||||
/// Trait representing the operations necessary to render a document
|
||||
pub trait Render<'a, T> {
|
||||
/// The type of the output
|
||||
type Error;
|
||||
|
||||
/// Write to the output
|
||||
fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error>;
|
||||
|
||||
/// Emit an error
|
||||
fn fail_doc(&self) -> Self::Error;
|
||||
}
|
||||
|
||||
/// The given text, which must not contain line breaks.
|
||||
pub struct PrettyFormatter<'b, 'a, T> {
|
||||
tree: &'b PrettyTree<'a, T>,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
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<'a, T> PrettyTree<'a, T> {
|
||||
/// Returns a value which implements `std::fmt::Display`
|
||||
///
|
||||
/// ```
|
||||
/// use pretty::{BoxDoc, Doc};
|
||||
/// let doc =
|
||||
/// BoxDoc::<()>::group(BoxDoc::text("hello").append(Doc::line()).append(Doc::text("world")));
|
||||
/// assert_eq!(format!("{}", doc.pretty(80)), "hello world");
|
||||
/// ```
|
||||
#[inline]
|
||||
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<'a, T>: Render<'a, T> {
|
||||
/// Push an annotation onto the stack
|
||||
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error>;
|
||||
/// Pop an annotation from the stack
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Annotation<A> {
|
||||
Push(Rc<A>),
|
||||
Pop,
|
||||
}
|
||||
|
||||
macro_rules! make_spaces {
|
||||
() => { "" };
|
||||
($s: tt $($t: tt)*) => { concat!(" ", make_spaces!($($t)*)) };
|
||||
}
|
||||
|
||||
pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);
|
||||
|
||||
fn append_docs2<'a, T>(
|
||||
ldoc: Rc<PrettyTree<'a, T>>,
|
||||
rdoc: Rc<PrettyTree<'a, T>>,
|
||||
mut consumer: impl FnMut(Rc<PrettyTree<'a, T>>),
|
||||
) -> Rc<PrettyTree<'a, T>> {
|
||||
let d = append_docs(rdoc, &mut consumer);
|
||||
consumer(d);
|
||||
append_docs(ldoc, &mut consumer)
|
||||
}
|
||||
|
||||
fn append_docs<'a, T>(
|
||||
mut doc: Rc<PrettyTree<'a, T>>,
|
||||
consumer: &mut impl FnMut(Rc<PrettyTree<'a, T>>),
|
||||
) -> Rc<PrettyTree<'a, T>> {
|
||||
loop {
|
||||
// Since appended documents often appear in sequence on the left side we
|
||||
// gain a slight performance increase by batching these pushes (avoiding
|
||||
// to push and directly pop `Append` documents)
|
||||
match doc.as_ref() {
|
||||
PrettyTree::Append { lhs, rhs } => {
|
||||
let d = append_docs(rhs.clone(), consumer);
|
||||
consumer(d);
|
||||
doc = lhs.clone();
|
||||
}
|
||||
_ => return doc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn best<'a, W, T: Text<'a>>(doc: Rc<PrettyTree<'a, T>>, width: usize, out: &mut W) -> Result<(), W::Error>
|
||||
where
|
||||
W: RenderAnnotated<'a, T>,
|
||||
W: ?Sized,
|
||||
{
|
||||
Best {
|
||||
pos: 0,
|
||||
back_cmds: vec![RenderCommand { indent: 0, mode: Mode::Break, node: doc }],
|
||||
front_cmds: vec![],
|
||||
annotation_levels: vec![],
|
||||
width,
|
||||
}
|
||||
.best(0, out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
enum Mode {
|
||||
Break,
|
||||
Flat,
|
||||
}
|
||||
|
||||
struct RenderCommand<'a, T> {
|
||||
indent: usize,
|
||||
mode: Mode,
|
||||
node: Rc<PrettyTree<'a, T>>,
|
||||
}
|
||||
|
||||
fn write_newline<'a, W, T: Text<'a>>(ind: usize, out: &mut W) -> Result<(), W::Error>
|
||||
where
|
||||
W: ?Sized + Render<'a, T>,
|
||||
{
|
||||
out.write_all(&[T::newline()])?;
|
||||
write_spaces(ind, out)
|
||||
}
|
||||
|
||||
fn write_spaces<'a, W, T: Text<'a>>(spaces: usize, out: &mut W) -> Result<(), W::Error>
|
||||
where
|
||||
W: ?Sized + Render<'a, T>,
|
||||
{
|
||||
let mut inserted = 0;
|
||||
while inserted < spaces {
|
||||
let insert = core::cmp::min(SPACES.len(), spaces - inserted);
|
||||
out.write_all(&[T::from_static_spaces(&SPACES[..insert])])?;
|
||||
inserted += insert;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Best<'a, T> {
|
||||
pos: usize,
|
||||
back_cmds: Vec<RenderCommand<'a, T>>,
|
||||
front_cmds: Vec<Rc<PrettyTree<'a, T>>>,
|
||||
annotation_levels: Vec<usize>,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> Best<'a, T> {
|
||||
fn fitting(&mut self, next: Rc<PrettyTree<'a, T>>, 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);
|
||||
let mut mode = Mode::Flat;
|
||||
|
||||
loop {
|
||||
let mut doc = match self.front_cmds.pop() {
|
||||
None => {
|
||||
if bidx == 0 {
|
||||
// All commands have been processed
|
||||
return true;
|
||||
} else {
|
||||
bidx -= 1;
|
||||
mode = Mode::Break;
|
||||
self.back_cmds[bidx].node.clone()
|
||||
}
|
||||
}
|
||||
Some(cmd) => cmd,
|
||||
};
|
||||
|
||||
loop {
|
||||
match doc.as_ref() {
|
||||
PrettyTree::Nil => {}
|
||||
PrettyTree::Append { lhs, rhs } => {
|
||||
doc = append_docs2(lhs.clone(), rhs.clone(), |send| self.front_cmds.push(send));
|
||||
continue;
|
||||
}
|
||||
// Newlines inside the group makes it not fit, but those outside lets it
|
||||
// fit on the current line
|
||||
PrettyTree::Hardline => return mode == Mode::Break,
|
||||
PrettyTree::RenderLength { length: len, body: _ } => {
|
||||
pos += len;
|
||||
if pos > self.width {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PrettyTree::Text(ref s) => {
|
||||
pos += s.len();
|
||||
if pos > self.width {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PrettyTree::MaybeInline { block: flat, inline } => {
|
||||
doc = match mode {
|
||||
Mode::Break => flat.clone(),
|
||||
Mode::Flat => inline.clone(),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
PrettyTree::Column { invoke: function } => {
|
||||
doc = Rc::new(function(pos));
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Nesting { invoke: function } => {
|
||||
doc = Rc::new(function(ind));
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Nest { space: _, doc: next }
|
||||
| PrettyTree::Group { items: next }
|
||||
| PrettyTree::Annotated { style: _, body: next }
|
||||
| PrettyTree::Union { lhs: _, rhs: next } => {
|
||||
doc = next.clone();
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Fail => return false,
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn best<W>(&mut self, top: usize, out: &mut W) -> Result<bool, W::Error>
|
||||
where
|
||||
W: RenderAnnotated<'a, T>,
|
||||
W: ?Sized,
|
||||
{
|
||||
let mut fits = true;
|
||||
|
||||
while top < self.back_cmds.len() {
|
||||
let mut cmd = self.back_cmds.pop().unwrap();
|
||||
loop {
|
||||
let RenderCommand { indent: ind, mode, node } = cmd;
|
||||
match node.as_ref() {
|
||||
PrettyTree::Nil => {}
|
||||
PrettyTree::Append { lhs, rhs } => {
|
||||
cmd.node = append_docs2(lhs.clone(), rhs.clone(), |send| {
|
||||
self.back_cmds.push(RenderCommand { indent: ind, mode, node: send })
|
||||
});
|
||||
continue;
|
||||
}
|
||||
PrettyTree::MaybeInline { block, inline } => {
|
||||
cmd.node = match mode {
|
||||
Mode::Break => block.clone(),
|
||||
Mode::Flat => inline.clone(),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Group { items } => {
|
||||
match mode {
|
||||
Mode::Break if self.fitting(items.clone(), self.pos, ind) => {
|
||||
cmd.mode = Mode::Flat;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
cmd.node = items.clone();
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Nest { space, doc } => {
|
||||
// Once https://doc.rust-lang.org/std/primitive.usize.html#method.saturating_add_signed is stable
|
||||
// this can be replaced
|
||||
let new_ind = if *space >= 0 {
|
||||
ind.saturating_add(*space as usize)
|
||||
} else {
|
||||
ind.saturating_sub(space.unsigned_abs())
|
||||
};
|
||||
cmd = RenderCommand { indent: new_ind, mode, node: doc.clone() };
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Hardline => {
|
||||
// The next document may have different indentation so we should use it if we can
|
||||
match self.back_cmds.pop() {
|
||||
Some(next) => {
|
||||
write_newline(next.indent, out)?;
|
||||
self.pos = next.indent;
|
||||
cmd = next;
|
||||
continue;
|
||||
}
|
||||
None => {
|
||||
write_newline(ind, out)?;
|
||||
self.pos = ind;
|
||||
}
|
||||
}
|
||||
}
|
||||
PrettyTree::RenderLength { length: len, body: doc } => match doc.as_ref() {
|
||||
PrettyTree::Text(s) => {
|
||||
out.write_all(slice::from_ref(s))?;
|
||||
self.pos += len;
|
||||
fits &= self.pos <= self.width;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
PrettyTree::Text(ref s) => {
|
||||
out.write_all(slice::from_ref(s))?;
|
||||
self.pos += s.len();
|
||||
fits &= self.pos <= self.width;
|
||||
}
|
||||
PrettyTree::Annotated { style: color, body: doc } => {
|
||||
out.push_annotation(color.clone())?;
|
||||
self.annotation_levels.push(self.back_cmds.len());
|
||||
cmd.node = doc.clone();
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Union { lhs: left, rhs: right } => {
|
||||
let pos = self.pos;
|
||||
let annotation_levels = self.annotation_levels.len();
|
||||
let bcmds = self.back_cmds.len();
|
||||
|
||||
self.back_cmds.push(RenderCommand { indent: ind, mode, node: left.clone() });
|
||||
|
||||
let mut buffer = BufferWrite::new(0);
|
||||
|
||||
match self.best(bcmds, &mut buffer) {
|
||||
Ok(true) => buffer.render(out)?,
|
||||
Ok(false) | Err(_) => {
|
||||
self.pos = pos;
|
||||
self.back_cmds.truncate(bcmds);
|
||||
self.annotation_levels.truncate(annotation_levels);
|
||||
cmd.node = right.clone();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
PrettyTree::Column { invoke: column } => {
|
||||
cmd.node = Rc::new(column(self.pos));
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Nesting { invoke: nesting } => {
|
||||
cmd.node = Rc::new(nesting(self.pos));
|
||||
continue;
|
||||
}
|
||||
PrettyTree::Fail => return Err(out.fail_doc()),
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
while self.annotation_levels.last() == Some(&self.back_cmds.len()) {
|
||||
self.annotation_levels.pop();
|
||||
out.pop_annotation()?;
|
||||
}
|
||||
}
|
||||
Ok(fits)
|
||||
}
|
||||
}
|
||||
115
pretty-print/projects/pretty-print/src/render/write_fmt.rs
Normal file
115
pretty-print/projects/pretty-print/src/render/write_fmt.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
use crate::{Render, RenderAnnotated, Text, render::Annotation};
|
||||
use alloc::rc::Rc;
|
||||
use color_ansi::AnsiStyle;
|
||||
use core::fmt::{Debug, Error, Formatter};
|
||||
|
||||
/// Writes to something implementing `std::fmt::Write`
|
||||
pub struct FmtWrite<W> {
|
||||
upstream: W,
|
||||
}
|
||||
/// Represents a terminal writer.
|
||||
#[derive(Debug)]
|
||||
pub struct BufferWrite<T> {
|
||||
buffer: Vec<T>,
|
||||
annotations: Vec<(usize, Annotation<AnsiStyle>)>,
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> BufferWrite<T> {
|
||||
/// Creates a new terminal writer.
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
BufferWrite { buffer: Vec::with_capacity(capacity), annotations: Vec::new() }
|
||||
}
|
||||
/// Creates a new terminal writer.
|
||||
pub fn render<W>(&mut self, render: &mut W) -> Result<(), W::Error>
|
||||
where
|
||||
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_all(s)?;
|
||||
}
|
||||
start = *end;
|
||||
match annotation {
|
||||
Annotation::Push(a) => render.push_annotation(a.clone())?,
|
||||
Annotation::Pop => render.pop_annotation()?,
|
||||
}
|
||||
}
|
||||
let s = &self.buffer[start..];
|
||||
if !s.is_empty() {
|
||||
render.write_all(s)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> Render<'a, T> for BufferWrite<T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error> {
|
||||
self.buffer.extend(s.iter().cloned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fail_doc(&self) -> Self::Error {
|
||||
Error
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for FmtWrite<W>
|
||||
where
|
||||
W: core::fmt::Write,
|
||||
{
|
||||
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> RenderAnnotated<'a, T> for BufferWrite<T> {
|
||||
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||
self.annotations.push((self.buffer.len(), Annotation::Push(annotation)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
self.annotations.push((self.buffer.len(), Annotation::Pop));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Debug for FmtWrite<W> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("IoWrite").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> FmtWrite<W> {
|
||||
/// Create a new `FmtWrite` from something implementing `std::fmt::Write`
|
||||
pub fn new(upstream: W) -> FmtWrite<W> {
|
||||
FmtWrite { upstream }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W, T: Text<'a>> Render<'a, T> for FmtWrite<W>
|
||||
where
|
||||
W: core::fmt::Write,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
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 {
|
||||
Error
|
||||
}
|
||||
}
|
||||
165
pretty-print/projects/pretty-print/src/render/write_io.rs
Normal file
165
pretty-print/projects/pretty-print/src/render/write_io.rs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
#[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};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{Error, Write};
|
||||
|
||||
/// Represents a terminal writer.
|
||||
pub struct TerminalWriter<W> {
|
||||
color_stack: Vec<Rc<AnsiStyle>>,
|
||||
upstream: AnsiWriter<W>,
|
||||
}
|
||||
|
||||
/// Writes to something implementing `std::io::Write`
|
||||
pub struct IoWrite<W> {
|
||||
upstream: W,
|
||||
}
|
||||
|
||||
impl<W> Debug for IoWrite<W> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("IoWrite").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> IoWrite<W> {
|
||||
/// Creates a new terminal writer.
|
||||
pub fn new(upstream: W) -> IoWrite<W> {
|
||||
IoWrite { upstream }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, W, T: Text<'a>> Render<'a, T> for IoWrite<W>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn write_all(&mut self, s: &[T]) -> std::io::Result<()> {
|
||||
for i in s {
|
||||
self.upstream.write_all(i.as_str().as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fail_doc(&self) -> Self::Error {
|
||||
std::io::Error::other("Document failed to render")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for IoWrite<W>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Debug for TerminalWriter<W> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("TerminalWriter").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> TerminalWriter<W> {
|
||||
/// Creates a new terminal writer.
|
||||
pub fn new(upstream: W) -> Self {
|
||||
TerminalWriter { color_stack: Vec::new(), upstream: AnsiWriter::new(upstream) }
|
||||
}
|
||||
/// Creates a new terminal writer with a specific color.
|
||||
pub fn with_color(mut self, color: AnsiAbility) -> Self {
|
||||
self.upstream.set_ability(color);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W, T: Text<'a>> Render<'a, T> for TerminalWriter<W>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn write_all(&mut self, s: &[T]) -> std::io::Result<()> {
|
||||
for i in s {
|
||||
self.upstream.write_all(i.as_str().as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fail_doc(&self) -> Self::Error {
|
||||
Error::other("Document failed to render")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, T: Text<'a>> RenderAnnotated<'a, T> for TerminalWriter<W> {
|
||||
fn push_annotation(&mut self, color: Rc<AnsiStyle>) -> Result<(), Self::Error> {
|
||||
self.color_stack.push(color.clone());
|
||||
self.upstream.set_style(&color)
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
self.color_stack.pop();
|
||||
match self.color_stack.last() {
|
||||
Some(previous) => self.upstream.set_style(previous),
|
||||
None => self.upstream.reset_style(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes to something implementing `std::io::Write`
|
||||
pub struct VecWrite<'v, T> {
|
||||
upstream: &'v mut Vec<T>,
|
||||
}
|
||||
|
||||
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<T>) -> 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<AnsiStyle>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
58
pretty-print/projects/pretty-print/src/text.rs
Normal file
58
pretty-print/projects/pretty-print/src/text.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
34
pretty-print/projects/pretty-print/src/traits/mod.rs
Normal file
34
pretty-print/projects/pretty-print/src/traits/mod.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
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<'a, T> {
|
||||
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
|
||||
///
|
||||
/// ```
|
||||
/// use pretty::{Arena, DocAllocator};
|
||||
///
|
||||
/// let arena = Arena::<()>::new();
|
||||
/// let body = arena.line().append("x");
|
||||
/// let doc = arena
|
||||
/// .text("let")
|
||||
/// .append(arena.line())
|
||||
/// .append("x")
|
||||
/// .group()
|
||||
/// .append(body.clone().flat_alt(arena.line().append("in").append(body)))
|
||||
/// .group();
|
||||
///
|
||||
/// 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<E>(self, inline: E) -> PrettyTree<'a, T>
|
||||
where
|
||||
E: Into<PrettyTree<'a, T>>;
|
||||
/// 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<'a, T>;
|
||||
|
||||
/// Increase the indentation level of this document.
|
||||
fn nest(self, offset: isize) -> PrettyTree<'a, T>;
|
||||
}
|
||||
39
pretty-print/projects/pretty-print/src/traits/printer.rs
Normal file
39
pretty-print/projects/pretty-print/src/traits/printer.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use crate::Text;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Marker trait for types that can be pretty printed.
|
||||
pub trait PrettyPrint<'a, T: Text<'a>> {
|
||||
/// Build a pretty tree for this type.
|
||||
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) {
|
||||
panic!("Error: {}", e);
|
||||
}
|
||||
buffer
|
||||
}
|
||||
/// 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) {
|
||||
panic!("Error: {}", e);
|
||||
}
|
||||
match String::from_utf8(buffer) {
|
||||
Ok(s) => s,
|
||||
Err(e) => panic!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> PrettyPrint<'a, T> for PrettyTree<'a, T> {
|
||||
fn pretty(&self, _: &PrettyProvider) -> PrettyTree<'a, T> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
84
pretty-print/projects/pretty-print/src/tree/display.rs
Normal file
84
pretty-print/projects/pretty-print/src/tree/display.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use super::*;
|
||||
|
||||
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::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() },
|
||||
Self::MaybeInline { block, inline } => Self::MaybeInline { block: block.clone(), inline: inline.clone() },
|
||||
Self::Nest { space, doc } => Self::Nest { space: *space, doc: doc.clone() },
|
||||
Self::RenderLength { length: len, body: doc } => Self::RenderLength { length: *len, body: doc.clone() },
|
||||
Self::Union { lhs: left, rhs: right } => Self::Union { lhs: left.clone(), rhs: right.clone() },
|
||||
Self::Column { invoke: column } => Self::Column { invoke: column.clone() },
|
||||
Self::Nesting { invoke: nesting } => Self::Nesting { invoke: nesting.clone() },
|
||||
Self::Fail => Self::Fail,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<'a, T>| match doc {
|
||||
PrettyTree::MaybeInline { block: flat, inline: alt } => {
|
||||
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Text(t)) if t.as_str() == " ")
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let is_line_ = |doc: &PrettyTree<'a, T>| match doc {
|
||||
PrettyTree::MaybeInline { block: flat, inline: alt } => {
|
||||
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Nil))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
match self {
|
||||
PrettyTree::Nil => f.debug_tuple("Nil").finish(),
|
||||
PrettyTree::Append { lhs: _, rhs: _ } => {
|
||||
let mut f = f.debug_list();
|
||||
append_docs(self, &mut |doc| {
|
||||
f.entry(doc);
|
||||
});
|
||||
f.finish()
|
||||
}
|
||||
_ if is_line(self) => f.debug_tuple("Line").finish(),
|
||||
_ if is_line_(self) => f.debug_tuple("Line?").finish(),
|
||||
PrettyTree::MaybeInline { block, inline } => f.debug_tuple("FlatAlt").field(block).field(inline).finish(),
|
||||
PrettyTree::Group { items } => {
|
||||
if is_line(self) {
|
||||
return f.debug_tuple("SoftLine").finish();
|
||||
}
|
||||
if is_line_(self) {
|
||||
return f.debug_tuple("SoftLine?").finish();
|
||||
}
|
||||
f.debug_tuple("Group").field(items).finish()
|
||||
}
|
||||
PrettyTree::Nest { space, doc } => f.debug_tuple("Nest").field(&space).field(doc).finish(),
|
||||
PrettyTree::Hardline => f.debug_tuple("Hardline").finish(),
|
||||
PrettyTree::RenderLength { body: doc, .. } => doc.fmt(f),
|
||||
PrettyTree::Text(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(),
|
||||
PrettyTree::Nesting { .. } => f.debug_tuple("Nesting(..)").finish(),
|
||||
PrettyTree::Fail => f.debug_tuple("Fail").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn append_docs<'a, T>(mut doc: &PrettyTree<'a, T>, consumer: &mut impl FnMut(&PrettyTree<'a, T>)) {
|
||||
loop {
|
||||
match doc {
|
||||
PrettyTree::Append { lhs, rhs } => {
|
||||
append_docs(lhs, consumer);
|
||||
doc = rhs;
|
||||
}
|
||||
_ => break consumer(doc),
|
||||
}
|
||||
}
|
||||
}
|
||||
50
pretty-print/projects/pretty-print/src/tree/into.rs
Normal file
50
pretty-print/projects/pretty-print/src/tree/into.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use super::*;
|
||||
|
||||
impl<'a, U, T: Text<'a>> Add<U> for PrettyTree<'a, T>
|
||||
where
|
||||
U: Into<Self>,
|
||||
{
|
||||
type Output = Self;
|
||||
fn add(self, rhs: U) -> Self::Output {
|
||||
self.append(rhs.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U, T: Text<'a>> AddAssign<U> for PrettyTree<'a, T>
|
||||
where
|
||||
U: Into<Self>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: U) {
|
||||
*self = self.clone().append(rhs.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U, T> From<Option<U>> for PrettyTree<'a, T>
|
||||
where
|
||||
Self: From<U>,
|
||||
{
|
||||
fn from(x: Option<U>) -> Self {
|
||||
match x {
|
||||
Some(x) => x.into(),
|
||||
None => Self::Nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<()> for PrettyTree<'a, T> {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Nil
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> From<&'static str> for PrettyTree<'a, T> {
|
||||
fn from(s: &'static str) -> Self {
|
||||
Self::Text(T::from_static_str(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> From<T> for PrettyTree<'a, T> {
|
||||
fn from(s: T) -> Self {
|
||||
Self::Text(s)
|
||||
}
|
||||
}
|
||||
434
pretty-print/projects/pretty-print/src/tree/mod.rs
Normal file
434
pretty-print/projects/pretty-print/src/tree/mod.rs
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
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::{borrow::Cow, io::Write};
|
||||
|
||||
mod display;
|
||||
mod into;
|
||||
|
||||
/// The concrete document type. This type is not meant to be used directly. Instead use the static
|
||||
/// functions on `Doc` or the methods on an `DocAllocator`.
|
||||
///
|
||||
/// The `T` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how
|
||||
/// it is used
|
||||
#[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(T),
|
||||
/// A document with ansi styles
|
||||
Annotated {
|
||||
/// The style to use for the text
|
||||
style: Rc<AnsiStyle>,
|
||||
/// The text to display
|
||||
body: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents
|
||||
Append {
|
||||
/// The first document
|
||||
lhs: Rc<Self>,
|
||||
/// The second document
|
||||
rhs: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents with a space in between
|
||||
Group {
|
||||
/// The first document
|
||||
items: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
MaybeInline {
|
||||
/// The first document
|
||||
block: Rc<Self>,
|
||||
/// The second document
|
||||
inline: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
Nest {
|
||||
/// The first document
|
||||
space: isize,
|
||||
/// The second document
|
||||
doc: Rc<Self>,
|
||||
},
|
||||
/// Stores the length of a string document that is not just ascii
|
||||
RenderLength {
|
||||
/// The length of the string
|
||||
length: usize,
|
||||
/// The document
|
||||
body: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
Union {
|
||||
/// The first document
|
||||
lhs: Rc<Self>,
|
||||
/// The second document
|
||||
rhs: Rc<Self>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
Column {
|
||||
/// The first document
|
||||
invoke: Rc<dyn Fn(usize) -> Self + 'a>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
Nesting {
|
||||
/// The first document
|
||||
invoke: Rc<dyn Fn(usize) -> Self + 'a>,
|
||||
},
|
||||
/// Concatenates two documents with a line in between
|
||||
Fail,
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
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(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::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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> PrettyTree<'a, T> {
|
||||
/// The given text, which must not contain line breaks.
|
||||
#[inline]
|
||||
pub fn text(data: impl Into<T>) -> Self {
|
||||
Self::Text(data.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
|
||||
/// Writes a rendered document to a `std::io::Write` object.
|
||||
#[inline]
|
||||
#[cfg(feature = "std")]
|
||||
pub fn render<W>(&self, width: usize, out: &mut W) -> std::io::Result<()>
|
||||
where
|
||||
W: ?Sized + std::io::Write,
|
||||
{
|
||||
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<T>) -> 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<W>(&self, width: usize, out: &mut W) -> core::fmt::Result
|
||||
where
|
||||
W: ?Sized + core::fmt::Write,
|
||||
{
|
||||
self.render_raw(width, &mut FmtWrite::new(out))
|
||||
}
|
||||
|
||||
/// Writes a rendered document to a `RenderAnnotated<A>` object.
|
||||
#[inline]
|
||||
pub fn render_raw<W>(&self, width: usize, out: &mut W) -> Result<(), W::Error>
|
||||
where
|
||||
W: RenderAnnotated<'a, T>,
|
||||
W: ?Sized,
|
||||
{
|
||||
render::best(Rc::new(self.clone()), width, out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
|
||||
/// The given text, which must not contain line breaks.
|
||||
#[inline]
|
||||
#[cfg(feature = "std")]
|
||||
pub fn render_colored<W: Write>(&self, width: usize, out: W) -> std::io::Result<()> {
|
||||
render::best(Rc::new(self.clone()), width, &mut crate::TerminalWriter::new(out))
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// ```
|
||||
/// use pretty::{Arena, DocAllocator};
|
||||
///
|
||||
/// let arena = Arena::<()>::new();
|
||||
/// let body = arena.line().append("x");
|
||||
/// let doc = arena
|
||||
/// .text("let")
|
||||
/// .append(arena.line())
|
||||
/// .append("x")
|
||||
/// .group()
|
||||
/// .append(body.clone().flat_alt(arena.line().append("in").append(body)))
|
||||
/// .group();
|
||||
///
|
||||
/// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
|
||||
/// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
|
||||
/// ```
|
||||
#[inline]
|
||||
fn flat_alt<E>(self, flat: E) -> Self
|
||||
where
|
||||
E: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
Self::MaybeInline { block: Rc::new(self), inline: Rc::new(flat.into()) }
|
||||
}
|
||||
/// Indents `self` by `adjust` spaces from the current cursor position
|
||||
///
|
||||
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||
/// like `RefDoc` or `RcDoc`
|
||||
///
|
||||
/// ```rust
|
||||
/// use pretty::DocAllocator;
|
||||
///
|
||||
/// let arena = pretty::Arena::<()>::new();
|
||||
/// let doc = arena
|
||||
/// .text("prefix")
|
||||
/// .append(arena.text(" "))
|
||||
/// .append(arena.reflow("The indent function indents these words!").indent(4));
|
||||
/// assert_eq!(
|
||||
/// doc.1.pretty(24).to_string(),
|
||||
/// "
|
||||
/// prefix The indent
|
||||
/// function
|
||||
/// indents these
|
||||
/// words!"
|
||||
/// .trim_start(),
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
fn indent(self, adjust: usize) -> Self {
|
||||
let spaces = {
|
||||
use crate::render::SPACES;
|
||||
let mut doc = PrettyTree::Nil;
|
||||
let mut remaining = adjust;
|
||||
while remaining != 0 {
|
||||
let i = SPACES.len().min(remaining);
|
||||
remaining -= i;
|
||||
doc = doc.append(PrettyTree::text(T::from_static_spaces(&SPACES[..i])))
|
||||
}
|
||||
doc
|
||||
};
|
||||
spaces.append(self).hang(adjust.try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Increase the indentation level of this document.
|
||||
#[inline]
|
||||
fn nest(self, offset: isize) -> Self {
|
||||
if let Self::Nil = self {
|
||||
return self;
|
||||
}
|
||||
if offset == 0 {
|
||||
return self;
|
||||
}
|
||||
Self::Nest { space: offset, doc: Rc::new(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
|
||||
/// Append the given document after this document.
|
||||
#[inline]
|
||||
pub fn append<E>(self, follow: E) -> Self
|
||||
where
|
||||
E: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
let rhs = follow.into();
|
||||
match (&self, &rhs) {
|
||||
(Self::Nil, _) => rhs,
|
||||
(_, Self::Nil) => self,
|
||||
_ => Self::Append { lhs: Rc::new(self), rhs: Rc::new(rhs) },
|
||||
}
|
||||
}
|
||||
/// Allocate a document that intersperses the given separator `S` between the given documents
|
||||
/// `[A, B, C, ..., Z]`, yielding `[A, S, B, S, C, S, ..., S, Z]`.
|
||||
///
|
||||
/// Compare [the `intersperse` method from the `itertools` crate](https://docs.rs/itertools/0.5.9/itertools/trait.Itertools.html#method.intersperse).
|
||||
///
|
||||
/// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
|
||||
/// like `RefDoc` or `RcDoc`
|
||||
#[inline]
|
||||
pub fn join<I, T1, T2>(terms: I, joint: T2) -> PrettyTree<'a, T>
|
||||
where
|
||||
I: IntoIterator<Item = T1>,
|
||||
T1: Into<PrettyTree<'a, T>>,
|
||||
T2: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
let joint = joint.into();
|
||||
let mut iter = terms.into_iter().map(|s| s.into());
|
||||
let mut terms = PrettySequence::new(0);
|
||||
terms += iter.next().unwrap_or(PrettyTree::Nil);
|
||||
for term in iter {
|
||||
terms += joint.clone();
|
||||
terms += term;
|
||||
}
|
||||
terms.into()
|
||||
}
|
||||
/// Allocate a document that intersperses the given separator `S` between the given documents
|
||||
pub fn concat<I>(docs: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
let mut head = Self::Nil;
|
||||
for item in docs.into_iter() {
|
||||
head += item.into();
|
||||
}
|
||||
head
|
||||
}
|
||||
|
||||
/// Mark this document as a group.
|
||||
///
|
||||
/// Groups are layed out on a single line if possible. Within a group, all basic documents with
|
||||
/// several possible layouts are assigned the same layout, that is, they are all layed out
|
||||
/// horizontally and combined into a one single line, or they are each layed out on their own
|
||||
/// line.
|
||||
#[inline]
|
||||
pub fn group(self) -> Self {
|
||||
match self {
|
||||
Self::Group { .. } | Self::Text(_) | Self::Nil => self,
|
||||
_ => Self::Group { items: Rc::new(self) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this document as a comment.
|
||||
#[inline]
|
||||
pub fn annotate(self, style: Rc<AnsiStyle>) -> Self {
|
||||
Self::Annotated { style, body: Rc::new(self) }
|
||||
}
|
||||
/// Mark this document as a hard line break.
|
||||
#[inline]
|
||||
pub fn union<E>(self, other: E) -> Self
|
||||
where
|
||||
E: Into<PrettyTree<'a, T>>,
|
||||
{
|
||||
Self::Union { lhs: Rc::new(self), rhs: Rc::new(other.into()) }
|
||||
}
|
||||
|
||||
/// Lays out `self` so with the nesting level set to the current column
|
||||
///
|
||||
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||
/// like `RefDoc` or `RcDoc`
|
||||
///
|
||||
/// ```rust
|
||||
/// use pretty::{docs, DocAllocator};
|
||||
///
|
||||
/// let arena = &pretty::Arena::<()>::new();
|
||||
/// let doc = docs![
|
||||
/// arena,
|
||||
/// "lorem",
|
||||
/// " ",
|
||||
/// arena.intersperse(["ipsum", "dolor"].iter().cloned(), arena.line_()).align(),
|
||||
/// arena.hardline(),
|
||||
/// "next",
|
||||
/// ];
|
||||
/// assert_eq!(doc.1.pretty(80).to_string(), "lorem ipsum\n dolor\nnext");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn align(self) -> Self {
|
||||
Self::Column {
|
||||
invoke: Rc::new(move |col| {
|
||||
let self_ = self.clone();
|
||||
Self::Nesting { invoke: Rc::new(move |nest| self_.clone().nest(col as isize - nest as isize)) }
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lays out `self` with a nesting level set to the current level plus `adjust`.
|
||||
///
|
||||
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||
/// like `RefDoc` or `RcDoc`
|
||||
///
|
||||
/// ```rust
|
||||
/// use pretty::DocAllocator;
|
||||
///
|
||||
/// let arena = pretty::Arena::<()>::new();
|
||||
/// let doc = arena
|
||||
/// .text("prefix")
|
||||
/// .append(arena.text(" "))
|
||||
/// .append(arena.reflow("Indenting these words with nest").hang(4));
|
||||
/// assert_eq!(
|
||||
/// doc.1.pretty(24).to_string(),
|
||||
/// "prefix Indenting these\n words with\n nest",
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn hang(self, adjust: isize) -> Self {
|
||||
self.nest(adjust).align()
|
||||
}
|
||||
|
||||
/// Lays out `self` and provides the column width of it available to `f`
|
||||
///
|
||||
/// NOTE: The doc pointer type, `D` may need to be cloned. Consider using cheaply cloneable ptr
|
||||
/// like `RefDoc` or `RcDoc`
|
||||
///
|
||||
/// ```rust
|
||||
/// use pretty::DocAllocator;
|
||||
///
|
||||
/// let arena = pretty::Arena::<()>::new();
|
||||
/// let doc = arena
|
||||
/// .text("prefix ")
|
||||
/// .append(arena.column(|l| arena.text("| <- column ").append(arena.as_string(l)).into_doc()));
|
||||
/// assert_eq!(doc.1.pretty(80).to_string(), "prefix | <- column 7");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn width<F>(self, f: F) -> Self
|
||||
where
|
||||
F: Fn(isize) -> Self + Clone + 'static,
|
||||
T: Clone,
|
||||
{
|
||||
Self::Column {
|
||||
invoke: Rc::new(move |start| {
|
||||
let f = f.clone();
|
||||
self.clone() + Self::Column { invoke: Rc::new(move |end| f(end as isize - start as isize)) }
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts `self` between `before` and `after`
|
||||
#[inline]
|
||||
pub fn enclose<E, F>(self, before: E, after: F) -> Self
|
||||
where
|
||||
E: Into<Self>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
before.into().append(self).append(after.into())
|
||||
}
|
||||
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn single_quotes(self) -> Self {
|
||||
self.enclose("'", "'")
|
||||
}
|
||||
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn double_quotes(self) -> Self {
|
||||
self.enclose("\"", "\"")
|
||||
}
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn parens(self) -> Self {
|
||||
self.enclose("(", ")")
|
||||
}
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn angles(self) -> Self {
|
||||
self.enclose("<", ">")
|
||||
}
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn braces(self) -> Self {
|
||||
self.enclose("{", "}")
|
||||
}
|
||||
/// Puts `self` between `before` and `after` if `cond` is true
|
||||
pub fn brackets(self) -> Self {
|
||||
self.enclose("[", "]")
|
||||
}
|
||||
}
|
||||
6
pretty-print/projects/pretty-print/tests/main.rs
Normal file
6
pretty-print/projects/pretty-print/tests/main.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#[test]
|
||||
fn ready() {
|
||||
println!("it works!")
|
||||
}
|
||||
|
||||
mod readme;
|
||||
5
pretty-print/projects/pretty-print/tests/readme.md
Normal file
5
pretty-print/projects/pretty-print/tests/readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## Tests
|
||||
|
||||
```bash
|
||||
wee test
|
||||
```
|
||||
39
pretty-print/projects/pretty-print/tests/readme.rs
Normal file
39
pretty-print/projects/pretty-print/tests/readme.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#![allow(unused)]
|
||||
use SExp::*;
|
||||
use pretty_print::*;
|
||||
enum SExp {
|
||||
Atom(u32),
|
||||
List(Vec<SExp>),
|
||||
}
|
||||
|
||||
impl SExp {
|
||||
/// Return a pretty printed format of self.
|
||||
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.iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group())
|
||||
.append(PrettyTree::text(")")),
|
||||
}
|
||||
}
|
||||
/// Return a pretty printed format of self.
|
||||
pub fn to_pretty(&self, width: usize) -> String {
|
||||
let mut w = Vec::new();
|
||||
self.to_doc().render(width, &mut w).unwrap();
|
||||
String::from_utf8(w).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let atom = Atom(5);
|
||||
assert_eq!("5", atom.to_pretty(10));
|
||||
let list = List(vec![Atom(1), Atom(2), Atom(3)]);
|
||||
assert_eq!("(1 2 3)", list.to_pretty(10));
|
||||
assert_eq!(
|
||||
"\
|
||||
(1
|
||||
2
|
||||
3)",
|
||||
list.to_pretty(5)
|
||||
);
|
||||
}
|
||||
30
pretty-print/projects/pretty-test/Cargo.toml
Normal file
30
pretty-print/projects/pretty-test/Cargo.toml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "pretty-test"
|
||||
publish = false
|
||||
version = "0.0.0"
|
||||
authors = ["Aster <192607617@qq.com>"]
|
||||
description = "..."
|
||||
repository = "https://github.com/oovm/sub_projects"
|
||||
documentation = "https://docs.rs/sub_projects"
|
||||
readme = "../../Readme.md"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
exclude = ["package.json", "tests/**"]
|
||||
|
||||
[dependencies]
|
||||
pretty = "0.12.1"
|
||||
|
||||
[dependencies.logparse-pretty-print]
|
||||
version = "*"
|
||||
default-features = false
|
||||
features = ["std"]
|
||||
path = "../pretty-print"
|
||||
name = "pretty-print"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
6
pretty-print/projects/pretty-test/package.json
Normal file
6
pretty-print/projects/pretty-test/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"p": "cargo publish --allow-dirty"
|
||||
}
|
||||
}
|
||||
2
pretty-print/projects/pretty-test/readme.md
Normal file
2
pretty-print/projects/pretty-test/readme.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Title
|
||||
=====
|
||||
1
pretty-print/projects/pretty-test/src/errors.rs
Normal file
1
pretty-print/projects/pretty-test/src/errors.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
5
pretty-print/projects/pretty-test/src/lib.rs
Normal file
5
pretty-print/projects/pretty-test/src/lib.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||
#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
|
||||
#![doc = include_str!("../readme.md")]
|
||||
#![doc(html_logo_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||
#![doc(html_favicon_url = "https://raw.githubusercontent.com/oovm/shape-rs/dev/projects/images/Trapezohedron.svg")]
|
||||
48
pretty-print/projects/pretty-test/tests/main.rs
Normal file
48
pretty-print/projects/pretty-test/tests/main.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#![allow(dead_code, unused)]
|
||||
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() {
|
||||
println!("it works!")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box1() {
|
||||
let doc = PrettyTree::text("hello")
|
||||
.annotate(Rc::new(AnsiStyle::new(AnsiColor::Red)))
|
||||
.append(PrettyTree::Hardline)
|
||||
.append("the")
|
||||
.append("∷")
|
||||
.append("world");
|
||||
let mut buffer = vec![];
|
||||
doc.render_colored(10, &mut buffer).unwrap();
|
||||
|
||||
println!("{}", String::from_utf8_lossy(&buffer));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box2() {
|
||||
let doc = PrettyTree::join(vec!["hello", " ", " ", "world"], "∷").group();
|
||||
let mut buffer = vec![];
|
||||
doc.render_colored(10, &mut buffer).unwrap();
|
||||
|
||||
println!("{}", String::from_utf8_lossy(&buffer));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_doc_inference() {
|
||||
let doc = PrettyTree::text("test").append(PrettyTree::line_or_space()).append(PrettyTree::text("test")).group();
|
||||
|
||||
println!("{}", doc.pretty(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn newline_in_text() {
|
||||
let doc = PrettyTree::text("test")
|
||||
.append(PrettyTree::line_or_space().append(PrettyTree::text("\"test\n test\"")).nest(4))
|
||||
.group();
|
||||
println!("{}", doc.pretty(10));
|
||||
}
|
||||
5
pretty-print/projects/pretty-test/tests/readme.md
Normal file
5
pretty-print/projects/pretty-test/tests/readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## Tests
|
||||
|
||||
```bash
|
||||
wee test
|
||||
```
|
||||
9
pretty-print/rustfmt.toml
Normal file
9
pretty-print/rustfmt.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
version = "Two"
|
||||
edition = "2018"
|
||||
max_width = 128
|
||||
|
||||
imports_granularity = "Crate"
|
||||
use_small_heuristics = "Max"
|
||||
control_brace_style = "ClosingNextLine"
|
||||
normalize_comments = true
|
||||
format_code_in_doc_comments = true
|
||||
|
|
@ -6,7 +6,7 @@ use crate::tui::{
|
|||
LogViewer,
|
||||
input::{FieldMatcher, InputTarget},
|
||||
},
|
||||
model::{FieldsName, LogEntry},
|
||||
model::{LogEntry, SpanDescriptor},
|
||||
};
|
||||
|
||||
mod serialize_regex {
|
||||
|
|
@ -56,7 +56,7 @@ impl MatcherValue {
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum Matcher {
|
||||
Field {
|
||||
span: FieldsName,
|
||||
span: SpanDescriptor,
|
||||
name: String,
|
||||
value: MatcherValue,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use crate::tui::{
|
|||
input::{FieldMatcher, InputState, InputTarget},
|
||||
view::LogView,
|
||||
},
|
||||
model::{FieldsName, LogEntry, id},
|
||||
model::{LogEntry, SpanDescriptor, id},
|
||||
processing::Cursor,
|
||||
widgets::{fieldtree, last_error::LastError},
|
||||
};
|
||||
|
|
@ -54,7 +54,7 @@ impl LogViewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_selected_field(&self) -> Option<(FieldsName, String, String)> {
|
||||
pub fn get_selected_field(&self) -> Option<(SpanDescriptor, String, String)> {
|
||||
let (span_idx, name_idx) = self.field_state.get()?;
|
||||
let entry = self.selected().map(|(s, _)| s)?;
|
||||
|
||||
|
|
|
|||
146
src/tui/mod.rs
146
src/tui/mod.rs
|
|
@ -1,3 +1,5 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
use arboard::SetExtLinux;
|
||||
use crossterm::{
|
||||
event::{
|
||||
DisableMouseCapture, EnableMouseCapture, KeyEventKind, KeyEventState, MouseButton,
|
||||
|
|
@ -5,7 +7,11 @@ use crossterm::{
|
|||
},
|
||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
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},
|
||||
|
|
@ -23,10 +29,11 @@ use crate::tui::{
|
|||
LogViewer,
|
||||
input::{FieldMatcher, InputState, InputTarget},
|
||||
},
|
||||
model::SpanDescriptor,
|
||||
reader::LogfileReader,
|
||||
widgets::{
|
||||
fieldtree::FieldTree, hyperlink::Hyperlink, items::Items, last_error::LastError,
|
||||
styled::IntoStyled,
|
||||
line_text::style_span, styled::IntoStyled,
|
||||
},
|
||||
};
|
||||
use ratatui::{
|
||||
|
|
@ -37,7 +44,7 @@ use ratatui::{
|
|||
prelude::CrosstermBackend,
|
||||
style::Style,
|
||||
symbols::merge::MergeStrategy,
|
||||
text::Line,
|
||||
text::{Line, Span, Text},
|
||||
widgets::{
|
||||
Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap,
|
||||
},
|
||||
|
|
@ -64,6 +71,8 @@ const HELP_TEXT: &str = "Generic:
|
|||
-> / enter / l enter nested view
|
||||
|
||||
f toggle show active filters
|
||||
z zoom in on a selected field
|
||||
y copy the selected value to the clipboard
|
||||
|
||||
───────────────────────────────────────────────────────
|
||||
targeting logs:
|
||||
|
|
@ -72,7 +81,7 @@ targeting logs:
|
|||
(to target values of fields)
|
||||
|
||||
t the currently selected log
|
||||
the surrounding element (when inside a span)
|
||||
s the surrounding element (when inside a span)
|
||||
|
||||
either a field after `f` or the text of the current log:
|
||||
p ... with a prefix
|
||||
|
|
@ -117,6 +126,11 @@ pub fn run(logs_dir: PathBuf, compiler_root: Option<PathBuf>, theme: ThemeName)
|
|||
}
|
||||
}
|
||||
|
||||
enum ZoomTarget {
|
||||
Field { span: SpanDescriptor, name: String },
|
||||
Log,
|
||||
}
|
||||
|
||||
enum Tab {
|
||||
FileChooser {
|
||||
files: Vec<DirEntry>,
|
||||
|
|
@ -127,6 +141,10 @@ enum Tab {
|
|||
LogViewer(LogViewer),
|
||||
Empty,
|
||||
Help,
|
||||
Zoom {
|
||||
target: ZoomTarget,
|
||||
value: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
@ -137,6 +155,21 @@ impl Tab {
|
|||
(Tab::LogViewer(_), Some(path)) => format!("logs of {}", path.display()),
|
||||
(Tab::LogViewer(_), None) => "logs".to_string(),
|
||||
(Tab::Help, _) => "help".to_string(),
|
||||
(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}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,8 +275,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 { .. } => self.pop_tab(),
|
||||
Tab::FileChooser {
|
||||
files,
|
||||
state,
|
||||
|
|
@ -327,6 +361,52 @@ impl App {
|
|||
|
||||
KeyCode::Char('u') => lv.undo(),
|
||||
KeyCode::Char('r') => lv.redo(),
|
||||
KeyCode::Char('z') => {
|
||||
if let Some((span, name, value)) = lv.get_selected_field() {
|
||||
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') => {
|
||||
let value = if let Some((_, _, value)) = lv.get_selected_field() {
|
||||
if let InputState::Target(InputTarget::Fields(..)) = lv.input_state {
|
||||
Some(value)
|
||||
} else if let Some((l, _)) = lv.selected() {
|
||||
Some(l.line_text("".to_string(), &lv.filters).message)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(value) = value {
|
||||
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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyCode::Tab => {
|
||||
lv.input_state.target(InputTarget::Fields(None));
|
||||
|
|
@ -585,13 +665,14 @@ 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);
|
||||
|
||||
let (footer_focused, header_focused) = match self.current_tab() {
|
||||
Tab::Help => (false, false),
|
||||
Tab::Zoom { .. } => (false, false),
|
||||
Tab::FileChooser { .. } => (false, true),
|
||||
Tab::LogViewer(lv) => {
|
||||
let target_fields =
|
||||
|
|
@ -716,6 +797,61 @@ impl Widget for &mut App {
|
|||
|
||||
Paragraph::new(HELP_TEXT).render(popup_area, buf);
|
||||
}
|
||||
Tab::Zoom { value, .. } => {
|
||||
Clear.render(popup_area, buf);
|
||||
let popup_area = {
|
||||
let block = Block::bordered()
|
||||
.title_top("zoom")
|
||||
.style(styles.default)
|
||||
.padding(Padding::symmetric(3, 1))
|
||||
.border_style(styles.border_highlighted);
|
||||
let inner = block.inner(popup_area);
|
||||
block.render(popup_area, buf);
|
||||
inner
|
||||
};
|
||||
|
||||
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 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)
|
||||
})
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.last_error.clone().styled(&styles).render(error, buf);
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ unsafe impl<V: Visitor> TraceWith<V> for RawLogEntry {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum FieldsName {
|
||||
pub enum SpanDescriptor {
|
||||
/// The main fields (not of a span) of a log entry
|
||||
Main,
|
||||
Span(String),
|
||||
|
|
@ -409,23 +409,23 @@ pub struct SpansRef<'a> {
|
|||
}
|
||||
|
||||
impl<'a> SpansRef<'a> {
|
||||
pub fn find(&self, span: &FieldsName, name: &str) -> Option<&String> {
|
||||
pub fn find(&self, span: &SpanDescriptor, name: &str) -> Option<&String> {
|
||||
self.named()
|
||||
.find(|(name, _)| name == span)
|
||||
.and_then(|(_, fields)| fields.get(name))
|
||||
}
|
||||
|
||||
pub fn named(&self) -> impl Iterator<Item = (FieldsName, &'a LogFields)> {
|
||||
iter::once((FieldsName::Main, self.main)).chain(self.spans.iter().rev().enumerate().map(
|
||||
|(idx, fields)| {
|
||||
pub fn named(&self) -> impl Iterator<Item = (SpanDescriptor, &'a LogFields)> {
|
||||
iter::once((SpanDescriptor::Main, self.main)).chain(
|
||||
self.spans.iter().rev().enumerate().map(|(idx, fields)| {
|
||||
(
|
||||
fields
|
||||
.name()
|
||||
.map(|i| FieldsName::Span(i.to_string()))
|
||||
.unwrap_or_else(|| FieldsName::Numbered(idx)),
|
||||
.map(|i| SpanDescriptor::Span(i.to_string()))
|
||||
.unwrap_or_else(|| SpanDescriptor::Numbered(idx)),
|
||||
fields,
|
||||
)
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ mod tests {
|
|||
use crate::tui::{
|
||||
filter::{Filter, FilterKind, Matcher, MatcherValue},
|
||||
log_viewer::filters::Filters,
|
||||
model::FieldsName,
|
||||
model::SpanDescriptor,
|
||||
processing::Cursor,
|
||||
widgets::last_error::LastError,
|
||||
};
|
||||
|
|
@ -311,7 +311,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "message".to_string(),
|
||||
value: MatcherValue::Exact("foo".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Remove,
|
||||
}));
|
||||
|
|
@ -337,7 +337,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "message".to_string(),
|
||||
value: MatcherValue::Exact("baz".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Remove,
|
||||
}));
|
||||
|
|
@ -368,7 +368,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "name".to_string(),
|
||||
value: MatcherValue::Exact("nest".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Inline,
|
||||
}));
|
||||
|
|
@ -415,7 +415,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "name".to_string(),
|
||||
value: MatcherValue::Exact("nest".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Inline,
|
||||
}));
|
||||
|
|
@ -465,7 +465,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "name".to_string(),
|
||||
value: MatcherValue::Exact("nest1".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Inline,
|
||||
}));
|
||||
|
|
@ -473,7 +473,7 @@ mod tests {
|
|||
matcher: Matcher::Field {
|
||||
name: "name".to_string(),
|
||||
value: MatcherValue::Exact("nest2".to_string()),
|
||||
span: FieldsName::Main,
|
||||
span: SpanDescriptor::Main,
|
||||
},
|
||||
kind: FilterKind::Inline,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use tui_widget_list::{ListBuilder, ListState, ListView};
|
|||
use crate::tui::{
|
||||
block_around,
|
||||
log_viewer::LogViewer,
|
||||
model::{FieldsName, 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: &[(FieldsName, &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<&FieldsName>) -> (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
|
||||
|
|
@ -146,9 +154,9 @@ impl Styled<'_, &mut FieldTree<'_>> {
|
|||
self.styles,
|
||||
Some(if let Some(i) = name {
|
||||
match i {
|
||||
FieldsName::Main => "own fields".to_string(),
|
||||
FieldsName::Span(s) => format!("fields of {s}"),
|
||||
FieldsName::Numbered(idx) => format!("fields of span #{idx}"),
|
||||
SpanDescriptor::Main => "own fields".to_string(),
|
||||
SpanDescriptor::Span(s) => format!("fields of {s}"),
|
||||
SpanDescriptor::Numbered(idx) => format!("fields of span #{idx}"),
|
||||
}
|
||||
} else {
|
||||
"fields".to_string()
|
||||
|
|
@ -191,9 +199,9 @@ impl Widget for Styled<'_, &mut FieldTree<'_>> {
|
|||
};
|
||||
|
||||
let mut item = Line::from(match span_name {
|
||||
FieldsName::Main => "own fields".to_string(),
|
||||
FieldsName::Span(s) => s.to_string(),
|
||||
FieldsName::Numbered(idx) => format!("span #{idx}"),
|
||||
SpanDescriptor::Main => "own fields".to_string(),
|
||||
SpanDescriptor::Span(s) => s.to_string(),
|
||||
SpanDescriptor::Numbered(idx) => format!("span #{idx}"),
|
||||
});
|
||||
if cx.is_selected && self_focussed {
|
||||
item = item.style(self.styles.highlighted);
|
||||
|
|
@ -220,6 +228,7 @@ impl Widget for Styled<'_, &mut FieldTree<'_>> {
|
|||
i,
|
||||
Config {
|
||||
collapse_space: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::tui::{
|
|||
filters::Filters,
|
||||
input::{FieldMatcher, InputState, InputTarget},
|
||||
},
|
||||
model::{FieldsName, LogEntry},
|
||||
model::{LogEntry, SpanDescriptor},
|
||||
widgets::{
|
||||
last_error::LastError,
|
||||
line_text::Highlighted,
|
||||
|
|
@ -23,7 +23,7 @@ pub struct Items<'a> {
|
|||
input_state: &'a InputState,
|
||||
filters: &'a Filters,
|
||||
|
||||
selected_footer_field: Option<(FieldsName, String, String)>,
|
||||
selected_footer_field: Option<(SpanDescriptor, String, String)>,
|
||||
last_error: LastError,
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ impl<'a> Items<'a> {
|
|||
filters: &'a Filters,
|
||||
selected_offset: usize,
|
||||
input_state: &'a InputState,
|
||||
selected_footer_field: Option<(FieldsName, String, String)>,
|
||||
selected_footer_field: Option<(SpanDescriptor, String, String)>,
|
||||
last_error: LastError,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ impl Into<Line<'static>> for Styled<'_, LineText> {
|
|||
parsed,
|
||||
Config {
|
||||
collapse_space: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue