summaryrefslogtreecommitdiff
path: root/docs/typ/guide.typ
blob: b247ab6b9626c01b58a420af9bca8e435bca2cf1 (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// The guide: pardes day to day, read once in about ten minutes. The
// cheatsheet is the index of keys and words; the reference has every
// 9P file; scripting has the recipes.
#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs

pardes is a screen of columns. Each column holds panes, and each pane is a
tag (a line of words) over a body: a file, a terminal, a PDF or an image.
Above the columns runs the workspace tag, and each column has a tag of its
own. Any text anywhere can be clicked: #btn("B2") runs it, #btn("B3") looks
at it. The keys are helix's, in modes.

= Modes <modes>

Each pane has its own mode and keeps it while you are elsewhere: leave a
terminal at its `$` and it is still `$` when you come back. The box at the
left of a pane's tag shows the mode.

#pairs(
  [normal (box blank)], [keys move and select; an edit acts on the selection. File and PDF panes start here, and so does a terminal from #key("Alt-n").],
  [insert (`^`)], [keys type. #keys("i", "a", "o") and the rest enter it.],
  [raw (`$`), terminals only], [keys go to the program. A terminal from #word("Tty"), the shell a bare `pardes` starts with, and a command pane start here.],
)

#key("Ctrl-b") switches a terminal between raw and normal; in normal mode
the shell's prompts are hidden and its text is a page to move over and
copy from. #word("Mode") in the tag steps raw, normal, insert. Esc and
Shift-Esc depend on the mode:

#pairs(
  [normal], [#key("Esc"): back to the previous pane (#word("Last")). #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.],
  [insert], [#key("Esc"): back to normal. #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.],
  [raw `$`], [#key("Esc"): to the program, except at an idle, empty shell prompt, where it goes back to the previous pane. #key("Shift-Esc"): always back to the previous pane. Either way the terminal stays `$`.],
  [a PDF], [#key("Esc"): clears the selection and the search highlights. #key("Shift-Esc"): back to the previous pane.],
)

"The previous pane" is the last other pane on the jump list; with none, the
next pane down its column (wrapping), else any other pane. In raw mode
every key but #key("Ctrl-b"), Esc and the paste chords goes to the
program, so #key("Ctrl-w") and #key("Alt-n") need you out of raw mode
first (and #key("Ctrl-w") out of insert mode, where it deletes a word).

= The mouse <mouse>

#pairs(
  btn("B1"), [select; a plain click puts the cursor there and gives the pane the keyboard. In a tag it starts typing at the click, in insert mode. A double click selects the word, the line (at a line's start or end), or up to the matching bracket or quote.],
  btn("B2"), [execute: a builtin word runs, anything else is a shell line.],
  btn("B3"), [look: open the file, address, directory or URL, or else find the word's next place.],
)

A #btn("B2") or #btn("B3") click with no drag takes the word under it,
where a word is the run of letters, digits, non-ASCII and `. - + / : @ _ ~`
around the click; a trailing `:` is dropped. So #btn("B3") anywhere on
`src/bar.c:12:5:` in a compiler's `src/bar.c:12:5: error` opens
`src/bar.c` at line 12, byte column 5, while a click on `error` finds
`error`. Drag instead to say exactly what you mean. #key("Enter") and
#key("Tab") in normal mode look and execute the selection, or the word
under the cursor. The chords are on the cheatsheet.

= Tags <tags>

There are three kinds, and which directory a command runs in depends on
which one you click:

#tag("Newcol Joincol Find Grep Help Changelog Tutor Dump Themes Config Debug Exit")
#tag("New Tty Find Grep Joincol Delcol")
#tag("Save Tty Collapse Del", path: "/home/me/notes.txt")

- The workspace tag and the column tags run commands in the session's
  directory (where pardes started). A column's own words (#word("New"),
  #word("Tty"), #word("Delcol")) act on that column; a pane word
  (#word("Save"), #word("Del")) acts on the column's pane with the
  keyboard, or its first; from the workspace tag, on the pane with the
  keyboard.
- A pane's tag runs commands in the pane's directory: a file's directory, a
  terminal's current directory.

A tag is text with undo: type a word into it and click it, or delete the
defaults. #key(":") moves the keyboard between a body and its tag. A pane
tag may wrap onto several lines; column and workspace tags are one line.
The path at the start of a pane's tag is computed: typing into it, or
clicking it, drafts a new name, #key("Enter") confirms and the next
#word("Save") writes there; #key("Esc") cancels. #word("Collapse") folds a
pane to its tag. Unsaved text shows on the grip, the box left of the tag.
Drag the grip up or down to resize, or onto another column to move the
pane there.

= The active column and the keyboard <active-column>

Two things are easy to confuse:

- *The pane with the keyboard* is where your keys go.
- *The active column* is where new panes go. It is the column you last
  typed in, #btn("B1")-clicked in, dropped a pane into or gave the keyboard
  to its tag, or the one that got the last new pane. A look does not move
  it.

They usually agree. Here is how they split. You are typing in column 1, so
column 1 is active. You click #word("New") in column 2's tag: a scratch
pane opens in column 2 and takes the keyboard, and column 2 is now
active. In it you #btn("B3") `x.txt`, which is already open in column 1:
the keyboard jumps to that pane in column 1, but the active column stays
2, so the next pane a look opens lands in column 2, away from where you
are. Type or click in column 1 and it is active again.

== Where new panes go <new-panes>

#word("Placement") picks the rules: `acme` (the default) or `pardes`.
Under `acme`, a new pane goes into the column whose tag asked, else the
active column, never into a new column. An empty column it takes whole;
#word("New") and 9P's #file("pane/new") take the bottom half of the
column's last pane; a pane opened from a pane's text (a look, #word("Tty"),
#key("Alt-n")) goes under the pane with the most blank rows, or halves the
biggest; a command pane goes to the last column. #word("New") in a pane's
tag opens a `+New` scratch named in that pane's directory, in that pane's
column; from a column tag, in that column and the session's directory.
Every new pane but a command pane takes the keyboard. No pane is made
shorter than its tag and two rows; where there is no room the pane is
refused.

A column can be empty, as in acme: closing its last pane leaves it, its
tag over blank space. #word("Delcol") and #word("Joincol") take columns
away. Closing the session's last pane quits pardes.

= Looking <looking>

#btn("B3") (or #key("Enter")) on a path opens it, or goes to the pane that
already shows it; `file:12` goes to line 12. The address forms are on the
cheatsheet. A directory types `ls` into a terminal idle there, else opens
a terminal there. A URL opens in the browser.

A relative path is looked for where the click was, then where you have
been: first in the looking pane's own directory, then in the directory of
each pane on the jump list, most recent first. The first that names a
file, or a pane open on that path, wins. `./x` and `../x` look only in the
pane's own directory.

= Command panes <command-panes>

#btn("B2") on a line that is no builtin (`make`, `git log`) runs it with
the #word("Shell") setting's `-c`, in the directory of the tag or pane it
came from. In a terminal idle at an empty prompt, a line from that
terminal's own text is typed into its shell. Anywhere else it runs in a
command pane, a terminal of its own:

#tag("Kill Save Collapse Del", path: "/home/me/src (make) exit 0")

Its tag says `running`, then `exit N`. The next command for the same
directory reuses a command pane that has finished (from a column tag, only
one in that column), below what it showed; a pane still running, or one a
background job still prints to, is never reused, so a second command
meanwhile gets a pane of its own. #word("Kill") stops what pardes started
(`Kill make`: those whose line starts with `make`); #word("Exit") quits
pardes.

= Unsaved panes <unsaved-panes>

A pane whose text is unsaved is marked on its grip. #word("Del") on it
refuses once: a notice says `1 unsaved pane — Del again to discard` and the
pane is listed in `+Unsaved`. The same #word("Del") again, with nothing
edited since, discards it. #word("Exit"), #word("Restore") and
#word("Delcol") refuse once over unsaved panes the same way. A `+New`
scratch under 100 bytes and a command's output never hold anything up.

From the keyboard (#key("SPC d")), #word("Del") on a pane with panes above
and below it also asks which neighbour takes its rows: #key("k") above,
#key("j") below, any other key keeps the pane. A click never asks, and
gives the rows to the pane above.

= Terminals <terminals>

A terminal is a pane like any other.

#tag("Tty+bash Save Mode Filter Collapse Del", path: "/home/me/src")

- `Tty+bash` opens another terminal on that shell; a bare #word("Tty")
  runs the #word("Shell") setting, `$SHELL` when it is executable, else
  `/bin/sh`.
- #word("Save") asks for a path on the pane's notice band and writes the
  terminal's scrollback there as text; `Save path` writes it at once.
- #word("Filter") maps the program's colours through the theme.
- In raw mode Ctrl-V types what you yanked into the program (with nothing
  yanked, the program gets the key), and Ctrl-Shift-V the desktop
  clipboard.
- A program that tracks the mouse (htop, vim with `mouse=a`) gets
  #btn("B1")'s clicks and drags and the wheel over its grid; #btn("B2") and
  #btn("B3") stay pardes's. Hold Shift to swap: #btn("B1", shift: true)
  selects and Shift-wheel scrolls pardes's scrollback, while
  #btn("B2", shift: true) and #btn("B3", shift: true) go to the program as
  its buttons 2 and 3. A full-screen program that does not track the mouse
  gets the wheel as arrow keys. Tags, grips and gutters stay pardes's.
- `Repl python` in a terminal's tag makes #btn("B2") on a `.py` pane send
  the text to that REPL instead of running it.

= Reviewing diffs <reviewing-diffs>

Open a `.diff` or `.patch`, or run `git diff` (or `git show`, `diff -u`) as
a command: once it has finished, output that starts as a diff is shown as
one, each hunk coloured in its file's language and its added and removed
rows tinted. Then #btn("B3") to jump, with the usual look resolution:

- a `diff --git`, `---` or `+++` line opens the file (the new one, or the
  old one on a `---` line unless the `+++` under it names another);
- a `@@ -a,b +c,d @@` line goes to line `c` of the new file;
- the `+`, `-` or space in a hunk line's first column goes to that line in
  the new file (for a removed line, the line now standing where it was);
- the code after it is ordinary words.

git's `a/` and `b/` prefixes are dropped (and `c/ i/ w/ o/` with
`diff.mnemonicPrefix`); a `--no-prefix` diff's paths are kept as written.

= `pardes FILE` and `--wait` <editor>

In a pane's shell, `pardes FILE` opens FILE in this session and returns at
once, as acme's `B` does. A FILE not there yet opens an empty pane named
for it; #word("Save") creates it, making its directories first. Something
the session refuses (a bad name) is printed and the command exits 1.

`pardes --wait FILE` (`-w`) returns when the pane showing FILE is closed
(exit 0) or the session goes away (exit 1), as acme's `E` does. Set
`EDITOR='pardes --wait'` (`GIT_EDITOR` follows it), and `git commit`,
`crontab -e` and fish's Ctrl-O open in a pane and read the file once you
close it. Bare `pardes` inside a pane refuses and names `--nested`, which
starts a separate session whose shells do not forward to it.

= Keys <keys>

Motions select what they cross, and an edit acts on the selection: #key("w")
then #key("d") deletes a word. #key("x") selects lines, #key("v") extends
characters, #key(";") collapses to the cursor. #key("s") makes a cursor
per regex match inside the selection, and every edit then acts at each.
#key("/") is a case-insensitive substring search, one hit a line; the
regexes are on #key("s") and #key("S"). #keys("n", "N") step through
everything a look would open, across panes. Line end is #key("g l"), and
#key("$") is helix's keep-pipe. #key("SPC") starts the leader,
#key("SPC ?") lists every path, and #word("Help") lists every key and
builtin. #word("Tutor") (#key("SPC h t")) practises them.

The language servers run as child processes, one per language when it is
on `PATH`: `zls`, `rust-analyzer`, `clangd`, `gopls`,
`typescript-language-server` and `pyright-langserver`
(`PARDES_LSP_ZIG`, `_RS`, `_C`, `_GO`, `_TS`, `_PY` name another, empty
turns one off):

#pairs(
  [#keys("g d", "g D", "g y", "g i", "g r")], [definition, declaration, type, implementation, references; Ctrl-#btn("B1") is a definition too],
  [#keys("SPC l k", "SPC l r", "SPC l a", "SPC l h")], [hover, rename, code action, select the references],
  [#keys("SPC l s", "SPC l S", "SPC l d", "SPC l D")], [symbols and diagnostics, of the file and the workspace],
  [#keys("SPC l c", "SPC l C", "SPC l t", "SPC l T")], [incoming and outgoing calls, supertypes and subtypes],
  [#keys("] d", "[ d")], [next and previous diagnostic],
  [#key("=")], [format],
  [#keys("SPC l i", "SPC l w")], [#word("Lspinfo"): the servers' state; #word("Lspwhy"): why the last query found what it did],
)

One answer jumps; several open a list where #key("Enter") on a row goes
there. In insert mode #key("Tab") after a `.` lists the candidate
declarations and inserts nothing. A format or a rename within the file is
one undo step; a rename that reaches other files opens a preview instead
of changing them.

= Sessions <sessions>

```
pardes --detach=work &      a session with no screen of its own
pardes --attach=work        show it here
pardes-gui --attach=work    the SDL window can attach too
```

The session owns the panes, shells and files; frontends come and go.
#word("Attach") `work` (#key("SPC s a")) switches this window to that
session, and #word("Detach") (#key("SPC s D")) leaves it running, shells
and all. Bare `--detach` names the session after its pid; bare `--attach`
needs exactly one session. Every attached frontend sees the same screen,
at the smallest common size. With none attached, `size C R` on the root
ctl sets the screen and messages clear by the clock. A detached session
ends when its last pane closes, as any session does.

= Config <config>

#word("Config") (#key("SPC f c")) opens the startup file,
`~/.config/pardes/init` (`$XDG_CONFIG_HOME/pardes/init` when that is
absolute; on macOS `~/Library/Application Support/pardes/init`). Each line
is one builtin, run at start as if executed; `#` starts a comment, and a
line that fails is skipped silently. Text that is no builtin does not run
as a shell command.

```
Theme atelier
Shell zsh
Placement pardes
```

#word("DumpConfig") opens every live setting as the line that sets it, so
it pastes back into `init` as is. The settings are listed with the root
ctl in the reference (#doc("fs", section: "settings")). Keys are
compile-time, in `src/config.zig`.

#word("Dump") writes the workspace to `pardes-<date>-<time>.zon` in
#word("DumpDir"), and `Restore path` (or `pardes -l dump.zon`) brings it
back: panes, columns, tags, selections, theme and changed settings; a
terminal returns with its last MiB of output and a new shell in its old
directory. Undo history and REPL bindings are not kept. A crash appends
two lines (build, time, platform, pid; the panic message) to `crashes`
beside `init`.