├── .gitignore ├── BUGS.md ├── LICENSE.md ├── Makefile ├── README.md ├── See.c ├── TODO.md ├── acme.hook ├── bin ├── + ├── ++ ├── +++ ├── - ├── -- ├── --- ├── Acme ├── Auto ├── Blame ├── Checkout ├── Clear ├── Delall ├── Do ├── Exec ├── Getall ├── Getfn ├── Getids ├── Godoc ├── Goimports ├── Hook ├── Iswin ├── Mv ├── Open ├── Rdoc ├── Read ├── Rename ├── Rm ├── Run ├── Switch ├── To ├── Unhook ├── Write ├── XDel ├── XDump ├── XLoad ├── XPut ├── XPutall └── doGoimports └── update-doc /.gitignore: -------------------------------------------------------------------------------- 1 | bin/See 2 | *.o 3 | -------------------------------------------------------------------------------- /BUGS.md: -------------------------------------------------------------------------------- 1 | ``bin/Autos`` and the hooks mechanism (``bin/Hook``, 2 | ``bin/Unhook``) both crashes acme reproducibly enough 3 | here. 4 | 5 | I haven't investigated further; for the record, it crashed 6 | upon executing ``Unhook`` in a ``win(1)`` buffer, and some 7 | crashes seems to be related to the autodump mechanism 8 | in ``bin/Acme`` (which calls ``XDump``, which itself calls 9 | ``Hook/Unhook``). 10 | 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2021 M. Bivert 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Default installation directory. 2 | # make install dir=$HOME/bin 3 | dir ?= /bin/ 4 | root ?= root 5 | group ?= root 6 | 7 | # XXX the hook is conceptually 8 | # on a per-user basis 9 | user ?= mb 10 | hdir ?= /home/${user}/ 11 | 12 | .PHONY: all 13 | all: bin/See 14 | 15 | .PHONY: help 16 | help: 17 | @echo 'install dir=... group=... root=...' 18 | @echo ' install bin/* to $dir (default /bin/);' 19 | @echo ' default owner:group is root:root' 20 | @echo 'uninstall dir=...' 21 | @echo ' uninstall bin/* from $dir (default: /bin/)' 22 | @echo 'update-doc: update README.md from inline bin/* doc' 23 | 24 | bin/See: See.c 25 | @echo Compiling See... 26 | @9c See.c && 9l See.o -o bin/See && rm See.o 27 | 28 | .PHONY: install 29 | install: bin/See install-hook 30 | @echo "Installing bin/* to ${dir}/..." 31 | @for x in bin/*; do \ 32 | install -o ${root} -g ${group} -m 755 $$x ${dir}/`basename $$x`; \ 33 | done 34 | @# Convenient shortcut 35 | @rm -f ${dir}/O 36 | @ln -s ${dir}/Open ${dir}/O 37 | 38 | install-hook: acme.hook 39 | @echo "Installing hook to ${hdir}..." 40 | @install -m 755 ./acme.hook ${hdir}/acme.hook 41 | 42 | .PHONY: uninstall 43 | uninstall: 44 | @echo "Removing all bin/* from ${dir}/..." 45 | @for x in bin/*; do echo rm -f ${dir}/`basename $$x`; done 46 | @rm -f ${dir}/O 47 | 48 | .PHONY: update-doc 49 | update-doc: 50 | @echo 'Updating README.md to include inline documentation...' 51 | @sh ./update-doc '^# Tools' ./README.md ./bin/ 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | We provide some tools thereafter described, aiming at reducing the amount of windows, 3 | and ease to some extent Acme's scriptability, pushing 4 | [lib/acme.rc](https://github.com/9fans/plan9port/blob/master/lib/acme.rc) a bit further. 5 | 6 | ## Run 7 | This is perhaps the most useful, at least to me. Most others tools were developed 8 | mainly in order to create [``Run``][gh-mb-at-run]. It aims at implementing a typical 9 | "build/run" feature, more precisely, it: 10 | 11 | - Puts (save) all opened files (see [``XPutall``][gh-mb-at-xputall]); 12 | - Opens if necessary an output buffer/window (by default, it 13 | targets the ``+Buffer`` window, described in the 14 | [next subsection](https://github.com/mbivert/acme-tools#buffer-window)), 15 | and clearing its content (see [``Clear``][gh-mb-at-clear]) if necessary 16 | - Executes (see [``Exec``][gh-mb-at-exec], [``To``][gh-mb-at-to]) the 17 | program related to the current buffer (there's an ad-hoc 18 | long list of ``if/else`` in [``Run``][gh-mb-at-run] to determine 19 | what to do); 20 | - Redirecting stderr/stdout to the output buffer along the way, 21 | eventually tweaking stderr's output by piping it to a program 22 | (this for instance allows to rewrite some error message to 23 | be more [acme(1)](http://man.cat-v.org/plan_9/1/acme)/[plumber(4)](http://man.cat-v.org/plan_9/4/plumber) compliant). 24 | 25 | Of course, CLI arguments can be provided to the program to be executed, 26 | for instance: ``Run -- -o foo -x bar baz`` will feed the program 27 | ``-o foo -x bar baz`` as CLI arguments. 28 | 29 | A convenient ``-m `` option will execute ``make `` in 30 | the first directory containing a ``Makefile``, starting from the current 31 | directory, and going up in the tree until it reaches ``/``. For instance, when: 32 | 33 | - middle clicking ``Run -m tests`` on the tagline of a 34 | file ``/home/user/gits/project/dir/subdir/foo.go``; 35 | - assuming a ``/home/user/gits/project/Makefile`` exists; 36 | - will run ``make tests`` in ``/home/user/gits/project/`` (of course, 37 | surrounded by all previous boilerplate: saving files, opening output 38 | buffer, etc.) 39 | 40 | [gh-mb-at-run]: https://github.com/mbivert/acme-tools/blob/master/bin/Run 41 | [gh-mb-at-clear]: https://github.com/mbivert/acme-tools/blob/master/bin/Clear 42 | [gh-mb-at-exec]: https://github.com/mbivert/acme-tools/blob/master/bin/Exec 43 | [gh-mb-at-xputall]: https://github.com/mbivert/acme-tools/blob/master/bin/XPutall 44 | [gh-mb-at-to]: https://github.com/mbivert/acme-tools/blob/master/bin/To 45 | 46 | ## "+Buffer" window 47 | In practice, I've found that having a single *+Errors* window, with automatic 48 | content flushing, is more suitable than spanning one *+Errors* window per directory, 49 | with no flushing. 50 | 51 | *NOTE*: acme's default seem however most reasonable: better be verbose, 52 | and let the user filter as needed. 53 | 54 | By convention, we called this window the *+Buffer* window, being the 55 | only window whose name suffixed by *+Buffer*. Its access is automatically 56 | managed via the *Getids* command, described thereafter. 57 | 58 | This window's tagline can be used for running global commands, 59 | such as the session-related ones, also described later on. 60 | 61 | ## Getids 62 | *Getids* is a key script, on which most other scripts are built upon. 63 | It allows to easily target: 64 | 65 | - the *+Buffer* window (creating it when necessary); 66 | - multiple windows (X//-like looping); 67 | - current window (the default). 68 | 69 | It outputs a list of window IDs, one per line, from either: 70 | 71 | - a number (window ID); 72 | - an awk(1) regular expression; 73 | - **-a** for **a**utomatically targeting the *+Buffer* window; 74 | - nothing (current window). 75 | 76 | As an example, a script like *Do* that runs a command from a window's 77 | tagline, thus allowing to run/chain acme(1) commands, has an optional 78 | argument that is forwarded to *Getids*: 79 | 80 | # Clean current window 81 | (tagline)$ Do 'Edit ,d' 82 | 83 | # Clean the content of all +Errors windows 84 | (sh|tagline)$ Do 'Edit ,d' '\+Errors$' 85 | 86 | # Write is used to store data in special buffer's files (9P), 87 | # similar to lib/acme.rc's winwrite(). 88 | # 89 | # The following writes and closes all windows whose name contain 90 | # "/project42/", cleaning the +Buffer window's content thereafter, 91 | # creating it if  necessary. 92 | (sh|tagline)$ Write ctl put '\/project42\/' && \ 93 | Write ctl delete '\/project42\/' && \ 94 | Do 'Edit ,d' -a 95 | 96 | ## XDump, XLoad, Switch (sessions) 97 | Again aiming at reducing windows proliferation, those three 98 | small scripts load/dump current windows' states: 99 | 100 | # Put all files and creates a dump file in 101 | # $HOME/acme.dumps/project42.dump with current state 102 | (sh|tagline)$ XPutall && XDump project42 # optional .dump extension 103 | 104 | # Restore state from $HOME/acme.dumps/projects42.dump, 105 | # closing all opened windows beforehand 106 | (sh|tagline)$ XLoad project42.dump # optional .dump extension 107 | 108 | # Dump current state to $HOME/acme.dumps/project42.dump and 109 | # load session $HOME/acme.dumps/website.dump 110 | (sh|tagline)$ Switch project42 website # optional .dump extension 111 | 112 | # Tools (do not edit, automatically generated cf. Makefile) 113 | ## + 114 | 115 | NAME 116 | + 117 | 118 | SYNOPSYS 119 | + [-h] 120 | 121 | DESCRIPTION 122 | + indents text from stdin by prepending a tab to 123 | every line; resulting output on stdout. 124 | 125 | ## ++ 126 | 127 | NAME 128 | ++ 129 | 130 | SYNOPSYS 131 | ++ [-h] 132 | 133 | DESCRIPTION 134 | ++ indents text from stdin by prepending 2 tabs to 135 | every line; resulting output on stdout. 136 | 137 | ## +++ 138 | 139 | NAME 140 | +++ 141 | 142 | SYNOPSYS 143 | +++ [-h] 144 | 145 | DESCRIPTION 146 | +++ indents text from stdin by prepending 3 tabs to 147 | every line; resulting output on stdout. 148 | 149 | ## - 150 | 151 | NAME 152 | - 153 | 154 | SYNOPSYS 155 | - [-h] 156 | 157 | DESCRIPTION 158 | - unindents text from stdin by removing a heading tab from 159 | every line; resulting output on stdout. 160 | 161 | ## -- 162 | 163 | NAME 164 | -- 165 | 166 | SYNOPSYS 167 | -- [-h] 168 | 169 | DESCRIPTION 170 | -- unindents text from stdin by removing 2 heading tabs from 171 | every line; resulting output on stdout. 172 | 173 | ## --- 174 | 175 | NAME 176 | --- 177 | 178 | SYNOPSYS 179 | --- [-h] 180 | 181 | DESCRIPTION 182 | --- unindents text from stdin by removing 3 heading tab from 183 | every line; resulting output on stdout. 184 | 185 | ## Acme 186 | 187 | NAME 188 | Acme 189 | 190 | SYNOPSYS 191 | Acme [-h] 192 | Acme [args...] 193 | 194 | DESCRIPTION 195 | Acme is a basic wrapper to launch acme(1). It starts 196 | the plumber if needed, set a few convenient environment 197 | variable, and will periodically create a 198 | /home/mb/acme.dumps/autodump.dump using "XDump autodump". 199 | 200 | ## Auto 201 | 202 | NAME 203 | Auto 204 | 205 | SYNOPSYS 206 | Auto [-h] 207 | Auto [-k] [cmd] 208 | Auto [-n seconds] [-r] [cmd] 209 | 210 | DESCRIPTION 211 | Auto will periodically run the given command from the tagline 212 | where Auto was started. 213 | 214 | -n allows to specify a running period (default: 10 seconds) 215 | -k will kill either all automatic jobs associated with the concerned 216 | window, or all the ones associated with the given command. 217 | -r will first kill all automatic jobs associated with the given 218 | command before (re)starting it 219 | 220 | Unless for -k, where we look for all commands, cmd defaults 221 | to "Run". 222 | 223 | EXAMPLES 224 | Running 'Auto -n 5 -r "Run -- -k"' from a window will: 225 | 226 | - kill all "Run -- -k" jobs associated to $winid; 227 | - run "Run -k" from the tagline every 5 seconds. 228 | 229 | Running 'Auto' from a window will: 230 | 231 | - Execute 'Run' from that tagline every 10 seconds. 232 | 233 | ## Blame 234 | 235 | NAME 236 | Blame 237 | 238 | SYNOPSYS 239 | Blame [-h] 240 | Blame 241 | 242 | DESCRIPTION 243 | Blame is intended to be run only from the tagline. It'll put 244 | the current buffer, and swap its content for the output 245 | of 'git blame $%', where $% is the current buffer's filename. 246 | 247 | If the current buffer already contains the output of a 248 | 'git blame', Blame will remove the blaming data. 249 | 250 | ## Checkout 251 | 252 | NAME 253 | Checkout 254 | 255 | SYNOPSYS 256 | Checkout [-h] 257 | Checkout 258 | 259 | DESCRIPTION 260 | Checkout git-checkout(1)s the file associated to the current buffer, 261 | and reload the file from disk. 262 | 263 | ## Clear 264 | 265 | NAME 266 | Clear 267 | 268 | SYNOPSYS 269 | Clear [-h] 270 | Clear [-a|id|pattern] 271 | 272 | DESCRIPTION 273 | Clear removes all content from the windows pointed by -a|id|pattern, 274 | which is forwarded to Getids, thus defaulting to $winid (current 275 | window). 276 | 277 | ## Delall 278 | 279 | NAME 280 | Delall 281 | 282 | SYNOPSYS 283 | Delall [-h] 284 | Delall 285 | 286 | DESCRIPTION 287 | Delall deletes all existing windows. 288 | 289 | ## Do 290 | 291 | NAME 292 | Do 293 | 294 | SYNOPSYS 295 | Do [-h] 296 | Do [-a|id|pattern [-k]] 297 | 298 | DESCRIPTION 299 | Do runs a command in the taglines of the windows pointed by 300 | [-a|id|pattern], which is forwarded to Getids, thus defaulting to 301 | $winid (current window). 302 | 303 | Similar to https://github.com/mkhl/cmd/blob/master/acme/acmeeval/main.go, 304 | but written in sh(1), orthogonal with Getids's behavior (+Buffer 305 | management, and X// like looping on filenames patterns. 306 | 307 | If -a is augmented with a -k, we will keep the original directory 308 | of the +Buffer window. Use case is the autodump in the Acme 309 | script, which systematically reset the directory, and thus messes 310 | up regularly e.g. the "clickable links" of the compiler output. 311 | 312 | EXAMPLES 313 | $ Do 'Edit ,d' -a 314 | Clear '+Buffer' window's body 315 | 316 | 317 | ## doGoimports 318 | 319 | NAME 320 | doGoimports 321 | 322 | SYNOPSYS 323 | doGoimports [-h] 324 | doGoimports 325 | Goimports [-h] 326 | Goimports 327 | 328 | DESCRIPTION 329 | doGoimports will run 'goimports -w' on every opened 330 | '.go' file, and update ("get") the corresponding buffers. 331 | 332 | Goimports wraps calling doGoimports in Exec -- doGoimports's output is 333 | sent to the +Buffer instead of a random +Error window. 334 | 335 | ## Exec 336 | 337 | NAME 338 | Exec 339 | 340 | SYNOPSYS 341 | Exec [-h] 342 | Exec [-t [-a|id|pattern]] [-e [-a|id|pattern]] 343 | [-o [-a|id|pattern]] [-p outp] [-q errp] [-r both] 344 | 345 | DESCRIPTION 346 | Exec runs a command, sending stdout/stderr to acme buffers, 347 | defaulting to +Buffer. 348 | 349 | The buffers are automatically cleaned before hand. 350 | 351 | -t selects stdout buffer, forwarding [-a|id|pattern] to Getids 352 | -t selects stderr buffer, forwarding [-a|id|pattern] to Getids 353 | 354 | -o is a shortcut to set both -t and -e. 355 | 356 | -p and -q allows to pipe stdout/stderr through the given shell 357 | commands, before forwarding the result to To, which will be in 358 | charge of writing the content to an acme(1) buffer. 359 | 360 | -r allows to set both outp and errp at once. 361 | 362 | 363 | ## Getall 364 | 365 | NAME 366 | Getall 367 | 368 | SYNOPSYS 369 | Getall [-h] 370 | Getall 371 | 372 | DESCRIPTION 373 | Getall updates all windows from filesystem (runs Get everywhere). 374 | 375 | ## Getfn 376 | 377 | NAME 378 | Getfn 379 | 380 | SYNOPSYS 381 | Getfn [-h] 382 | Getfn [-a|id|pattern] 383 | 384 | DESCRIPTION 385 | Getfn prints the filenames of windows pointed by [-a|id|pattern], 386 | which is forwarded to Getids, thus defaulting to $winid 387 | (current window). 388 | 389 | This is a nice shortcut to avoid callers to handle multi-lines tag. 390 | 391 | ## Getids 392 | 393 | NAME 394 | Getids 395 | 396 | SYNOPSYS 397 | Getids [-h] 398 | Getids [-a|id|pattern] 399 | 400 | DESCRIPTION 401 | This is a key script; goals are: 402 | 403 | - to transform a pattern to a list of acme buffer IDs; 404 | - allow easy targetting the special '+Buffer' window; 405 | - to default to current window ($winid). 406 | 407 | When called with no arguments, simply display $winid, failing 408 | with an error code if it is not set. 409 | 410 | When called with -a, look for a +Buffer window, creating it 411 | if needed, and display its id. 412 | 413 | When called with a number, assume an existing windo id, and 414 | display it. 415 | 416 | Otherwise, assume argument is an awk(1) pattern, and display 417 | all matching buffers IDs, one per line. 418 | 419 | ## Godoc 420 | 421 | NAME 422 | Godoc 423 | 424 | SYNOPSYS 425 | Godoc [-h] 426 | Godoc [args...] 427 | 428 | DESCRIPTION 429 | Godoc executes go doc with the given arguments. This 430 | is a shortcut for: 431 | $ Exec go doc [args...] 432 | 433 | 434 | ## Goimports 435 | 436 | NAME 437 | doGoimports 438 | 439 | SYNOPSYS 440 | doGoimports [-h] 441 | doGoimports 442 | 443 | DESCRIPTION 444 | Goimports will run 'goimports -w' on every opened 445 | '.go' file, and update ("get") the corresponding buffers. 446 | 447 | ## Hook 448 | 449 | NAME 450 | Hook 451 | 452 | SYNOPSYS 453 | Hook [-h] 454 | 455 | DESCRIPTION 456 | Hook registers a single hook $HOME/acme.hook to 457 | all existing buffers, and to all buffers about 458 | to future buffer. 459 | 460 | win(1) buffers are systematically ignored. 461 | 462 | It also registers a pid in $HOME/acme.hook.pid for Unhook. 463 | 464 | BUGS 465 | For now at least, the idea is to have a central script 466 | acting as a hook, that could eventually delegate things 467 | to other scripts in case there's a need for finer filtering. 468 | 469 | 470 | ## Iswin 471 | 472 | NAME 473 | Iswin 474 | 475 | SYNOPSYS 476 | Iswin [-h] 477 | Iswin 478 | 479 | DESCRIPTION 480 | Iswin test if the buffer pointed by the given id seems to correspond 481 | to a win(1) buffer. 482 | 483 | It exists with status 0 if it's the case, otherwise with exit 484 | status 1, following ordinary sh(1) conventions. 485 | 486 | ## Mv 487 | 488 | NAME 489 | Mv 490 | 491 | SYNOPSYS 492 | Mv [-h] 493 | Mv [-a|id|pattern] 494 | 495 | DESCRIPTION 496 | Move (mv(1)) current file, or the one pointed by [-a|id|pattern], 497 | to a new one, on the filesystem that is, and updating acme's 498 | buffer's name accordingly. 499 | 500 | If 'to' doesn't start with a leading '/', renaming is performed 501 | relatively instead of absolutely. 502 | 503 | If the current buffer points to a file (by opposition to a directory), 504 | and 'to' ends with a '/', then the file pointed by the current buffer 505 | is moved to the directory pointed by 'to'. 506 | 507 | If 'to' describes a missing path, intermediate directories 508 | are created (mkdir(1) -p); existing file is overwritten, 509 | following mv(1)' semantic. 510 | 511 | EXAMPLE 512 | In each of the example below, the source filename is 513 | to be interpreted as the current buffer's name. 514 | 515 | Renaming "/home/foo/bar.c" to "/home/foo/baz.c": 516 | Mv /home/foo/baz.c 517 | Mv baz.c 518 | 519 | Renaming "/tmp/bar/" to "/tmp/foo/": 520 | Mv foo 521 | Mv foo/ 522 | 523 | Renaming "foo.c" to "foo/bar/foo.c": 524 | Mv foo/bar/ 525 | 526 | Renaming "foo.c" to "foo/bar": 527 | Mv foo/bar 528 | 529 | BUGS: 530 | We may want to allow regexp(7) with capture, so as to allow 531 | renaming multiple files/buffers at once. 532 | 533 | We require realpath(1) to be available to clean up the path, 534 | e.g. in case of 'Mv ../../'. 535 | 536 | 537 | ## Open 538 | 539 | NAME 540 | Open 541 | 542 | SYNOPSYS 543 | Open [-h] 544 | Open [-m] [-n] [-p] [-u] [-x] [-g] [-q] [pattern] 545 | Open [-r] [name] 546 | 547 | DESCRIPTION 548 | Open given file with acme, creating it as an empty file 549 | if necessary. 550 | 551 | If -m is specified, move back to / and open the first file 552 | that match given name. Note that this mimick Run's -m behavior, 553 | hence the option's name. 554 | 555 | If -n is specified, nor the file nor its parent directory will 556 | be created on disk/loaded from disk. 557 | 558 | If -p is specified, print opened files' acme windows' IDs, 559 | one per line. 560 | 561 | If -u is specified, Open will only open the file if no buffer 562 | matches the given pattern (unique). If no pattern is specified, 563 | the name will be used as a pattern instead. If there's already 564 | a corresponding buffer opened, then some of it will be 565 | made visible by writing show its acme//ctl. 566 | 567 | -x automatically triggers a chmod +x on the file. 568 | 569 | With -g, the name is interpreted as a pattern 570 | to be grep(1)'d in $HOME/acme.files: the first existing 571 | file matching the pattern is opened. If no match, exit 572 | with failure. -g implies -u. 573 | 574 | By default, Open will focus on the buffer being opened 575 | (see acme(4), 'ctl' file, 'show' option): -q disables this 576 | behavior. 577 | 578 | -r registers a filename to $HOME/acme.files. If no name is 579 | specified, then Open tries to collect one from a current 580 | acme buffer, if any. 581 | 582 | Paths starting with '/' are considered absolutely, otherwise, 583 | relatively. 584 | 585 | Nonexistent paths are created (mkdir(1) -p). 586 | 587 | Opened filenames are systematically added to $HOME/acme.files, 588 | which is cleaned via sort(1) -u, and only contains absolute 589 | paths. 590 | 591 | EXAMPLES 592 | The following will create a '+Buffer' window if there's no 593 | window with filename suffixed by +Buffer; this will be 594 | considered a "virtual" file, not tied to an on-disk file: 595 | 596 | Open -n -u '+Buffer' '\+Buffer$' 597 | 598 | 599 | ## Rdoc 600 | 601 | NAME 602 | Rdoc 603 | 604 | SYNOPSYS 605 | Rdoc [-h] 606 | Rdoc [cmd] 607 | 608 | DESCRIPTION 609 | Rdoc executes help(cmd) within R, via Exec. Conceptually, 610 | this is a shortcut for (there's some extra-boilerplate): 611 | $ Exec "echo 'help($cmd)' | Rscript /dev/stdin" 612 | 613 | BUG 614 | Relies on a sed(1) with the [:print:] character class for 615 | cleanup. 616 | 617 | 618 | ## Read 619 | 620 | NAME 621 | Read 622 | 623 | SYNOPSYS 624 | Read [-h] 625 | Read [-a|id|pattern] 626 | 627 | DESCRIPTION 628 | Read reads the body of the buffer pointed by [-a|id|pattern] 629 | and prints it to stdout. 630 | 631 | EXAMPLES 632 | Display '+Buffer's content on stdout: 633 | Read -a 634 | 635 | Look for a buffer suffixed by '+Files'' and displays its 636 | content on stdout: 637 | Read '\+Files' 638 | 639 | ## Rename 640 | 641 | NAME 642 | Rename 643 | 644 | SYNOPSYS 645 | Rename [-h] 646 | Rename [-d dot] [-a|id|pattern] 647 | 648 | DESCRIPTION 649 | Rename renames an identifier in current selection of the 650 | windows pointed by [-a|id|pattern], forwarded to Getids, 651 | thus defaulting to $winid (default window), using the 652 | famous sam(1) idiom described in sam_tut.pdf. 653 | 654 | By default, renaming is performed on the whole file (dot=,); 655 | the -d option allows to narrow the scope by setting the 656 | dot. 657 | 658 | EXAMPLE 659 | Rename identifiers from "fs" to "ifs" on current window: 660 | Rename fs ifs 661 | 662 | Same, but perform renaming on current selection only: 663 | Rename -d '' fs ifs 664 | Rename -d . fs ifs 665 | 666 | Same, but on all opened C files/headers: 667 | Rename -d . fs ifs '\.[ch]$' 668 | 669 | 670 | ## Rm 671 | 672 | NAME 673 | Rm 674 | 675 | SYNOPSYS 676 | Rm [-h] 677 | Rm [-a|id|pattern] 678 | 679 | DESCRIPTION 680 | Rm removes buffers pointed by [-a|id|pattern], forwarded 681 | to Getids, thus defaulting to $winid (current window), 682 | and related files/directories from filesystems. 683 | 684 | ## Run 685 | 686 | NAME 687 | Run 688 | 689 | SYNOPSYS 690 | Run [-h] 691 | Run [-t [-a|id|pattern]] [-e [-a|id|pattern]] [-m] [args] 692 | Run [-o [-a|id|pattern]] [-m] [args] 693 | 694 | DESCRIPTION 695 | Run is a smart script running a command automagically inferred 696 | from file/filename, redirecting its stdout/stderr to the buffers 697 | pointed by -t/-e/-o, after having written all buffers. 698 | 699 | User is expected to complete/adjust this script following 700 | his need. In particular, the $errp variable allows to 701 | pipe stderr to a command before sending to the error buffer 702 | pointed by -e/-o: this can be used for instance to adjust 703 | error messages to match plumbing rules. 704 | 705 | The buffers are automatically cleaned before hand. 706 | 707 | -t selects stdout buffer, forwarding [-a|id|pattern] to Getids 708 | -t selects stderr buffer, forwarding [-a|id|pattern] to Getids 709 | 710 | -o is a shortcut to set both -t and -e. 711 | 712 | -m will force the use of a Makefile by going up to / from current 713 | buffer's directory, until a Makefile is found. 714 | 715 | As a convenience, the $ARGS environment variable is feed to the 716 | program called to execute/compile the target. By comparison, the 717 | [args] are forwarded to the executed/compiled target. This allows 718 | specifying ad-hoc options. 719 | 720 | EXAMPLES 721 | 1. By default, on a buffer pointing to a Makefile, 'Run tests' will: 722 | - Put all buffers 723 | - Run 'make tests', redirecting stdin/stdout to '+Buffer', creating 724 | it if necessary, and cleaning it beforehand. 725 | 726 | 2. If executing 'Run -m tests' on a file /home/user/project/lib/lib.go, 727 | and if a /home/user/project/Makefile file exist, will use that 728 | makefile in a 'make tests'. 729 | 730 | 3. With the default setup, 'Run -- -m x' on a .c file will compile 731 | the C file (assuming single file project) and execute the resulting 732 | binary with '-m x' as CLI arguments. 733 | 734 | 4. 'ARGS=--show-trace Run' on a .nix file will attempt to execute the 735 | .nix file, launching the nix interpreter with '--show-trace'. 736 | 737 | ## See 738 | 739 | ./bin/See [-n] 740 | 741 | ## Switch 742 | 743 | NAME 744 | Switch 745 | 746 | SYNOPSYS 747 | Switch [-h] 748 | Switch 749 | Switch -r 750 | 751 | DESCRIPTION 752 | Switch dumps current state to from and loads the state described 753 | by to. 754 | 755 | ## To 756 | 757 | NAME 758 | To 759 | 760 | SYNOPSYS 761 | To [-h] 762 | To [-c] [-a|id|pattern] 763 | 764 | DESCRIPTION 765 | To redirects stdin/stdout to the first window pointed by 766 | [-a|id|pattern]. We default to -a (+Buffer file). 767 | 768 | If there's no buffer to write to, act as a cat(1). 769 | 770 | If -c is specified, then the output buffer is cleared. 771 | 772 | EXAMPLES 773 | # Display stat(1)'s output for current buffer in +Buffer, 774 | # creating it if necessary. 775 | (tagline)$ stat $% | To 776 | 777 | ## Unhook 778 | 779 | NAME 780 | Unhook 781 | 782 | SYNOPSYS 783 | Unhook [-h] 784 | 785 | DESCRIPTION 786 | Unhook "unregisters" $HOME/acme.hook by killing all the 787 | processes in the group of the process referenced in 788 | $HOME/acme.hook.pid. 789 | 790 | BUGS 791 | This is crude. 792 | 793 | For now, this is only useful because regular file dumping 794 | mechanism is broken once someone listens to an event file. 795 | 796 | So we need to unhook (before rehooking) each time we want 797 | to dump. See XDump. 798 | 799 | 800 | ## Write 801 | 802 | NAME 803 | Write 804 | 805 | SYNOPSYS 806 | Write [-h] 807 | Write [-a|id|pattern] 808 | 809 | DESCRIPTION 810 | Write writes data to buffer's special file (ctl/addr/body/data, etc.), 811 | for all files pointed by [-a|id|pattern], forwarded to Getids, 812 | thus defaulting to $winid (current window). 813 | 814 | EXAMPLES 815 | # Deletes all existing windows. 816 | (sh|tagline)$ Write ctl delete '.*' 817 | 818 | # Appends "hello" to current window's body 819 | (sh|tagline)$ Write body hello 820 | 821 | # Rename the current buffer ( is processed by printf(1)) 822 | (sh|tagline)$ Write ctl "name /path/to/newname\n" 823 | 824 | ## XDel 825 | 826 | NAME 827 | XDel 828 | 829 | SYNOPSYS 830 | XDel [-h] 831 | XDel [-a|id|pattern] 832 | 833 | DESCRIPTION 834 | XDel stores the content of the buffers pointed by [-a|id|pattern], 835 | and then proceed to close the buffer, while storing 836 | their filenames to the '+Files' buffer if necessary. The 837 | '+Files' buffer is created is necessary. 838 | 839 | 840 | ## XDump 841 | 842 | NAME 843 | XDump 844 | 845 | SYNOPSYS 846 | XDump [-h] 847 | XDump [-k] 848 | 849 | DESCRIPTION 850 | XDump dumps current state to . is automatically 851 | appended a .dump suffix if needed, and is stored as a file in 852 | $HOME/acme.dumps/ 853 | 854 | If -k is provided, it'll be forwarded to Do: the directory 855 | name of the +Buffer window won't be altered. 856 | 857 | ## XLoad 858 | 859 | NAME 860 | XLoad 861 | 862 | SYNOPSYS 863 | XLoad [-h] 864 | XLoad 865 | 866 | DESCRIPTION 867 | XLoad loads the state described by . is automatically 868 | appended a .dump suffix, and is looked as a file in $HOME/acme.dumps/. 869 | 870 | We nuke all existing windows before hand, as Acme's Load just 871 | add windows. 872 | 873 | 874 | ## XPut 875 | 876 | NAME 877 | XPut 878 | 879 | SYNOPSYS 880 | XPut [-h] 881 | XPut [-a|id|pattern] 882 | 883 | DESCRIPTION 884 | XPut stores files pointed by [-a|id|pattern], forwarded to Getids, 885 | thus defaulting to $winid (current window). 886 | 887 | EXAMPLES 888 | # Writes all windows 889 | (sh|tagline)$ XPut '.*' 890 | 891 | ## XPutall 892 | 893 | NAME 894 | XPutall 895 | 896 | SYNOPSYS 897 | XPutall [-h] 898 | XPutall [-a|id|pattern] 899 | 900 | DESCRIPTION 901 | XPutall stores all buffers (runs Put everywhere). 902 | 903 | 904 | -------------------------------------------------------------------------------- /See.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include <9pclient.h> 5 | #include 6 | 7 | /* 8 | * NOTE: this was written a long time ago. 9 | * 10 | * Going back to previous directories is nicely handled as 11 | * side effect when mouse search the window's title: 12 | * - click (M3) in the window's name after the slash of the 13 | * path you want to reach 14 | * - push mouse up and release 15 | */ 16 | 17 | enum { 18 | Pathmax = EVENTSIZE*UTFmax+1, 19 | }; 20 | 21 | char cwd[Pathmax]; 22 | 23 | // if -n, files will be opened to fwin. 24 | Win *win, *fwin; 25 | 26 | void 27 | cd(char *s) 28 | { 29 | int n; 30 | 31 | memset(cwd, 0, sizeof cwd); 32 | if (*s == '/') { 33 | if (strlen(s)+1 > sizeof(cwd)) 34 | goto err; 35 | strcpy(cwd, s); 36 | } else { 37 | getwd(cwd, sizeof(cwd)); 38 | n = strlen(cwd); 39 | if (cwd[n] != '/') 40 | cwd[++n] = '/'; 41 | if (strlen(s)+1+n > sizeof(cwd)) 42 | goto err; 43 | strcat(cwd, s); 44 | } 45 | cleanname(cwd); 46 | 47 | winname(win, "%s", cwd); 48 | 49 | winctl(win, "get"); 50 | return; 51 | 52 | err: 53 | sysfatal("directory too long, increase Pathmax (%d)", Pathmax); 54 | } 55 | 56 | /* 57 | * from $PLAN9/src/cmd/netfiles/acme.c 58 | * The pipe allows to leave the file window in a clean state. 59 | */ 60 | int 61 | pipetowin2(Win *w, char *name, int errto, char *cmd, ...) 62 | { 63 | va_list arg; 64 | char *p; 65 | int fd[3], pid, pfd[2]; 66 | char buf[1024]; 67 | int n; 68 | 69 | /* 70 | * cannot use winfd here because of buffering caused 71 | * by pipe. program might exit before final write to acme 72 | * happens. so we might return before the final write. 73 | * 74 | * to avoid this, we tend the pipe ourselves. 75 | */ 76 | if(pipe(pfd) < 0) 77 | sysfatal("pipe: %r"); 78 | va_start(arg, cmd); 79 | p = evsmprint(cmd, arg); 80 | va_end(arg); 81 | fd[0] = open("/dev/null", OREAD); 82 | fd[1] = pfd[1]; 83 | if(errto == 0) 84 | fd[2] = dup(fd[1], -1); 85 | else 86 | fd[2] = dup(errto, -1); 87 | pid = threadspawnl(fd, "rc", "rc", "-c", p, 0); 88 | free(p); 89 | while((n = read(pfd[0], buf, sizeof buf)) > 0) 90 | winwrite(w, name, buf, n); 91 | close(pfd[0]); 92 | return pid; 93 | } 94 | 95 | void 96 | opentofwin(char *fn) 97 | { 98 | winname(fwin, "%s", fn); 99 | winwrite(fwin, "addr", ",", 1); 100 | winwrite(fwin, "ctl", "dot=addr", 8); 101 | waitfor(pipetowin2(fwin, "data", 0, "%s %s", "cat", fn, NULL)); 102 | winwrite(fwin, "ctl", "clean", 5); 103 | } 104 | 105 | int 106 | isdir(char *s) 107 | { 108 | Dir *d; 109 | int ret; 110 | 111 | d = dirstat(s); 112 | 113 | if (d == nil) 114 | return 0; 115 | 116 | ret = d->mode & DMDIR; 117 | 118 | free(d); 119 | 120 | return ret; 121 | } 122 | 123 | void 124 | readevent(Event *e) 125 | { 126 | if (e->c2 == 'L' || e->c2 == 'l') { 127 | if (isdir(e->text)) 128 | cd(e->text); 129 | else if (fwin != nil) 130 | opentofwin(e->text); 131 | } else if (e->c2 == 'X' || e->c2 == 'x') { 132 | if (strncmp(e->text, "Del", 3) == 0) { 133 | windel(win, 1); 134 | winfree(win); 135 | if (fwin != nil) { 136 | windel(fwin, 1); 137 | winfree(fwin); 138 | } 139 | threadexitsall(nil); 140 | } 141 | } 142 | winwriteevent(win, e); 143 | } 144 | 145 | void 146 | threadmain(int argc, char *argv[]) 147 | { 148 | Event e1, e2; 149 | 150 | fwin = nil; 151 | 152 | ARGBEGIN { 153 | case 'n': 154 | fwin = newwin(); 155 | break; 156 | case 'h': 157 | // fprint(2, "%s [-n]\n", argv0); 158 | fprint(1, "%s [-n]\n", argv0); 159 | threadexitsall(nil); 160 | break; 161 | default: 162 | print("unknown flag '%c'\n", ARGC()); 163 | break; 164 | } ARGEND 165 | 166 | // cd requires win to be initialized first 167 | win = newwin(); 168 | 169 | // XXX $HOME set but not $home 170 | putenv("home", getenv("HOME")); 171 | 172 | if (argc > 1) 173 | cd(argv[1]); 174 | else 175 | cd(getenv("home")); 176 | 177 | for (;;) { 178 | winreadevent(win, &e1); 179 | 180 | if (e1.c1 == 'M') { 181 | readevent(&e1); 182 | if (e1.flag & 2) { 183 | winreadevent(win, &e2); 184 | readevent(&e2); 185 | } 186 | continue; 187 | } 188 | winwriteevent(win, &e1); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # XDump accumulating in main tagline @many-xdump-tagline 2 | To be tested, but likely, XDump stores many "XDump" in 3 | the dump file's tagline. If so, we could/should trim 4 | them. 5 | 6 | See also @auto-xdump-file. 7 | 8 | # Auto : crashes @auto-crash 9 | Recurrent acme crashes when using `Auto`: 10 | 11 | $ cat /tmp/acme.nohup.out.* 12 | acme: text.delete: Bad file descriptor 13 | acme: text.delete: Success 14 | acme: text.delete: Bad file descriptor 15 | acme: text.delete: Bad file descriptor 16 | 17 | See `/home/mb/plan9port/src/cmd/acme/text.c:/"text.delete"`. 18 | 19 | # Do : option to avoid updating +Buffer directory @do-no-chdir 20 | Or at least a mechanism so that our autodump mechanism 21 | (XDump) restores it after the dump. 22 | 23 | # Persistent Run/Exec on Switch @keep-exec-on-switch 24 | Existing Run outputing in a given +Buffer seems to 25 | die out of +Buffer being deleted/recreated on a Switch. 26 | 27 | Simplest way would be to make +Buffer permanent in XLoad(/Delall), 28 | but this wouldn't solve the issue in the general case. 29 | 30 | Another option would be that all Exec would register/tag their 31 | output windows, so that Delall wouldn't delete them. (e.g. 32 | /tmp/+Buffer -> /tmp/+Keep+Buffer). 33 | 34 | # optional/configurable autodump @autodump-Acme 35 | Allow automatic reload of autodump in Acme on startup. 36 | Allow autodump period configuration 37 | Allow to disable autodump on startup. 38 | Document in README.md 39 | 40 | Perhaps have autodump in an external script. 41 | 42 | # Ad-hoc buttons/scripts 43 | Have a file with two fields: 44 | 45 |