rwb-codemirror-helix 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -0
- package/dist/index.cjs +3849 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +3859 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# codemirror-helix
|
|
2
|
+
|
|
3
|
+
A vibe-coded, in the sense that I literally have not looked at the code, set of Helix keybindings for CodeMirror 6.
|
|
4
|
+
That said, it's fairly complete and works for my purposes (read: getting Helix keybindings in Obsidian).
|
|
5
|
+
|
|
6
|
+
--- Everything past this is LLM generated; read at your own risk. ---
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Normal / Insert / Select modes** with block cursor and per-mode CSS classes
|
|
11
|
+
- **Motions**: `h j k l w b e W B E f F t T ; , Alt-. 0 Home End G %`
|
|
12
|
+
- **Count prefixes**: `5j`, `3w`, `10l`, …
|
|
13
|
+
- **g-prefix** (goto): `gg` `ge` `gh` `gl` `gs` `gt` `gc` `gb` `gj` `gk` `g.` `gw` `g|`
|
|
14
|
+
- **z-prefix** (view): `zz` `zt` `zb` `zj` `zk`; `Z` sticky view mode
|
|
15
|
+
- **m-prefix** (match/surround/text-objects): `mm` `ms` `mr` `md` `mi` `ma`
|
|
16
|
+
- **`[ ]` prefixes**: `]p` `[p` `]Space` `[Space`
|
|
17
|
+
- **Editing**: `d` `Alt-d` `c` `Alt-c` `y` `p` `P` `R` `r` `~` `` ` `` `Alt-`` ` `` `>` `<` `J` `Alt-J` `.` `u` `U` `Ctrl-a` `Ctrl-x` `&` `_`
|
|
18
|
+
- **Multi-cursor**: `C` `Alt-C` `s` `S` `Alt-s` `K` `Alt-K` `)` `(` `Alt-)` `Alt-(` `Alt--` `Alt-_` `Alt-,` `,`
|
|
19
|
+
- **Named registers**: `"ay` `"ap` — yank/paste with any letter register
|
|
20
|
+
- **Surround**: `ms(` `md(` `mr('`
|
|
21
|
+
- **Text objects**: `miw` `maw` `mi(` `ma{` `mi"` …
|
|
22
|
+
- **Search**: `/` `?` `n` `N` `*` `Alt-*` — regex-capable inline panel
|
|
23
|
+
- **Jump list**: `Ctrl-s` `Ctrl-o` `Ctrl-i`
|
|
24
|
+
- **Insert mode helpers**: `Ctrl-w` `Alt-Backspace` `Alt-d` `Alt-Delete` `Ctrl-h` `Ctrl-d` `Delete` `Ctrl-u` `Ctrl-k` `Ctrl-s` `Enter` `Ctrl-r`
|
|
25
|
+
- **Status bar** showing current mode, count, pending operation, active register, and cursor position
|
|
26
|
+
- **Dot-repeat** (`.`) and **undo/redo** (`u` / `U`)
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install rwb-codemirror-helix
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`@codemirror/state` and `@codemirror/view` are peer dependencies and must be installed separately.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { EditorView } from "@codemirror/view";
|
|
40
|
+
import { EditorState } from "@codemirror/state";
|
|
41
|
+
import { helixKeymap } from "codemirror-helix";
|
|
42
|
+
|
|
43
|
+
const view = new EditorView({
|
|
44
|
+
state: EditorState.create({
|
|
45
|
+
doc: "Hello, world!",
|
|
46
|
+
extensions: [
|
|
47
|
+
helixKeymap(),
|
|
48
|
+
// ... your other extensions
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
parent: document.getElementById("editor"),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
helixKeymap({
|
|
59
|
+
// Show the status bar panel at the bottom of the editor. Default: true
|
|
60
|
+
statusBar: true,
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Reading the current mode
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { getHelixMode } from "codemirror-helix";
|
|
68
|
+
|
|
69
|
+
const mode = getHelixMode(view.state); // "normal" | "insert" | "select"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can also subscribe to mode changes via the exported `helixStateField`:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { helixStateField } from "codemirror-helix";
|
|
76
|
+
|
|
77
|
+
// Inside a ViewPlugin or updateListener:
|
|
78
|
+
const { mode } = update.state.field(helixStateField);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### CSS customisation
|
|
82
|
+
|
|
83
|
+
The editor root element receives a class for the current mode:
|
|
84
|
+
|
|
85
|
+
| Mode | Class |
|
|
86
|
+
|--------|-----------------------|
|
|
87
|
+
| Normal | `helix-mode-normal` |
|
|
88
|
+
| Insert | `helix-mode-insert` |
|
|
89
|
+
| Select | `helix-mode-select` |
|
|
90
|
+
|
|
91
|
+
Status bar colours and the block cursor can be overridden with CSS variables.
|
|
92
|
+
By default the status bar inherits the editor's background and foreground, so
|
|
93
|
+
it blends in with any theme. The mode indicator colours are chosen to be legible
|
|
94
|
+
on light backgrounds; override them for a dark theme:
|
|
95
|
+
|
|
96
|
+
```css
|
|
97
|
+
/* Example: dark theme overrides */
|
|
98
|
+
.cm-editor {
|
|
99
|
+
--helix-status-bg: #2d2d2d;
|
|
100
|
+
--helix-status-fg: #ccc;
|
|
101
|
+
--helix-status-border: #444;
|
|
102
|
+
--helix-normal-color: #5af;
|
|
103
|
+
--helix-insert-color: #8f8;
|
|
104
|
+
--helix-select-color: #fa8;
|
|
105
|
+
--helix-count-color: #ff8;
|
|
106
|
+
--helix-pending-color: #aaa;
|
|
107
|
+
--helix-register-color: #c8f;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Keybinding reference
|
|
112
|
+
|
|
113
|
+
### Normal mode
|
|
114
|
+
|
|
115
|
+
#### Movement
|
|
116
|
+
|
|
117
|
+
| Key | Action |
|
|
118
|
+
|-----|--------|
|
|
119
|
+
| `h` / `←` | Move left |
|
|
120
|
+
| `l` / `→` | Move right |
|
|
121
|
+
| `j` / `↓` | Move down |
|
|
122
|
+
| `k` / `↑` | Move up |
|
|
123
|
+
| `w` | Next word start |
|
|
124
|
+
| `b` | Previous word start |
|
|
125
|
+
| `e` | Next word end |
|
|
126
|
+
| `W` `B` `E` | Same, WORD (whitespace-delimited) |
|
|
127
|
+
| `f<c>` | Find char forward on line |
|
|
128
|
+
| `F<c>` | Find char backward on line |
|
|
129
|
+
| `t<c>` | Till char forward (one before) |
|
|
130
|
+
| `T<c>` | Till char backward (one after) |
|
|
131
|
+
| `Alt-.` | Repeat last `f`/`F`/`t`/`T` |
|
|
132
|
+
| `0` / `Home` | Line start |
|
|
133
|
+
| `End` | Line end |
|
|
134
|
+
| `%` | Select entire file |
|
|
135
|
+
| `Ctrl-d` | Scroll half page down |
|
|
136
|
+
| `Ctrl-u` | Scroll half page up |
|
|
137
|
+
| `Ctrl-f` / `PageDown` | Scroll page down |
|
|
138
|
+
| `Ctrl-b` / `PageUp` | Scroll page up |
|
|
139
|
+
|
|
140
|
+
#### g-prefix (goto)
|
|
141
|
+
|
|
142
|
+
| Key | Action |
|
|
143
|
+
|-----|--------|
|
|
144
|
+
| `gg` | Go to file start (or line N with count) |
|
|
145
|
+
| `ge` | Go to file end |
|
|
146
|
+
| `gh` | Go to line start |
|
|
147
|
+
| `gl` | Go to line end |
|
|
148
|
+
| `gs` | Go to first non-whitespace |
|
|
149
|
+
| `gt` | Go to top of screen |
|
|
150
|
+
| `gc` | Go to middle of screen |
|
|
151
|
+
| `gb` | Go to bottom of screen |
|
|
152
|
+
| `gj` | Move down (same as `j`) |
|
|
153
|
+
| `gk` | Move up (same as `k`) |
|
|
154
|
+
| `g.` | Go to last modification |
|
|
155
|
+
| `gw` | Jump-to-word (two-key label overlay) |
|
|
156
|
+
| `g|` | Go to column N (1-based, with count) or line start |
|
|
157
|
+
| `G` | Go to last line (or line N with count) |
|
|
158
|
+
|
|
159
|
+
#### z-prefix (view)
|
|
160
|
+
|
|
161
|
+
| Key | Action |
|
|
162
|
+
|-----|--------|
|
|
163
|
+
| `zz` / `zc` | Center cursor on screen |
|
|
164
|
+
| `zt` | Scroll cursor to top |
|
|
165
|
+
| `zb` | Scroll cursor to bottom |
|
|
166
|
+
| `zj` / `zk` | Scroll one line down / up |
|
|
167
|
+
| `Z` | Enter sticky view mode (view keys stay active until `Escape`) |
|
|
168
|
+
|
|
169
|
+
#### Selection
|
|
170
|
+
|
|
171
|
+
| Key | Action |
|
|
172
|
+
|-----|--------|
|
|
173
|
+
| `v` | Enter Select mode |
|
|
174
|
+
| `V` | Select current line and enter Select mode |
|
|
175
|
+
| `x` | Select current line |
|
|
176
|
+
| `X` | Extend to full line bounds |
|
|
177
|
+
| `Alt-x` | Shrink to line content |
|
|
178
|
+
| `;` | Collapse selection to cursor |
|
|
179
|
+
| `Alt-;` | Flip selection anchor/head |
|
|
180
|
+
| `Alt-:` | Ensure selection is forward |
|
|
181
|
+
| `,` | Keep only primary selection |
|
|
182
|
+
| `Alt-,` | Remove primary selection |
|
|
183
|
+
| `)` | Rotate primary selection forward |
|
|
184
|
+
| `(` | Rotate primary selection backward |
|
|
185
|
+
|
|
186
|
+
#### Multi-cursor
|
|
187
|
+
|
|
188
|
+
| Key | Action |
|
|
189
|
+
|-----|--------|
|
|
190
|
+
| `C` | Copy selection to next line |
|
|
191
|
+
| `Alt-C` | Copy selection to previous line |
|
|
192
|
+
| `s` | Select within selection (regex prompt) |
|
|
193
|
+
| `S` | Split selection on delimiter (regex prompt) |
|
|
194
|
+
| `Alt-s` | Split selections on newlines |
|
|
195
|
+
| `K` | Keep selections matching regex |
|
|
196
|
+
| `Alt-K` | Remove selections matching regex |
|
|
197
|
+
| `Alt-_` | Merge consecutive/overlapping selections |
|
|
198
|
+
| `Alt--` | Merge all selections into one |
|
|
199
|
+
| `Alt-)` | Rotate selection contents forward |
|
|
200
|
+
| `Alt-(` | Rotate selection contents backward |
|
|
201
|
+
|
|
202
|
+
#### Editing
|
|
203
|
+
|
|
204
|
+
| Key | Action |
|
|
205
|
+
|-----|--------|
|
|
206
|
+
| `d` | Delete selection (yanked) |
|
|
207
|
+
| `Alt-d` | Delete without yanking |
|
|
208
|
+
| `c` | Change selection (delete + insert) |
|
|
209
|
+
| `Alt-c` | Change without yanking |
|
|
210
|
+
| `y` | Yank selection |
|
|
211
|
+
| `p` | Paste after |
|
|
212
|
+
| `P` | Paste before |
|
|
213
|
+
| `R` | Replace selection with yanked text |
|
|
214
|
+
| `r<c>` | Replace each char in selection with `<c>` |
|
|
215
|
+
| `u` | Undo |
|
|
216
|
+
| `U` | Redo |
|
|
217
|
+
| `J` | Join lines |
|
|
218
|
+
| `Alt-J` | Join lines and select the inserted space |
|
|
219
|
+
| `~` | Toggle case |
|
|
220
|
+
| `` ` `` | Lowercase selection |
|
|
221
|
+
| `Alt-`` ` `` | Uppercase selection |
|
|
222
|
+
| `>` | Indent |
|
|
223
|
+
| `<` | Unindent |
|
|
224
|
+
| `_` | Trim leading/trailing whitespace from selection |
|
|
225
|
+
| `.` | Repeat last change |
|
|
226
|
+
| `Ctrl-a` | Increment number under cursor |
|
|
227
|
+
| `Ctrl-x` | Decrement number under cursor |
|
|
228
|
+
| `Ctrl-c` | Toggle line comment |
|
|
229
|
+
| `&` | Align selections (insert spaces to align `from` positions) |
|
|
230
|
+
| `"<r>` | Select register `<r>` for next yank/paste |
|
|
231
|
+
|
|
232
|
+
#### m-prefix (match / surround / text objects)
|
|
233
|
+
|
|
234
|
+
| Key | Action |
|
|
235
|
+
|-----|--------|
|
|
236
|
+
| `mm` | Jump to matching bracket |
|
|
237
|
+
| `ms<c>` | Surround selection with `<c>` |
|
|
238
|
+
| `md<c>` | Delete surrounding `<c>` |
|
|
239
|
+
| `mr<from><to>` | Replace surrounding `<from>` with `<to>` |
|
|
240
|
+
| `miw` | Select inside word |
|
|
241
|
+
| `maw` | Select around word (includes trailing space) |
|
|
242
|
+
| `mi(` `mi[` `mi{` | Select inside bracket pair |
|
|
243
|
+
| `ma(` `ma[` `ma{` | Select around bracket pair |
|
|
244
|
+
| `mi"` `mi'` `` mi` `` | Select inside quotes |
|
|
245
|
+
|
|
246
|
+
Surround characters: `(` `)` `[` `]` `{` `}` `<` `>` and any symmetric char (e.g. `"`).
|
|
247
|
+
|
|
248
|
+
#### [ / ] prefixes
|
|
249
|
+
|
|
250
|
+
| Key | Action |
|
|
251
|
+
|-----|--------|
|
|
252
|
+
| `]p` | Move to next paragraph |
|
|
253
|
+
| `[p` | Move to previous paragraph |
|
|
254
|
+
| `]Space` | Add blank line below |
|
|
255
|
+
| `[Space` | Add blank line above |
|
|
256
|
+
|
|
257
|
+
#### Search
|
|
258
|
+
|
|
259
|
+
| Key | Action |
|
|
260
|
+
|-----|--------|
|
|
261
|
+
| `/` | Open search forward |
|
|
262
|
+
| `?` | Open search backward |
|
|
263
|
+
| `n` | Go to next match |
|
|
264
|
+
| `N` | Go to previous match |
|
|
265
|
+
| `*` | Search for word under cursor |
|
|
266
|
+
| `Alt-*` | Search for selection (no word boundaries) |
|
|
267
|
+
|
|
268
|
+
Search supports full JavaScript regex syntax.
|
|
269
|
+
|
|
270
|
+
#### Jump list
|
|
271
|
+
|
|
272
|
+
| Key | Action |
|
|
273
|
+
|-----|--------|
|
|
274
|
+
| `Ctrl-s` | Save current position to jump list |
|
|
275
|
+
| `Ctrl-o` | Jump back |
|
|
276
|
+
| `Ctrl-i` | Jump forward |
|
|
277
|
+
|
|
278
|
+
### Insert mode
|
|
279
|
+
|
|
280
|
+
| Key | Action |
|
|
281
|
+
|-----|--------|
|
|
282
|
+
| `Escape` | Return to Normal mode |
|
|
283
|
+
| `Ctrl-w` / `Alt-Backspace` | Delete previous word |
|
|
284
|
+
| `Alt-d` / `Alt-Delete` | Delete next word |
|
|
285
|
+
| `Ctrl-h` | Delete previous character |
|
|
286
|
+
| `Ctrl-d` / `Delete` | Delete next character |
|
|
287
|
+
| `Ctrl-u` | Delete to line start |
|
|
288
|
+
| `Ctrl-k` | Delete to line end |
|
|
289
|
+
| `Enter` / `Ctrl-j` | New line with indentation |
|
|
290
|
+
| `Ctrl-s` | Commit undo checkpoint |
|
|
291
|
+
| `Ctrl-r<r>` | Paste from register `<r>` |
|
|
292
|
+
|
|
293
|
+
### Entering Insert mode (from Normal)
|
|
294
|
+
|
|
295
|
+
| Key | Action |
|
|
296
|
+
|-----|--------|
|
|
297
|
+
| `i` | Insert before selection |
|
|
298
|
+
| `a` | Insert after selection |
|
|
299
|
+
| `I` | Insert at line start |
|
|
300
|
+
| `A` | Insert at line end |
|
|
301
|
+
| `o` | Open new line below |
|
|
302
|
+
| `O` | Open new line above |
|
|
303
|
+
|
|
304
|
+
## Development
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Install dependencies
|
|
308
|
+
npm install
|
|
309
|
+
|
|
310
|
+
# Run the demo app (hot-reloading)
|
|
311
|
+
npm run dev
|
|
312
|
+
|
|
313
|
+
# Run tests
|
|
314
|
+
npm test
|
|
315
|
+
|
|
316
|
+
# Type-check
|
|
317
|
+
npm run typecheck
|
|
318
|
+
|
|
319
|
+
# Build the library
|
|
320
|
+
npm run build
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## What's not implemented
|
|
324
|
+
|
|
325
|
+
- **Macros** (`q` / `@`)
|
|
326
|
+
- **Command-line mode** (`:` prompt)
|
|
327
|
+
- **LSP commands** (`gd`, `gr`, Space-mode) — require a language server
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
MIT
|