141 lines
5.1 KiB
Markdown
141 lines
5.1 KiB
Markdown
# Plumbing — configuring `~/lib/plumbing` for Kate
|
|
|
|
Kate is a full plan9port **plumb client**: it sends plumb messages (plumb a
|
|
token under the caret) and receives them on the `edit` port (open a file at a
|
|
line, or a directory as a folder). The *routing* — what a plumbed string means —
|
|
lives in the plumber's rules, not in Kate. This document covers the user-side
|
|
rules in `~/lib/plumbing` that make files and directories open in Kate.
|
|
|
|
The implementation (the native 9P client, the edit-port reader, and the
|
|
in-process handler) lives in `src/plumb/` — `plumb_lib` is the pure codec/9P
|
|
client and `plumbplugin.{h,cpp}` is the Kate glue.
|
|
|
|
## How plumbing reaches Kate
|
|
|
|
```
|
|
plumb <text> # or Kate's F2 on the caret token
|
|
│
|
|
▼
|
|
plumber ── applies ~/lib/plumbing rules ──► routes to a PORT
|
|
│
|
|
▼ (edit port)
|
|
Kate's reader thread ──► onPlumbEdit() ──► open file@line / open folder
|
|
```
|
|
|
|
- The plumber matches the message against rules **top to bottom; first match
|
|
wins**.
|
|
- A rule routes to a **port** (`plumb to edit`, `plumb to web`, …). The `edit`
|
|
port is where editors listen.
|
|
- `plumb client <prog>` / `plumb start <prog>` name what to run **if no
|
|
application currently holds that port**. A ruleset may contain **only one**
|
|
`client`/`start` action.
|
|
- `client` — the started program is expected to *open the port itself*; the
|
|
message is then delivered through the port. acme and Kate both work this
|
|
way (Kate via its edit-port reader).
|
|
- `start` — just runs the program with the given arguments.
|
|
|
|
## Make Kate the editor
|
|
|
|
Set the editor variable near the top of `~/lib/plumbing`:
|
|
|
|
```
|
|
editor = /usr/bin/kate
|
|
```
|
|
|
|
Every file rule ends with `plumb to edit` + `plumb client $editor`, so this
|
|
alone makes **files open in Kate**:
|
|
|
|
- If Kate is already running, its reader holds the `edit` port and receives the
|
|
message directly.
|
|
- If nothing holds `edit`, the plumber cold-starts `/usr/bin/kate`; once its
|
|
reader attaches, the message is delivered.
|
|
|
|
The **line/column** travels in the message as an `addr` attribute (e.g.
|
|
`addr=42`), *not* as a command-line argument. `plumb client` delivers it through
|
|
the port, and Kate's handler jumps to it — so `foo.c:42` opens `foo.c` at line
|
|
42.
|
|
|
|
> The `edit` port is single-consumer. If acme and Kate run at the same time,
|
|
> whichever holds `edit` wins; you cannot have both receive plumbed files. With
|
|
> `editor = /usr/bin/kate`, Kate is the one cold-started and holding the port.
|
|
|
|
## Open a directory as a folder
|
|
|
|
The stock `basic` rules only match **regular files** (`arg isfile`), so a bare
|
|
directory path fails with *"couldn't find destination for message."* Add a
|
|
directory rule. Place it **before** the generic file rules (those use `isfile`,
|
|
which fails on a directory, so ordering is not strictly required, but keeping it
|
|
with the other specific rules is clearest):
|
|
|
|
```
|
|
# any existing directory opens as a folder in Kate
|
|
type is text
|
|
data matches '[a-zA-Z¡-0-9_\-./@~]+'
|
|
arg isdir $0
|
|
data set $dir
|
|
plumb to edit
|
|
plumb start kate $dir
|
|
```
|
|
|
|
- `arg isdir $0` succeeds only for an existing directory and sets `$dir`.
|
|
- `data set $dir` makes the directory path the message payload.
|
|
- `plumb to edit` delivers to Kate's reader when it is running (the in-process
|
|
handler opens the folder; `start` does **not** fire).
|
|
- `plumb start kate $dir` cold-starts Kate on the directory when nothing holds
|
|
`edit`. Kate registers `inode/directory` in its desktop entry and opens a
|
|
directory as a folder.
|
|
|
|
Note the one-`client`/`start`-per-ruleset limit: this rule uses `start` (not
|
|
both `client` and `start`), which covers running and cold-start cases together.
|
|
|
|
## Apply changes to a running plumber
|
|
|
|
Rules reload without restarting the plumber:
|
|
|
|
```
|
|
cat ~/lib/plumbing | 9p write plumb/rules
|
|
```
|
|
|
|
A malformed ruleset is rejected whole (the write fails with an error such as
|
|
`ruleset has more than one client or start action`); the previous rules stay
|
|
in effect until a valid file is written.
|
|
|
|
## Quick verification
|
|
|
|
```
|
|
# a file (should route to the edit port, carrying addr=3)
|
|
plumb /path/to/file:3
|
|
|
|
# a directory (should route to the edit port, data = the dir)
|
|
plumb /path/to/dir
|
|
```
|
|
|
|
To watch what a plumb actually delivers, read the edit port in another shell:
|
|
|
|
```
|
|
9p read plumb/edit # prints the message: src/dst/wdir/type/attr/ndata/data
|
|
```
|
|
|
|
A successful file message looks like:
|
|
|
|
```
|
|
plumb
|
|
edit
|
|
<wdir>
|
|
text
|
|
addr=3
|
|
15
|
|
/path/to/file
|
|
```
|
|
|
|
and a directory message has an empty attr line and `data = /path/to/dir`.
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Cause | Fix |
|
|
|---|---|---|
|
|
| `plumb: can't send message: couldn't find destination` | no rule matched (e.g. a directory with only `isfile` rules) | add the directory rule above |
|
|
| files open in **acme**, not Kate | `editor` still points at acme | set `editor = /usr/bin/kate` and reload |
|
|
| `9p write plumb/rules` fails | malformed rule, or two `client`/`start` actions in one ruleset | keep a single `client`/`start` per rule; fix the reported line |
|
|
| directory plumb starts Kate but no folder | Kate build without `inode/directory` handling | verify `kate /some/dir` opens the folder from a shell |
|