summaryrefslogtreecommitdiff
path: root/docs/typ/setup.typ
blob: 60ea33cdd8fd59e0b702ccf9551c6cd895390a74 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Setting up your environment: what to put outside pardes (the shell's
// startup files, a desktop launcher, other programs' configs) so that it
// works best. Each snippet says in one line why it is there.
#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs

pardes runs as it is, but a few lines elsewhere make it fit: a mount so
scripts see the session as files, pardes as the editor and file opener of
other programs, and the servers and fonts it looks for.

= A 9P mount: 9ns <ninens>

`9ns` is cloud9's 9P namespace tool: it mounts a 9P tree through FUSE in a
private mount namespace, as a plain user, and runs a program inside. Get it
from cloud9 (`git.sr.ht/~gbrls/cloud9`): its `zig build` builds `9ns` on
Linux into `zig-out/bin`, and Linux needs `/dev/fuse`. With `--mntgen` it
mounts the whole registry of posted 9P servers at `/mnt/9p` and exports
`$NINE_MOUNT`; every pardes session posts itself there, as
`$NINE_MOUNT/pardes/<pid or name>`.

Run pardes inside one, and the editor and everything it starts (its
terminals, command panes, the programs they run) see `$NINE_MOUNT` and the
session tree, so scripting is just files:

#cmd("9ns --mntgen -- pardes-gui")

A desktop launcher (`~/.local/share/applications/pardes.desktop`) that
does it, and still starts pardes on a machine with no 9ns or no
`/dev/fuse`. Write your own absolute paths: a launcher's `Exec` line takes
a `$` only escaped, and a desktop session's `PATH` may not hold
`~/.local/bin`.

```
[Desktop Entry]
Type=Application
Name=pardes
Exec=/bin/sh -c "if [ -x /home/me/.local/bin/9ns ] && [ -e /dev/fuse ]; then exec /home/me/.local/bin/9ns --mntgen -- /home/me/.local/bin/pardes-gui; else exec /home/me/.local/bin/pardes-gui; fi"
Terminal=false
Categories=Development;
```

A shell can wrap itself, so every interactive shell has the mount (and the
terminal pardes runs in, with the pardes inside it). It checks `$NINE_MOUNT`
first, so a shell already inside a mount does not wrap again. fish, in
`~/.config/fish/config.fish`:

```
if status is-interactive; and not set -q NINE_MOUNT
        and test -e /dev/fuse; and type -q 9ns
    exec 9ns --mntgen -- fish
end
```

bash, in `~/.bashrc`:

```
if [[ $- == *i* && -z $NINE_MOUNT && -e /dev/fuse ]] && command -v 9ns >/dev/null; then
    exec 9ns --mntgen -- bash
fi
```

= pardes as your editor <editor-setup>

`pardes --wait FILE` opens FILE in the session whose pane it runs in and
returns when you close that pane; outside pardes it starts an editor of its
own, which also returns when you are done. Set it as the editor, and every
program that asks for one opens a pane:

#cmd("set -gx VISUAL 'pardes --wait'; set -gx EDITOR 'pardes --wait'   # fish")
#cmd("export VISUAL='pardes --wait' EDITOR='pardes --wait'            # bash, zsh")

`git commit` (`GIT_EDITOR`, then `core.editor`, then these), `crontab -e`
and fish's `edit_command_buffer` (Alt-E or Alt-V) read `VISUAL` before
`EDITOR`, so set both, or a `VISUAL` left from elsewhere wins. To edit the
command line with Ctrl-O in fish instead, add `bind ctrl-o edit_command_buffer` to `fish_user_key_bindings`.

= A pager <pager>

`pardes -` reads its standard input into the directory's one `+Pager`
pane, escapes stripped, and returns; the next paged text refills it. With
`Pager pardes` (the default) a terminal's shell gets `PAGER`, `GIT_PAGER`
and `SYSTEMD_PAGER` set to this pardes plus ` -` (the path quoted only when
it needs it), and `SYSTEMD_PAGERSECURE=0` (it has no shell escape), each
only where your environment does not set it. So `git log`, `journalctl` or
`man` output arrives as a pane to search and click in, a diff (`git diff`,
`git show`, `git log -p`) drawn as one, and the terminal is back at once;
command panes page through `cat`. `man` pages through `MANPAGER` first,
then `PAGER`, and pardes never sets `MANPAGER`: your own `MANPAGER` wins,
as does any `PAGER`, `GIT_PAGER` or `SYSTEMD_PAGER` of yours. To keep your
own pager, set it in your environment, or put `Pager off` in the startup
file (or write it to the root ctl); it applies to terminals started after
it. Outside pardes, `PAGER='pardes -'` opens a new editor on the text.

= yazi in a terminal pane <yazi>

yazi, run in a terminal pane, can open files as panes of the session: an
opener that runs `pardes` forwards each file to the session and returns at
once. In `~/.config/yazi/yazi.toml` (yazi 26):

```
[opener]
pardes = [
  { run = 'pardes %s', desc = "Open in pardes", for = "unix" },
]

[open]
prepend_rules = [
  { mime = "text/*", use = "pardes" },
]
```

`%s` is the selected files (older yazi spelled it `"$@"`). yazi's own
`edit` opener runs `$EDITOR` and waits, so with `EDITOR='pardes --wait'`
it already opens a pane and blocks until you close it; the opener above
does not wait. Without a file manager, #btn("B3") on a path in any pane
(an `ls`, a `find`, a compiler's message) does the same.

= Shells <shells>

pardes injects OSC 133 prompt marks into bash and fish, sourcing your own
`~/.bashrc` and `config.fish` first: they tell it where each prompt and
command starts and ends. That is what makes #key("Esc") at a prompt go back
a pane only when nothing is typed, and #file("pty/run") answer `exit N`
and the output. Other
shells (zsh, sh, dash, ksh, nu and the rest) get no marks, and marks a shell
emits on its own do not count: #file("pty/run") answers `error no prompt marks`, and a prompt counts as free whenever no program holds the terminal,
typed text or not. Running `exec zsh` inside a bash pane loses the marks
the same way. The #word("Shell") setting picks the shell; bare, it is
`$SHELL` if that is executable, else `/bin/sh`.

= Language servers <language-servers>

pardes starts a language server as a child process for a file it knows,
looking for the program on `PATH`; `PARDES_LSP_<LANG>` names another
program, and an empty value turns that language off.

#pairs(
  [`.zig`], [`zls` (`PARDES_LSP_ZIG`)],
  [`.rs`], [`rust-analyzer` (`PARDES_LSP_RS`)],
  [`.c .h .cc .cpp .hpp .cxx .hxx`], [`clangd` (`PARDES_LSP_C`)],
  [`.go`], [`gopls` (`PARDES_LSP_GO`)],
  [`.ts .tsx .js .jsx .mjs .cjs`], [`typescript-language-server --stdio` (`PARDES_LSP_TS`)],
  [`.py`], [`pyright-langserver --stdio`, from pyright (`PARDES_LSP_PY`)],
)

#word("Lspinfo") (#key("SPC l i")) says which server serves the file and
what state it is in.

= Fonts and themes <fonts-and-themes>

The terminal build draws in your terminal's font. The SDL window
(`pardes-gui`) carries Adwaita Mono and finds others in `/usr/share/fonts`,
`/usr/local/share/fonts`, `~/.local/share/fonts` and `~/.fonts`, with no
fontconfig: `Fonts` lists them, and `Font name:size` (size
8-72) picks one, a line for the startup file. Themes are a word away:
`Theme <name>`, or `ThemeFile themes/mine.zon` for your own, reloaded as
you save it (#doc("themes")).

= plan9port <plan9port>

plan9port's `9p` reads and writes a session without any mount, which is
what a script uses when `$NINE_MOUNT` is not set:

#cmd("9p -a \"unix!$PARDES_9P\" read index")
#cmd("echo Save | 9p -a \"unix!$PARDES_9P\" write pane/3/ctl")

Its `write` always opens with OTRUNC, so `9p write pane/3/body` replaces the
whole body where acme would append: append through a mount with `>>`.
acme habits carry over: `pardes FILE` in a pane is acme's `B`,
`pardes --wait` is `E`, the files have acme's names (`body`, `tag`, `ctl`,
`addr`, `data`, `event`), and a program holding a pane's #file("event")
takes its clicks, as acme's `win` and its friends do.