├── .gitignore ├── .gitlab-ci.yml ├── .hooks.yml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── kakpipe.png ├── rc └── kakpipe.kak └── src ├── args.rs ├── cmd ├── faces.rs ├── fifo.rs ├── mod.rs └── range_specs.rs ├── main.rs ├── mktemp.rs └── range_specs.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/rust 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=rust 4 | 5 | ### Rust ### 6 | # Generated by Cargo 7 | # will have compiled files and executables 8 | /target/ 9 | 10 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 11 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 12 | #Cargo.lock 13 | 14 | # End of https://www.toptal.com/developers/gitignore/api/rust 15 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - project: templates/gitlab 3 | file: 4 | - build_rust.yml 5 | -------------------------------------------------------------------------------- /.hooks.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: https://git.itsufficient.me/templates/git-rust-hooks.git 3 | hooks: 4 | - name: cargo fmt 5 | - name: cargo check 6 | - name: cargo test 7 | - name: cargo clippy 8 | - name: version_match 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - 0.5.0 - define !! command on kakpipe buffer to close it restart with same args 2 | - 0.4.0 - ability to define set, clear and export env var. error message on fifo buffer 3 | - 0.3.0 - add a argument to fifo to choose buffer prefix 4 | - 0.2.0 - kill processes when closing buffer. remove temp files 5 | - 0.1.4 - add -D flag to set option for fifo buffer 6 | - 0.1.3 - use structopt instead of arhg because of issue #93 7 | - 0.1.2 - add --rw switch as fifo is readonly by default 8 | - 0.1.1 - fix escape for face mode 9 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.66" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" 10 | 11 | [[package]] 12 | name = "argh" 13 | version = "0.1.9" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "c375edecfd2074d5edcc31396860b6e54b6f928714d0e097b983053fac0cabe3" 16 | dependencies = [ 17 | "argh_derive", 18 | "argh_shared", 19 | ] 20 | 21 | [[package]] 22 | name = "argh_derive" 23 | version = "0.1.9" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "aa013479b80109a1bf01a039412b0f0013d716f36921226d86c6709032fb7a03" 26 | dependencies = [ 27 | "argh_shared", 28 | "heck", 29 | "proc-macro2", 30 | "quote", 31 | "syn", 32 | ] 33 | 34 | [[package]] 35 | name = "argh_shared" 36 | version = "0.1.9" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "149f75bbec1827618262e0855a68f0f9a7f2edc13faebf33c4f16d6725edb6a9" 39 | 40 | [[package]] 41 | name = "async-channel" 42 | version = "1.7.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" 45 | dependencies = [ 46 | "concurrent-queue", 47 | "event-listener", 48 | "futures-core", 49 | ] 50 | 51 | [[package]] 52 | name = "async-executor" 53 | version = "1.4.1" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 56 | dependencies = [ 57 | "async-task", 58 | "concurrent-queue", 59 | "fastrand", 60 | "futures-lite", 61 | "once_cell", 62 | "slab", 63 | ] 64 | 65 | [[package]] 66 | name = "async-global-executor" 67 | version = "2.3.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" 70 | dependencies = [ 71 | "async-channel", 72 | "async-executor", 73 | "async-io", 74 | "async-lock", 75 | "blocking", 76 | "futures-lite", 77 | "once_cell", 78 | ] 79 | 80 | [[package]] 81 | name = "async-io" 82 | version = "1.10.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7" 85 | dependencies = [ 86 | "async-lock", 87 | "autocfg", 88 | "concurrent-queue", 89 | "futures-lite", 90 | "libc", 91 | "log", 92 | "parking", 93 | "polling", 94 | "slab", 95 | "socket2", 96 | "waker-fn", 97 | "winapi", 98 | ] 99 | 100 | [[package]] 101 | name = "async-lock" 102 | version = "2.6.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" 105 | dependencies = [ 106 | "event-listener", 107 | "futures-lite", 108 | ] 109 | 110 | [[package]] 111 | name = "async-process" 112 | version = "1.5.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" 115 | dependencies = [ 116 | "async-io", 117 | "autocfg", 118 | "blocking", 119 | "cfg-if", 120 | "event-listener", 121 | "futures-lite", 122 | "libc", 123 | "once_cell", 124 | "signal-hook", 125 | "winapi", 126 | ] 127 | 128 | [[package]] 129 | name = "async-std" 130 | version = "1.12.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" 133 | dependencies = [ 134 | "async-channel", 135 | "async-global-executor", 136 | "async-io", 137 | "async-lock", 138 | "async-process", 139 | "crossbeam-utils", 140 | "futures-channel", 141 | "futures-core", 142 | "futures-io", 143 | "futures-lite", 144 | "gloo-timers", 145 | "kv-log-macro", 146 | "log", 147 | "memchr", 148 | "once_cell", 149 | "pin-project-lite", 150 | "pin-utils", 151 | "slab", 152 | "wasm-bindgen-futures", 153 | ] 154 | 155 | [[package]] 156 | name = "async-task" 157 | version = "4.3.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" 160 | 161 | [[package]] 162 | name = "atomic-waker" 163 | version = "1.0.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 166 | 167 | [[package]] 168 | name = "autocfg" 169 | version = "1.1.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 172 | 173 | [[package]] 174 | name = "bitflags" 175 | version = "1.2.1" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 178 | 179 | [[package]] 180 | name = "blocking" 181 | version = "1.2.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" 184 | dependencies = [ 185 | "async-channel", 186 | "async-task", 187 | "atomic-waker", 188 | "fastrand", 189 | "futures-lite", 190 | "once_cell", 191 | ] 192 | 193 | [[package]] 194 | name = "boxfnonce" 195 | version = "0.1.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "5988cb1d626264ac94100be357308f29ff7cbdd3b36bda27f450a4ee3f713426" 198 | 199 | [[package]] 200 | name = "bumpalo" 201 | version = "3.11.1" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 204 | 205 | [[package]] 206 | name = "byteorder" 207 | version = "1.4.3" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 210 | 211 | [[package]] 212 | name = "cache-padded" 213 | version = "1.2.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" 216 | 217 | [[package]] 218 | name = "cc" 219 | version = "1.0.74" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" 222 | 223 | [[package]] 224 | name = "cfg-if" 225 | version = "1.0.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 228 | 229 | [[package]] 230 | name = "concurrent-queue" 231 | version = "1.2.4" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" 234 | dependencies = [ 235 | "cache-padded", 236 | ] 237 | 238 | [[package]] 239 | name = "crossbeam-utils" 240 | version = "0.8.12" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" 243 | dependencies = [ 244 | "cfg-if", 245 | ] 246 | 247 | [[package]] 248 | name = "ctor" 249 | version = "0.1.26" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 252 | dependencies = [ 253 | "quote", 254 | "syn", 255 | ] 256 | 257 | [[package]] 258 | name = "daemonize" 259 | version = "0.4.1" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "70c24513e34f53b640819f0ac9f705b673fcf4006d7aab8778bee72ebfc89815" 262 | dependencies = [ 263 | "boxfnonce", 264 | "libc", 265 | ] 266 | 267 | [[package]] 268 | name = "event-listener" 269 | version = "2.5.3" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 272 | 273 | [[package]] 274 | name = "fastrand" 275 | version = "1.8.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 278 | dependencies = [ 279 | "instant", 280 | ] 281 | 282 | [[package]] 283 | name = "futures-channel" 284 | version = "0.3.25" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 287 | dependencies = [ 288 | "futures-core", 289 | ] 290 | 291 | [[package]] 292 | name = "futures-core" 293 | version = "0.3.25" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 296 | 297 | [[package]] 298 | name = "futures-io" 299 | version = "0.3.25" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" 302 | 303 | [[package]] 304 | name = "futures-lite" 305 | version = "1.12.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 308 | dependencies = [ 309 | "fastrand", 310 | "futures-core", 311 | "futures-io", 312 | "memchr", 313 | "parking", 314 | "pin-project-lite", 315 | "waker-fn", 316 | ] 317 | 318 | [[package]] 319 | name = "getrandom" 320 | version = "0.2.8" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 323 | dependencies = [ 324 | "cfg-if", 325 | "libc", 326 | "wasi", 327 | ] 328 | 329 | [[package]] 330 | name = "gloo-timers" 331 | version = "0.2.4" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" 334 | dependencies = [ 335 | "futures-channel", 336 | "futures-core", 337 | "js-sys", 338 | "wasm-bindgen", 339 | ] 340 | 341 | [[package]] 342 | name = "heck" 343 | version = "0.4.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 346 | 347 | [[package]] 348 | name = "instant" 349 | version = "0.1.12" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 352 | dependencies = [ 353 | "cfg-if", 354 | ] 355 | 356 | [[package]] 357 | name = "js-sys" 358 | version = "0.3.60" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 361 | dependencies = [ 362 | "wasm-bindgen", 363 | ] 364 | 365 | [[package]] 366 | name = "kak" 367 | version = "0.1.2" 368 | source = "git+https://github.com/eburghar/kak.rs.git?tag=v0.1.2#5799d1bd4724504e50da98ea60ee4f7351c697a7" 369 | dependencies = [ 370 | "anyhow", 371 | "async-std", 372 | "byteorder", 373 | "yew-ansi", 374 | ] 375 | 376 | [[package]] 377 | name = "kakpipe" 378 | version = "0.5.7" 379 | dependencies = [ 380 | "anyhow", 381 | "argh", 382 | "async-process", 383 | "async-std", 384 | "byteorder", 385 | "daemonize", 386 | "kak", 387 | "nix", 388 | "rand", 389 | "yew-ansi", 390 | ] 391 | 392 | [[package]] 393 | name = "kv-log-macro" 394 | version = "1.0.7" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 397 | dependencies = [ 398 | "log", 399 | ] 400 | 401 | [[package]] 402 | name = "libc" 403 | version = "0.2.137" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 406 | 407 | [[package]] 408 | name = "log" 409 | version = "0.4.17" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 412 | dependencies = [ 413 | "cfg-if", 414 | "value-bag", 415 | ] 416 | 417 | [[package]] 418 | name = "memchr" 419 | version = "2.5.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 422 | 423 | [[package]] 424 | name = "memoffset" 425 | version = "0.6.5" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 428 | dependencies = [ 429 | "autocfg", 430 | ] 431 | 432 | [[package]] 433 | name = "nix" 434 | version = "0.20.2" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945" 437 | dependencies = [ 438 | "bitflags", 439 | "cc", 440 | "cfg-if", 441 | "libc", 442 | "memoffset", 443 | ] 444 | 445 | [[package]] 446 | name = "once_cell" 447 | version = "1.16.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 450 | 451 | [[package]] 452 | name = "parking" 453 | version = "2.0.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 456 | 457 | [[package]] 458 | name = "pin-project-lite" 459 | version = "0.2.9" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 462 | 463 | [[package]] 464 | name = "pin-utils" 465 | version = "0.1.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 468 | 469 | [[package]] 470 | name = "polling" 471 | version = "2.4.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" 474 | dependencies = [ 475 | "autocfg", 476 | "cfg-if", 477 | "libc", 478 | "log", 479 | "wepoll-ffi", 480 | "winapi", 481 | ] 482 | 483 | [[package]] 484 | name = "ppv-lite86" 485 | version = "0.2.16" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 488 | 489 | [[package]] 490 | name = "proc-macro2" 491 | version = "1.0.47" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 494 | dependencies = [ 495 | "unicode-ident", 496 | ] 497 | 498 | [[package]] 499 | name = "quote" 500 | version = "1.0.21" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 503 | dependencies = [ 504 | "proc-macro2", 505 | ] 506 | 507 | [[package]] 508 | name = "rand" 509 | version = "0.8.5" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 512 | dependencies = [ 513 | "libc", 514 | "rand_chacha", 515 | "rand_core", 516 | ] 517 | 518 | [[package]] 519 | name = "rand_chacha" 520 | version = "0.3.1" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 523 | dependencies = [ 524 | "ppv-lite86", 525 | "rand_core", 526 | ] 527 | 528 | [[package]] 529 | name = "rand_core" 530 | version = "0.6.4" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 533 | dependencies = [ 534 | "getrandom", 535 | ] 536 | 537 | [[package]] 538 | name = "signal-hook" 539 | version = "0.3.14" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" 542 | dependencies = [ 543 | "libc", 544 | "signal-hook-registry", 545 | ] 546 | 547 | [[package]] 548 | name = "signal-hook-registry" 549 | version = "1.4.0" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 552 | dependencies = [ 553 | "libc", 554 | ] 555 | 556 | [[package]] 557 | name = "slab" 558 | version = "0.4.7" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 561 | dependencies = [ 562 | "autocfg", 563 | ] 564 | 565 | [[package]] 566 | name = "socket2" 567 | version = "0.4.7" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 570 | dependencies = [ 571 | "libc", 572 | "winapi", 573 | ] 574 | 575 | [[package]] 576 | name = "syn" 577 | version = "1.0.103" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 580 | dependencies = [ 581 | "proc-macro2", 582 | "quote", 583 | "unicode-ident", 584 | ] 585 | 586 | [[package]] 587 | name = "unicode-ident" 588 | version = "1.0.5" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 591 | 592 | [[package]] 593 | name = "value-bag" 594 | version = "1.0.0-alpha.9" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" 597 | dependencies = [ 598 | "ctor", 599 | "version_check", 600 | ] 601 | 602 | [[package]] 603 | name = "version_check" 604 | version = "0.9.4" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 607 | 608 | [[package]] 609 | name = "waker-fn" 610 | version = "1.1.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 613 | 614 | [[package]] 615 | name = "wasi" 616 | version = "0.11.0+wasi-snapshot-preview1" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 619 | 620 | [[package]] 621 | name = "wasm-bindgen" 622 | version = "0.2.83" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 625 | dependencies = [ 626 | "cfg-if", 627 | "wasm-bindgen-macro", 628 | ] 629 | 630 | [[package]] 631 | name = "wasm-bindgen-backend" 632 | version = "0.2.83" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 635 | dependencies = [ 636 | "bumpalo", 637 | "log", 638 | "once_cell", 639 | "proc-macro2", 640 | "quote", 641 | "syn", 642 | "wasm-bindgen-shared", 643 | ] 644 | 645 | [[package]] 646 | name = "wasm-bindgen-futures" 647 | version = "0.4.33" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" 650 | dependencies = [ 651 | "cfg-if", 652 | "js-sys", 653 | "wasm-bindgen", 654 | "web-sys", 655 | ] 656 | 657 | [[package]] 658 | name = "wasm-bindgen-macro" 659 | version = "0.2.83" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 662 | dependencies = [ 663 | "quote", 664 | "wasm-bindgen-macro-support", 665 | ] 666 | 667 | [[package]] 668 | name = "wasm-bindgen-macro-support" 669 | version = "0.2.83" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 672 | dependencies = [ 673 | "proc-macro2", 674 | "quote", 675 | "syn", 676 | "wasm-bindgen-backend", 677 | "wasm-bindgen-shared", 678 | ] 679 | 680 | [[package]] 681 | name = "wasm-bindgen-shared" 682 | version = "0.2.83" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 685 | 686 | [[package]] 687 | name = "web-sys" 688 | version = "0.3.60" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" 691 | dependencies = [ 692 | "js-sys", 693 | "wasm-bindgen", 694 | ] 695 | 696 | [[package]] 697 | name = "wepoll-ffi" 698 | version = "0.1.2" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" 701 | dependencies = [ 702 | "cc", 703 | ] 704 | 705 | [[package]] 706 | name = "winapi" 707 | version = "0.3.9" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 710 | dependencies = [ 711 | "winapi-i686-pc-windows-gnu", 712 | "winapi-x86_64-pc-windows-gnu", 713 | ] 714 | 715 | [[package]] 716 | name = "winapi-i686-pc-windows-gnu" 717 | version = "0.4.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 720 | 721 | [[package]] 722 | name = "winapi-x86_64-pc-windows-gnu" 723 | version = "0.4.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 726 | 727 | [[package]] 728 | name = "yew-ansi" 729 | version = "0.1.0" 730 | source = "git+https://github.com/eburghar/yew-ansi.git#52a8be69fa514006257112b1bc3c337d1b09eecc" 731 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kakpipe" 3 | description = "`kakpipe` is a binary executable meant to be used inside kakoune editor for launching external tools inside colorful fifo buffers or displaying text with ansi colors in info boxes" 4 | version = "0.5.7" 5 | authors = ["Éric BURGHARD "] 6 | edition = "2018" 7 | 8 | [dependencies] 9 | anyhow = "1.0.40" 10 | argh = "0.1.5" 11 | async-process = "1.1.0" 12 | async-std = { version="1.9.0", features=["unstable"] } 13 | byteorder = "1.4.3" 14 | daemonize = "0.4.1" 15 | kak = { git = "https://github.com/eburghar/kak.rs.git", tag = "v0.1.2" } 16 | # kak = { path = "../kak" } 17 | nix = "0.20.0" 18 | rand = "0.8.3" 19 | yew-ansi = { git = "https://github.com/eburghar/yew-ansi.git", default-features = false } 20 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Simon Berger 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Simon Berger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kakpipe 2 | 3 | `kakpipe` is a binary executable meant to be used with the included [Kakoune](https://kakoune.org/) module 4 | `kakpipe.kak` for launching external tools inside colorful FIFO buffers, or displaying text with ANSI colors in 5 | info boxes. 6 | 7 | ![kakpipe](kakpipe.png?raw=true "colors in kakoune fifo buffer and info box") 8 | 9 | [TOC] 10 | 11 | ``` 12 | kakpipe 0.5.7 13 | 14 | Usage: kakpipe [] 15 | 16 | Utility to display text with ansi color codes inside kakoune fifo buffers or info boxes 17 | 18 | Options: 19 | --help display usage information 20 | 21 | Commands: 22 | fifo Return kakoune commands for opening a fifo buffer and 23 | initializing highlighters for ansi-codes, then detach 24 | itself, forward command output to the fifo, and serve 25 | range-specs definitions through a unix socket that can be 26 | consumed to stdout with the `range-specs` subcommand. 27 | range-specs Consume all available range-specs up to a given selection 28 | range from a given unix socket. 29 | faces Forward stdin to stdout with ansi color codes converted to 30 | kakoune face definitions 31 | ``` 32 | 33 | ## Simplify interface with external tools 34 | 35 | Defining a new command for interfacing external tool with Kakoune as described in 36 | [interfacing](https://github.com/mawww/kakoune/blob/master/doc/interfacing.asciidoc#interactive-output) is cumbersome 37 | for simple workflows, and as FIFO buffer doesn't support ANSI codes, either you have the extra work of defining a new 38 | file type and highlighting rules on top of some boilerplate code, or you have to accept to see everything in monochrome. 39 | 40 | As a result you generally end up using a shell, traveling back and forth to Kakoune just to launch a command 41 | because it's simpler. You loose at the same time the comfort of staying inside the editor for something that 42 | sometimes needs attention but few interactions. 43 | 44 | `kakpipe` tackles these difficulties and allows you to launch any external tool in colorful read-only FIFO buffer by 45 | just giving the command to launch along its arguments. 46 | 47 | ## Usage 48 | 49 | `kakpipe.kak` defines 2 Kakoune commands (one liner) built on top of `kakpipe fifo` 50 | 51 | - `:kakpipe` immediately switch to the buffer and let you see the result of the execution in real time, 52 | - `:kakpipe-bg` do the same without switching to the FIFO buffer 53 | 54 | On the status line, `[fifo]` serves as an indicator to see if the process is still running. 55 | 56 | You can quickly or fuzzily jump between the buffers, and inside a FIFO buffer created by `kakpipe` 2 commands speed 57 | up your workflows even more comparing to using a shell : 58 | 59 | - Closing the buffer with `:bd` stops `kakpipe` and the process, 60 | - `:!!` stop (if still running) and restart the same command that created the current FIFO buffer. 61 | 62 | You can now focus on : 63 | 64 | - Adding new commands and aliases on top of `:kakpipe` to launch external tools inside Kakoune even faster, 65 | - and/or adding behavior on the FIFO buffer, by defining a new type and some key mappings. 66 | 67 | You can read the section about how to integrate `kakpipe` to your module below and look at the forked 68 | [kakoune-cargo](https://gitlab.com/eburghar/kakoune-cargo) module to see how easy it is to simplify existing ones. 69 | 70 | ## Installation 71 | 72 | ### manual 73 | 74 | Install `kakpipe` somewhere within you `$PATH` 75 | 76 | ```sh 77 | cargo install --path . --root ~/.local 78 | ``` 79 | 80 | Copy `kakpipe.kak` in your autoload directory. Then enter in command prompt 81 | 82 | ``` 83 | :require-module kakpipe 84 | ``` 85 | 86 | ### with plug.kak 87 | 88 | with [plug.kak](https://github.com/andreyorst/plug.kak) 89 | 90 | ``` 91 | plug "eburghar/kakpipe" do %{ 92 | cargo install --force --path . --root ~/.local 93 | } 94 | ``` 95 | 96 | ## Examples 97 | 98 | ### Buffers 99 | 100 | `kakpipe` command arguments are forwarded to `kakpipe fifo` executable, so you should use `--` to separate 101 | arguments of the command from the executable ones in your scripts or at the command prompt. 102 | 103 | Here are all the accepted arguments by `kakpipe fifo` 104 | 105 | ``` 106 | kakpipe 0.5.7 107 | 108 | Usage: kakpipe fifo [] [-c] [-w] [-S] [-d] -s [-N ] [-n ] [-k] [-V ] [-D ] 109 | 110 | Return kakoune commands for opening a fifo buffer and initializing highlighters for ansi-codes, then detach itself, 111 | forward command output to the fifo, and serve range-specs definitions through a unix socket that can be consumed 112 | to stdout with the `range-specs` subcommand. 113 | 114 | Positional Arguments: 115 | cmd command to spawn 116 | args 117 | 118 | Options: 119 | -c, --close close current buffer before starting kakpipe (used 120 | internally by :!!) 121 | -w, --rw turns the buffer editable. by default they are readonly 122 | -S, --scroll scroll down fifo buffer as new content arrives 123 | -d, --debug stderr goes to *debug* buffer instead of fifo 124 | -s, --session kakoune session 125 | -N, --prefix fifo buffer name prefix (default is the command name) 126 | -n, --name fifo buffer name (default is prefix + temporary id) 127 | -k, --clear-env clear environment 128 | -V, --vars environment variables to set (NAME=VALUE) or to export 129 | (NAME) 130 | -D, --opts options to set in the buffer scope (NAME=VALUE) 131 | --help display usage information 132 | ``` 133 | 134 | Launch `cargo build` in a new FIFO buffer 135 | 136 | ``` 137 | :kakpipe -S -- cargo build --color=always 138 | ``` 139 | 140 | Launch `cargo build` in a new FIFO buffer in the *background* 141 | 142 | ``` 143 | :kakpipe-bg -- cargo build --color=always 144 | ``` 145 | 146 | Show a file with syntax coloring managed by [bat](https://github.com/sharkdp/bat) in a FIFO buffer named `*main.rs*` 147 | 148 | ``` 149 | :kakpipe -n main.rs -- bat -p --color=always src/main.rs 150 | ``` 151 | 152 | Show a `rustdoc` page in a buffer using [rusty-man](https://git.sr.ht/~ireas/rusty-man) 153 | 154 | ``` 155 | :kakpipe -- rusty-man --viewer rich std::string::String 156 | ``` 157 | 158 | Launch a one-liner script 159 | 160 | ``` 161 | kakpipe -S -N alive -- sh -c 'while true; do echo -e "\e[32malive !"; sleep 1; done' 162 | ``` 163 | 164 | Launch a long-running process in a new buffer with the variable `FORCE_COLOR` exported. 165 | 166 | ``` 167 | :kakpipe -S -V FORCE_COLOR=true -- npm run dev 168 | ``` 169 | 170 | Launch `lualatex` each time the current file is modified using [`entr`](http://eradman.com/entrproject/) 171 | 172 | ``` 173 | :kakpipe -S -N lualatex -- sh -c "echo '%val{buffile}' | entr -nr lualatex '%val{buffile}'" 174 | ``` 175 | 176 | Closing the buffer will stop the process. You can also use `-k` to clean up the environment in conjunction with 177 | `-V PATH` to reexport explicitly a variable. 178 | 179 | ### Info boxes 180 | 181 | For info boxes you use the `kakpipe faces` binary inside shell expansions. 182 | 183 | ``` 184 | kakpipe 0.5.7 185 | 186 | Usage: kakpipe faces 187 | 188 | Forward stdin to stdout with ansi color codes converted to kakoune face definitions 189 | 190 | Options: 191 | --help display usage information 192 | ``` 193 | 194 | Show a calendar in an info box 195 | 196 | ``` 197 | :info -markup %sh{ TERM=xterm-256color cal --color=always | kakpipe faces } 198 | ``` 199 | 200 | Show diff of current file in info box 201 | 202 | ``` 203 | :info -markup %sh{ git diff --color=always $kak_buffile | kakpipe faces } 204 | ``` 205 | 206 | ## Building new Commands 207 | 208 | Mimicking shell commands inside Kakoune are generally one-liners. 209 | 210 | ``` 211 | define-command -override -params 1.. -docstring 'launch cargo with the given parameters inside kakoune' cargo %{ 212 | kakpipe -S -- cargo --color=always %arg{@} 213 | } 214 | ``` 215 | 216 | ``` 217 | define-command -override -params 1 -docstring 'show a rustdoc page' rman %{ 218 | kakpipe -n %arg{1} -- rusty-man --viewer rich %arg{@} 219 | } 220 | ``` 221 | 222 | As well as for aliasing commands (shell like aliases) 223 | 224 | ``` 225 | define-command -params 0.. -docstring 'cargo check' cc %{ 226 | cargo check %arg{@} 227 | } 228 | ``` 229 | 230 | ``` 231 | define-command -params 0.. -docstring 'cargo build' cb %{ 232 | cargo build %arg{@} 233 | } 234 | ``` 235 | 236 | ``` 237 | define-command -docstring 'cargo install in ~/.local/bin' ci %{ 238 | cargo install --path . --root %sh{ echo -n ~/.local } 239 | } 240 | ``` 241 | 242 | ``` 243 | define-command -docstring 'cargo install current directory crate to ~/.local/bin' ci %{ 244 | cargo install --path . --root %sh{ echo -n ~/.local } 245 | } 246 | ``` 247 | 248 | ## Integrate `kakpipe` to your module 249 | 250 | You can easily add custom behavior to the FIFO buffer created by `kakpipe` by using one or several `-D name=value` 251 | command line arguments to set up options values in the FIFO buffer scope. 252 | 253 | You can for instance make a module defining custom mappings for a given file type and use `-D filetype=myfiletype` 254 | with `kakpipe` inside the plugin to automatically set up the file type of the created FIFO buffer. 255 | 256 | The `-n` options allows to use the same buffer (name) at each command invocation. By default, `kakpipe` open a new 257 | buffer which name is a '`-`' separated string made of the command name (or the prefix given with `-N`) and a 258 | random ID. The random ID is also used as a prefix for all temporary files that are generated in `/tmp/kakpipe/` 259 | (socket, FIFO and PID files). 260 | 261 | ``` 262 | define-command -override -params 1.. -docstring 'launch cargo with the given parameters inside kakoune' cargo %{ 263 | kakpipe -S -n cargo -D filetype=cargo -- cargo --color=always %arg{@} 264 | } 265 | ``` 266 | 267 | You can see [a 268 | patch](https://gitlab.com/eburghar/kakoune-cargo/-/compare/b15c75180e8c851c8687c90550746dfedceebbed...master?from_project_id=27156852&view=parallel) 269 | which shows how to use `kakpipe` as a replacement of highlighter and `mkfifo` boilerplate in the 270 | [kakoune-cargo](https://gitlab.com/Screwtapello/kakoune-cargo) plugin. 271 | 272 | ## References 273 | 274 | [kak-ansi](https://github.com/eraserhd/kak-ansi) is a tiny (23K) executable (written in C with no dependencies) 275 | exclusively targeted at highlighting ANSI codes in selections. `kak-ansi` works by removing ANSI codes from selections 276 | and adding range-specs to bring color and faces, but as a consequence can only work on read-write buffers. It writes 277 | to temporary files and adds its own (tiny) layer of boilerplate code to be used in your commands and FIFO. 278 | 279 | `kakpipe` manage process lifecycle and sends asynchronously its output to the FIFO buffer already stripped out 280 | of ANSI codes while providing range-specs from a Unix socket to be consumed separately. It works by default on 281 | read-only buffers because this is what command outputs are expected to be. 282 | -------------------------------------------------------------------------------- /kakpipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eburghar/kakpipe/7f29ee36b26cf1a6aab2ceb74a9d46f71de11f27/kakpipe.png -------------------------------------------------------------------------------- /rc/kakpipe.kak: -------------------------------------------------------------------------------- 1 | declare-option -hidden range-specs kakpipe_color_ranges 2 | declare-option -hidden str kakpipe_args "" 3 | 4 | provide-module kakpipe %{ 5 | 6 | define-command -docstring "Forwards outputs of the command given as parameter to a new fifo buffer and highlights text based on ansi color codes" kakpipe -params 1.. %{ 7 | evaluate-commands %sh{ exec kakpipe fifo -s $kak_session "$@" } 8 | } 9 | 10 | define-command -docstring "Forwards outputs of the command given as parameter to a new fifo buffer in the background and highlights text based on ansi color codes" kakpipe-bg -params 1.. %{ 11 | evaluate-commands -draft %sh{ exec kakpipe fifo -s $kak_session "$@" } 12 | } 13 | 14 | define-command -hidden -docstring "Close buffer and restart kakpipe on a kakpipe created buffer" kakpipe-restart %{ 15 | evaluate-commands %sh{ test -n "$kak_opt_kakpipe_args" && eval exec kakpipe fifo -c "$kak_opt_kakpipe_args" || echo nop } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use argh::{FromArgs, TopLevelCommand}; 2 | use std::path::Path; 3 | 4 | /// Utility to display text with ansi color codes inside kakoune fifo buffers or info boxes 5 | #[derive(FromArgs)] 6 | pub struct Args { 7 | #[argh(subcommand)] 8 | pub mode: Mode, 9 | } 10 | 11 | #[derive(FromArgs)] 12 | #[argh(subcommand)] 13 | pub enum Mode { 14 | Fifo(FifoArgs), 15 | RangeSpecs(RangeSpecsArgs), 16 | Faces(FacesArgs), 17 | } 18 | 19 | /// Split a variable definition in NAME and VALUE 20 | pub(crate) fn parse_key_val(exp: &str) -> (&str, Option<&str>) { 21 | let i = exp.find('='); 22 | if let Some(i) = i { 23 | // something after = 24 | if i + 1 < exp.len() { 25 | (&exp[..i], Some(&exp[i + 1..])) 26 | // nothing after = 27 | } else { 28 | (&exp[..i], Some("")) 29 | } 30 | } else { 31 | (exp, None) 32 | } 33 | } 34 | 35 | /// Return kakoune commands for opening a fifo buffer and initializing highlighters for ansi-codes, then detach itself, forward 36 | /// command output to the fifo, and serve range-specs definitions through a unix socket that can be consumed to stdout 37 | /// with the `range-specs` subcommand. 38 | #[derive(FromArgs)] 39 | #[argh(subcommand, name = "fifo")] 40 | pub struct FifoArgs { 41 | /// close current buffer before starting kakpipe (used internally by :!!) 42 | #[argh(switch, short = 'c')] 43 | pub close: bool, 44 | 45 | /// turns the buffer editable. by default they are readonly 46 | #[argh(switch, short = 'w')] 47 | pub rw: bool, 48 | 49 | /// scroll down fifo buffer as new content arrives 50 | #[argh(switch, short = 'S')] 51 | pub scroll: bool, 52 | 53 | /// stderr goes to *debug* buffer instead of fifo 54 | #[argh(switch, short = 'd')] 55 | pub debug: bool, 56 | 57 | /// kakoune session 58 | #[argh(option, short = 's')] 59 | pub session: String, 60 | 61 | /// fifo buffer name prefix (default is the command name) 62 | #[argh(option, short = 'N')] 63 | pub prefix: Option, 64 | 65 | /// fifo buffer name (default is prefix + temporary id) 66 | #[argh(option, short = 'n')] 67 | pub name: Option, 68 | 69 | /// clear environment 70 | #[argh(switch, short = 'k')] 71 | pub clear_env: bool, 72 | 73 | /// environment variables to set (NAME=VALUE) or to export (NAME) 74 | #[argh(option, short = 'V')] 75 | pub vars: Vec, 76 | 77 | /// options to set in the buffer scope (NAME=VALUE) 78 | #[argh(option, short = 'D')] 79 | pub opts: Vec, 80 | 81 | /// command to spawn 82 | #[argh(positional)] 83 | pub cmd: String, 84 | 85 | // arguments of command 86 | #[argh(positional)] 87 | pub args: Vec, 88 | } 89 | 90 | #[derive(FromArgs)] 91 | #[argh(subcommand, name = "range-specs")] 92 | /// Consume all available range-specs up to a given selection range from a given unix socket. 93 | pub struct RangeSpecsArgs { 94 | /// socket path to get range-specs from 95 | #[argh(positional)] 96 | pub socket: String, 97 | 98 | /// get range-specs up to range or all available range-specs by default 99 | #[argh(positional, default = "\"0.0,0.0\".to_owned()")] 100 | pub range: String, 101 | } 102 | 103 | #[derive(FromArgs)] 104 | #[argh(subcommand, name = "faces")] 105 | /// Forward stdin to stdout with ansi color codes converted to kakoune face definitions 106 | pub struct FacesArgs {} 107 | 108 | fn cmd<'a>(default: &'a str, path: &'a str) -> &'a str { 109 | Path::new(path) 110 | .file_name() 111 | .map(|s| s.to_str()) 112 | .flatten() 113 | .unwrap_or(default) 114 | } 115 | 116 | /// copy of argh::from_env to insert command name and version 117 | pub fn from_env() -> T { 118 | let strings: Vec = std::env::args().collect(); 119 | let cmd = cmd(&strings[0], &strings[0]); 120 | let strs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect(); 121 | T::from_args(&[cmd], &strs[1..]).unwrap_or_else(|early_exit| { 122 | println!("{} {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); 123 | println!("{}", early_exit.output); 124 | std::process::exit(match early_exit.status { 125 | Ok(()) => 0, 126 | Err(()) => 1, 127 | }) 128 | }) 129 | } 130 | -------------------------------------------------------------------------------- /src/cmd/faces.rs: -------------------------------------------------------------------------------- 1 | use kak::{escape::Mode, face}; 2 | 3 | use anyhow::Result; 4 | use async_std::io; 5 | 6 | /// Forward stdin to stdout after converting all ansi color code to kakoune face definition 7 | pub async fn faces() -> Result<()> { 8 | let stdin = io::stdin(); 9 | let mut line = String::new(); 10 | while let Ok(size) = stdin.read_line(&mut line).await { 11 | if size == 0 { 12 | break; 13 | } 14 | face::print(&line, Mode::Brace); 15 | line.clear(); 16 | } 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /src/cmd/fifo.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context, Result}; 2 | use async_process::{Command, Stdio}; 3 | use async_std::{ 4 | fs::{File, OpenOptions}, 5 | io::{ 6 | prelude::{BufReadExt, ReadExt, WriteExt}, 7 | BufReader, 8 | }, 9 | os::unix::net::UnixListener, 10 | prelude::FutureExt, 11 | stream::StreamExt, 12 | sync::Mutex, 13 | }; 14 | use kak::{ 15 | command::Client, 16 | range::{Pos, Range, Selection}, 17 | }; 18 | use std::{convert::TryFrom, env, path::Path, sync::Arc}; 19 | use yew_ansi::get_sgr_segments; 20 | 21 | use crate::{ 22 | args::{self, FifoArgs}, 23 | range_specs::SharedRanges, 24 | }; 25 | 26 | /// Serve all accumulated range_specs definition to stdout through a unix socket 27 | pub async fn range_specs(socket: &Path, sync: Arc>) -> Result<()> { 28 | let listener = UnixListener::bind(&socket) 29 | .await 30 | .context("error listening to range_specs socket")?; 31 | let mut incoming = listener.incoming(); 32 | let mut response = String::new(); 33 | 34 | while let Some(stream) = incoming.next().await { 35 | let mut stream = stream?; 36 | 37 | // wait for the range to be sent by client 38 | let mut len = [0u8; 1]; 39 | stream.read_exact(&mut len).await?; 40 | let mut range = vec![0u8; len[0] as usize]; 41 | stream.read_exact(&mut range).await?; 42 | 43 | // this is the selection kakoune wants the ranges for 44 | let selection = String::from_utf8_lossy(&range).parse::()?; 45 | // eprintln!("selection: {}", &selection); 46 | 47 | // reuse the same string between connexion 48 | response.clear(); 49 | let fifo_end; 50 | let mut empty_ranges; 51 | // release the lock after this block 52 | { 53 | let mut sync = sync.lock().await; 54 | // get fifo state and ranges emptyness 55 | fifo_end = sync.fifo_end; 56 | empty_ranges = sync.ranges.is_empty(); 57 | if !empty_ranges { 58 | // return all accumulated ranges lower or equal to selection 59 | let mut i = 0; 60 | for range in sync.ranges.iter() { 61 | // don't serve ranges for lines that kakoune didn't display yet 62 | if selection.is_valid() && range.selection.1 > selection.1 { 63 | break; 64 | } 65 | // separate ranges by space 66 | if i != 0 { 67 | response.push(' '); 68 | } 69 | response.push_str(&format!("{}", range)); 70 | i += 1; 71 | } 72 | // remove ranges already sent (for_each consumes the iterator and the drained items) 73 | sync.ranges.drain(0..i).for_each(|_| ()); 74 | // reevaluate ranges emptyness to stop the loop if fifo has been closed 75 | empty_ranges = sync.ranges.is_empty(); 76 | } 77 | } 78 | // eprintln!("response: {}", &response); 79 | stream.write_all(response.as_bytes()).await?; 80 | if fifo_end && empty_ranges { 81 | break; 82 | } 83 | } 84 | 85 | // at this point all ranges have been consumed and fifo has been closed 86 | Ok(()) 87 | } 88 | 89 | /// Forward stdin to stdout after removing all ansi color codes. Range_specs are written in a data structure 90 | /// shared between tasks 91 | pub async fn stdin_fifo( 92 | args: &FifoArgs, 93 | fifo: &Path, 94 | pid: &Path, 95 | client: &mut Client, 96 | sync: Arc>, 97 | ) -> Result<()> { 98 | // set environment 99 | let envs = args 100 | .vars 101 | .iter() 102 | .filter_map(|s| match args::parse_key_val(s) { 103 | (name, Some(value)) => Some((name, value.to_owned())), 104 | (name, None) => env::var(name).ok().map(|value| (name, value)), 105 | }); 106 | 107 | let mut fifo_file = OpenOptions::new().write(true).open(fifo).await?; 108 | 109 | // async read from command and async write to fifo 110 | let mut cmd = Command::new(&args.cmd); 111 | if args.clear_env { 112 | cmd.env_clear(); 113 | } 114 | let child = cmd 115 | .envs(envs) 116 | .args(&args.args) 117 | .stderr(Stdio::piped()) 118 | .stdout(Stdio::piped()) 119 | .current_dir(env::current_dir().unwrap()) 120 | .spawn(); 121 | 122 | // write error message to fifo in case spawn failed 123 | if let Err(e) = child { 124 | fifo_file 125 | .write_all(format!("error running {}: {}", &args.cmd, e).as_bytes()) 126 | .await?; 127 | fifo_file.flush().await?; 128 | return Err(e.into()); 129 | } 130 | 131 | // unwrap is safe at it this point because of the return above 132 | let mut child = child.unwrap(); 133 | 134 | // write the pid of the spawn process to a file then dispose it 135 | { 136 | let mut pid_file = File::create(pid).await?; 137 | pid_file 138 | .write_all(child.id().to_string().as_bytes()) 139 | .await?; 140 | } 141 | 142 | let mut stdout_reader = BufReader::new(child.stdout.take().unwrap()).lines(); 143 | let mut stderr_reader = BufReader::new(child.stderr.take().unwrap()).lines(); 144 | let mut l = 1; // line number 145 | let mut start = 1; // column 146 | if args.debug { 147 | // stdout goes to fifo 148 | let fifo_task = async { 149 | // TODO: how to deal with ansi-codes that spans several lines ? 150 | while let Some(line) = stdout_reader.next().await { 151 | let line = line?; 152 | for (effect, txt) in get_sgr_segments(&line) { 153 | let len = u32::try_from(txt.len()).unwrap(); 154 | let end = if len > 1 { 155 | start + len - 1 156 | } else { 157 | start + len 158 | }; 159 | if let Some(range) = Range::new(Selection(Pos(l, start), Pos(l, end)), effect) { 160 | let mut sync = sync.lock().await; 161 | sync.ranges.push_back(range); 162 | } 163 | let _ = fifo_file.write_all(txt.as_bytes()).await; 164 | start = if len > 1 { end + 1 } else { end }; 165 | } 166 | let _ = fifo_file.write_all(b"\n").await; 167 | let _ = fifo_file.flush().await; 168 | l += 1; 169 | start = 1; 170 | } 171 | Ok::<(), anyhow::Error>(()) 172 | }; 173 | 174 | // stderr goes to debug buffer 175 | let debug_task = async { 176 | while let Some(line) = stderr_reader.next().await { 177 | let line = line?; 178 | // debug buffer doesn't support markup otherwise we would have inserted faces 179 | // to get colored debug outputs 180 | client 181 | .send_command(&format!("echo -debug {}", line)) 182 | .await?; 183 | } 184 | Ok::<(), anyhow::Error>(()) 185 | }; 186 | 187 | // wait for stdout and stderr to complete. stop all if one fails 188 | fifo_task.try_join(debug_task).await?; 189 | } else { 190 | // TODO: deduplicate code with generics ? 191 | // TODO: we probably lose one line if both stdout and stderr completes at the same time 192 | // reads from stdout and stderr simultaneously 193 | while let Some(line) = stdout_reader.next().race(stderr_reader.next()).await { 194 | let line = line?; 195 | for (effect, txt) in get_sgr_segments(&line) { 196 | let len = u32::try_from(txt.len()).unwrap(); 197 | let end = if len > 1 { 198 | start + len - 1 199 | } else { 200 | start + len 201 | }; 202 | if let Some(range) = Range::new(Selection(Pos(l, start), Pos(l, end)), effect) { 203 | let mut sync = sync.lock().await; 204 | sync.ranges.push_back(range); 205 | } 206 | let _ = fifo_file.write_all(txt.as_bytes()).await; 207 | start = if len > 1 { end + 1 } else { end }; 208 | } 209 | let _ = fifo_file.write_all(b"\n").await; 210 | let _ = fifo_file.flush().await; 211 | l += 1; 212 | start = 1; 213 | // println!("{}", &line); 214 | } 215 | } 216 | 217 | // signal the range_specs task that we have processed all output 218 | { 219 | let _ = fifo_file.sync_all().await; 220 | let mut sync = sync.lock().await; 221 | sync.fifo_end = true; 222 | } 223 | 224 | // ok if some lines have been written 225 | if l >= 1 { 226 | Ok(()) 227 | } else { 228 | // return an error to stop all tasks as there is nothing more to do 229 | Err(anyhow!("no output")) 230 | } 231 | } 232 | 233 | /// Print kakoune initialization command for displaying the corresponding fifo buffer then 234 | /// combine stdin_fifo and range_specs 235 | pub async fn fifo(args: FifoArgs, fifo: &Path, pid: &Path, socket: &Path) -> Result<()> { 236 | // client connection to kakoune session 237 | let mut client = Client::new(&args.session)?; 238 | if args.debug { 239 | client 240 | .send_command(&format!( 241 | "echo -debug +++ start {} {:?}", 242 | &args.cmd, &args.args 243 | )) 244 | .await?; 245 | } 246 | // unless we use a double queue with an actor model and an atomic operation for switching 247 | // queues after adding or removing ranges, mutex seems to be unavoidable, because we can 248 | // produce and consume ranges at the same time and both task needs write access. 249 | let sync = Arc::new(Mutex::new(SharedRanges::new())); 250 | let task_stdin_fifo = stdin_fifo(&args, fifo, pid, &mut client, Arc::clone(&sync)); 251 | let task_ranges_specs = range_specs(socket, Arc::clone(&sync)); 252 | // wait for all tasks to complete. stops as soon as one future fails 253 | task_stdin_fifo.try_join(task_ranges_specs).await?; 254 | 255 | if args.debug { 256 | client 257 | .send_command(&format!( 258 | "echo -debug +++ end {} {:?}", 259 | &args.cmd, &args.args 260 | )) 261 | .await?; 262 | } 263 | Ok(()) 264 | } 265 | -------------------------------------------------------------------------------- /src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod faces; 2 | pub mod fifo; 3 | pub mod range_specs; 4 | -------------------------------------------------------------------------------- /src/cmd/range_specs.rs: -------------------------------------------------------------------------------- 1 | use crate::args::RangeSpecsArgs; 2 | 3 | use anyhow::{Context, Result}; 4 | use async_std::{ 5 | io::prelude::{ReadExt, WriteExt}, 6 | os::unix::net::UnixStream, 7 | }; 8 | use std::convert::TryFrom; 9 | 10 | /// Connects to the given unix socket, reads the available range_specs up to range given 11 | /// as parameter and return the kakoune command for setting the ranges in the buffer 12 | pub async fn range_specs(args: RangeSpecsArgs) -> Result<()> { 13 | let mut stream = UnixStream::connect(args.socket) 14 | .await 15 | .context("Couldn't connect to kakpipe socket")?; 16 | 17 | // send the range of the text to highlight to the server as a string i1.j1,i2.j2 18 | let len = args.range.len(); 19 | let mut buffer = Vec::::with_capacity(len + 1); 20 | buffer.push(u8::try_from(len).unwrap()); 21 | buffer.extend(args.range.as_bytes()); 22 | stream.write_all(&buffer).await?; 23 | 24 | // wait for the response 25 | let mut response = String::new(); 26 | let size = stream.read_to_string(&mut response).await?; 27 | 28 | if size != 0 { 29 | println!( 30 | "update-option buffer kakpipe_color_ranges\n\ 31 | set-option -add buffer kakpipe_color_ranges {}", 32 | response 33 | ); 34 | } else { 35 | println!("nop"); 36 | } 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod args; 2 | mod cmd; 3 | mod mktemp; 4 | mod range_specs; 5 | 6 | use anyhow::{bail, Result}; 7 | use async_std::task::block_on; 8 | use daemonize::Daemonize; 9 | use nix::{sys::stat, unistd}; 10 | use std::{env, fs}; 11 | 12 | use crate::{ 13 | args::{Args, Mode}, 14 | cmd::{faces::faces, fifo::fifo, range_specs::range_specs}, 15 | mktemp::{temp_dir, temp_file, temp_id}, 16 | }; 17 | 18 | fn main() -> Result<()> { 19 | let args: Args = args::from_env(); 20 | match args.mode { 21 | Mode::Fifo(args) => { 22 | // check -D arguments are well formed 23 | for o in args.opts.iter() { 24 | if let (_, None) = args::parse_key_val(o) { 25 | bail!("invalid KEY=value: no `=` found in `{}`", o); 26 | }; 27 | } 28 | 29 | // create random fifo, socket and pid files 30 | let base = temp_id(10); 31 | let tmp_dir = temp_dir("kakpipe")?; 32 | let fifo_path = temp_file(&tmp_dir, &base, "fifo")?; 33 | let socket_path = temp_file(&tmp_dir, &base, "sock")?; 34 | let pipe_pid_path = temp_file(&tmp_dir, &base, "pid1")?; 35 | let daemon_pid_path = temp_file(&tmp_dir, &base, "pid2")?; 36 | 37 | // Create the unix fifo 38 | unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU)?; 39 | 40 | // set buffer name 41 | let buffer_name = if let Some(name) = &args.name { 42 | name.to_owned() 43 | } else { 44 | // use the given prefix is any 45 | let mut res = if let Some(prefix) = &args.prefix { 46 | prefix.to_owned() 47 | // or use command instead 48 | } else if let Some(pos) = args.cmd.rfind('/') { 49 | if pos + 1 < args.cmd.len() { 50 | args.cmd[pos + 1..].to_owned() 51 | } else { 52 | args.cmd.clone() 53 | } 54 | } else { 55 | args.cmd.clone() 56 | }; 57 | // append the base 58 | res.push('-'); 59 | res.push_str(&base); 60 | res.replace("{", "").replace("}", "") 61 | }; 62 | 63 | // collect arguments after 'kakpipe fifo' 64 | let cmd_args = env::args().skip(2).fold(String::new(), |mut a, ref b| { 65 | a.push(' '); 66 | a.push_str(b); 67 | a 68 | }); 69 | 70 | // write kakoune initialization commands to stdout 71 | println!( 72 | "{close_buffer}\ 73 | hook -once global BufOpenFifo %{{\\*{buffer_name}\\*}} %{{ set-option buffer kakpipe_args %{{{cmd_args}}}\n alias buffer !! kakpipe-restart }}\n\ 74 | edit! -fifo {fifo_path}{scroll}{readonly} %{{*{buffer_name}*}}\n\ 75 | add-highlighter -override buffer/kakpipe ranges kakpipe_color_ranges\n\ 76 | hook -once buffer BufClose %{{\\*{buffer_name}\\*}} %{{ nop %sh{{\n 77 | test -f {pipe_pid_path} && pid=$(cat {pipe_pid_path}) && rm -f {pipe_pid_path} && test -n $pid && kill $pid >/dev/null 2>&1\n 78 | test -f {daemon_pid_path} && pid=$(cat {daemon_pid_path}) && rm -f {daemon_pid_path} && test -n $pid && kill $pid >/dev/null 2>&1\n 79 | test -p {fifo_path} && rm -f {fifo_path}\n 80 | test -S {socket_path} && rm -f {socket_path}\n 81 | }} }}\n\ 82 | try %{{ remove-hooks buffer kakpipe }}\n\ 83 | hook -group kakpipe buffer BufReadFifo .* %{{ evaluate-commands %sh{{ test -S {socket_path} && kakpipe range-specs {socket_path} $kak_hook_param }} }}", 84 | close_buffer= if args.close {"delete-buffer\n"} else { ""}, 85 | fifo_path=fifo_path.to_str().unwrap(), 86 | socket_path=socket_path.to_str().unwrap(), 87 | pipe_pid_path=pipe_pid_path.to_str().unwrap(), 88 | daemon_pid_path=daemon_pid_path.to_str().unwrap(), 89 | buffer_name=&buffer_name, 90 | cmd_args=&cmd_args, 91 | readonly=if args.rw { "" } else { " -readonly"}, 92 | scroll=if args.scroll { " -scroll" } else { "" }, 93 | ); 94 | // set buffer options 95 | args.opts 96 | .iter() 97 | .filter_map(|s| match args::parse_key_val(s) { 98 | (name, Some(value)) => Some((name, value)), 99 | _ => None, 100 | }) 101 | .for_each(|(name, value)| println!("set-option buffer {} {}", name, value)); 102 | 103 | // let stdout = fs::File::create("/tmp/daemon.out").unwrap(); 104 | // let stderr = fs::File::create("/tmp/daemon.err").unwrap(); 105 | let daemon = Daemonize::new() 106 | // .stdout(stdout) 107 | // .stderr(stderr) 108 | .pid_file(&daemon_pid_path) 109 | .working_directory(env::current_dir().unwrap()); 110 | // Detach 111 | daemon.start()?; 112 | // Concurrently run command, output stdout and stderr to fifo and serve ranges 113 | let res = block_on(fifo(args, &fifo_path, &pipe_pid_path, &socket_path)); 114 | 115 | // at this point fifo is closed. remove it 116 | let _ = fs::remove_file(fifo_path); 117 | // remove silently temp files 118 | let _ = fs::remove_file(socket_path); 119 | let _ = fs::remove_file(pipe_pid_path); 120 | let _ = fs::remove_file(daemon_pid_path); 121 | 122 | res? 123 | } 124 | Mode::RangeSpecs(args) => block_on(range_specs(args))?, 125 | Mode::Faces(_) => block_on(faces())?, 126 | }; 127 | Ok(()) 128 | } 129 | -------------------------------------------------------------------------------- /src/mktemp.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | use rand::{ 3 | distributions::Alphanumeric, 4 | {thread_rng, Rng}, 5 | }; 6 | use std::{ 7 | env, 8 | fs::create_dir_all, 9 | path::{Path, PathBuf}, 10 | }; 11 | 12 | /// return random 10 chars string 13 | pub fn temp_id(len: usize) -> String { 14 | thread_rng() 15 | .sample_iter(&Alphanumeric) 16 | .take(len) 17 | .map(char::from) 18 | .collect() 19 | } 20 | 21 | /// return path buf from a path, name and an extension 22 | pub fn temp_file(path: &Path, name: &str, ext: &str) -> Result { 23 | let mut res = path.join(name); 24 | if res.set_extension(ext) { 25 | Ok(res) 26 | } else { 27 | bail!("Failed to create {:?}/{}.{}", path, name, ext) 28 | } 29 | } 30 | 31 | pub fn temp_dir(basename: &str) -> Result { 32 | let tmp_dir = env::temp_dir().join(basename); 33 | create_dir_all(&tmp_dir)?; 34 | Ok(tmp_dir) 35 | } 36 | -------------------------------------------------------------------------------- /src/range_specs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | use kak::range::Range; 4 | 5 | /// Shared data between `stdin_fifo` which read stdout and stderr from 6 | /// the spawned command and extract ranges from ansi-code and `range_specs` 7 | /// which consumes the ranges 8 | pub struct SharedRanges { 9 | // a fifo queue of ranges 10 | pub ranges: VecDeque, 11 | // marker to signal `range_specs` that fifo has been closed 12 | pub fifo_end: bool, 13 | } 14 | 15 | impl SharedRanges { 16 | pub fn new() -> Self { 17 | Self { 18 | ranges: VecDeque::new(), 19 | fifo_end: false, 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------