pair-mode 0.1.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/LICENSE +21 -0
- package/README.md +209 -0
- package/assets/syntax/c.yaml +59 -0
- package/assets/syntax/css.yaml +43 -0
- package/assets/syntax/dockerfile.yaml +35 -0
- package/assets/syntax/elixir.yaml +30 -0
- package/assets/syntax/fish.yaml +47 -0
- package/assets/syntax/go.yaml +62 -0
- package/assets/syntax/html.yaml +70 -0
- package/assets/syntax/javascript.yaml +75 -0
- package/assets/syntax/json.yaml +39 -0
- package/assets/syntax/lua.yaml +111 -0
- package/assets/syntax/markdown.yaml +49 -0
- package/assets/syntax/proto.yaml +40 -0
- package/assets/syntax/python3.yaml +62 -0
- package/assets/syntax/ruby.yaml +73 -0
- package/assets/syntax/rust.yaml +78 -0
- package/assets/syntax/sh.yaml +69 -0
- package/assets/syntax/sql.yaml +35 -0
- package/assets/syntax/terraform.yaml +87 -0
- package/assets/syntax/toml.yaml +56 -0
- package/assets/syntax/typescript.yaml +49 -0
- package/assets/syntax/yaml.yaml +34 -0
- package/assets/syntax/zsh.yaml +51 -0
- package/dist/claude-code.js +9294 -0
- package/dist/cli.js +13701 -0
- package/dist/codex.js +9448 -0
- package/dist/opencode.js +9268 -0
- package/dist/pair-tui.js +2729 -0
- package/dist/pi.js +9354 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Owen Carpenter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# pair mode
|
|
2
|
+
|
|
3
|
+
A coding agent proposes an edit. Pair mode opens a side-by-side diff in a terminal
|
|
4
|
+
editor. Every line you type becomes a question the model must answer, and the edit
|
|
5
|
+
does not apply until you close the editor.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install -g pair-mode
|
|
11
|
+
pair-mode setup
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Install pair-mode globally before you run setup. Setup writes the install path into each
|
|
15
|
+
CLI's config as an absolute path. `npx pair-mode setup` runs from a package cache that
|
|
16
|
+
npm later prunes, which would leave every hook pointing at a deleted file. Setup detects
|
|
17
|
+
that case and stops.
|
|
18
|
+
|
|
19
|
+
The setup command detects the CLIs and multiplexers on your machine, registers the
|
|
20
|
+
required hooks, and writes a config file. Restart Claude Code after setup, because
|
|
21
|
+
Claude Code loads hooks only at startup.
|
|
22
|
+
|
|
23
|
+
If you install from git instead of npm, run `pnpm build` first. The package does not
|
|
24
|
+
ship a committed `dist/` directory; npm builds it fresh through the `prepublishOnly`
|
|
25
|
+
script, but a git checkout does not run that script.
|
|
26
|
+
|
|
27
|
+
## Supported CLIs
|
|
28
|
+
|
|
29
|
+
| CLI | Hook | Status |
|
|
30
|
+
| ----------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
|
31
|
+
| Claude Code | `PreToolUse`, matcher `Write\|Edit\|MultiEdit` | Shipped. |
|
|
32
|
+
| Codex | `PreToolUse`, matcher `apply_patch\|Edit\|Write` | Shipped. |
|
|
33
|
+
| pi | `tool_call` extension hook | Planned for 1.0.0. The adapter exists and passes its unit tests. Setup does not offer it yet. |
|
|
34
|
+
| opencode | `tool.execute.before` plugin hook | Planned for 1.0.0. Nobody has run it against a live opencode. Setup does not offer it yet. |
|
|
35
|
+
|
|
36
|
+
Codex has no `MultiEdit` matcher alias. Its `apply_patch` parser reads single-file Add,
|
|
37
|
+
Update, and Delete patches only. A multi-file or rename patch passes through untouched.
|
|
38
|
+
|
|
39
|
+
## The /pair command
|
|
40
|
+
|
|
41
|
+
`pair-mode setup` installs a `/pair` command for every CLI whose hook it registers. The
|
|
42
|
+
command toggles pair mode for the current directory. It also tells the agent how a held
|
|
43
|
+
edit comes back.
|
|
44
|
+
|
|
45
|
+
| CLI | Installed at | Invoked as |
|
|
46
|
+
| ----------- | ------------------------------- | ----------------------- |
|
|
47
|
+
| Claude Code | `~/.claude/commands/pair.md` | `/pair on`, `/pair off` |
|
|
48
|
+
| Codex | `~/.codex/skills/pair/SKILL.md` | `$pair on`, `$pair off` |
|
|
49
|
+
|
|
50
|
+
Codex deprecated `~/.codex/prompts/` in favour of skills, so pair mode installs a skill
|
|
51
|
+
there. Codex invokes a skill with `$`, not `/`.
|
|
52
|
+
|
|
53
|
+
The command runs a bare `pair-mode`, unlike a hook, which each CLI invokes by absolute
|
|
54
|
+
path. So `pair-mode` must resolve on your PATH. A global install puts it there.
|
|
55
|
+
|
|
56
|
+
`pair-mode doctor` warns when the command is missing, and warns when `pair-mode` does not
|
|
57
|
+
resolve on PATH. Neither warning raises the exit code.
|
|
58
|
+
|
|
59
|
+
Setup backs up an existing file before it rewrites the file.
|
|
60
|
+
|
|
61
|
+
## Editors
|
|
62
|
+
|
|
63
|
+
| Editor | Diff colour | Syntax colour on changed rows |
|
|
64
|
+
| -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| pair (default) | Yes | Yes. pair is the built-in review pane, so it paints syntax colour and change colour on the same row. It also reads mouse clicks and drags for selection, and it anchors a note to the exact span you select, not the whole line. |
|
|
66
|
+
| micro | Yes | No. micro paints one highlight group per character, so a changed row trades syntax colour for the diff band. |
|
|
67
|
+
| vim | Yes | Yes. `matchadd()` overlays the diff highlight on top of syntax, so a changed row keeps both. |
|
|
68
|
+
| nvim | Yes | Yes. Same `matchadd()` overlay as vim. |
|
|
69
|
+
| nano | Yes | No. nano has no syntax engine here, so a changed row gets the diff band only. |
|
|
70
|
+
|
|
71
|
+
Keys:
|
|
72
|
+
|
|
73
|
+
- pair: see [Keymap](#keymap) below.
|
|
74
|
+
- micro: Ctrl+W or F2 sends your annotations and closes the editor. F3 moves between
|
|
75
|
+
panes.
|
|
76
|
+
- vim and nvim: `:wqa` sends your annotations and closes the editor.
|
|
77
|
+
|
|
78
|
+
## Keymap
|
|
79
|
+
|
|
80
|
+
These keys work inside the pair review pane. Some have a mouse equivalent; the rest are
|
|
81
|
+
keyboard only.
|
|
82
|
+
|
|
83
|
+
| Key | Mouse equivalent | Action |
|
|
84
|
+
| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
85
|
+
| `j` `k` `↓` `↑` | — | Move the row cursor. A fold moves as one row. |
|
|
86
|
+
| `Ctrl+d` `Ctrl+u` | — | Page down and page up. |
|
|
87
|
+
| `n` `N` | — | Jump to the next and the previous changed run. |
|
|
88
|
+
| `v`, then a motion | click and drag | Select a span. |
|
|
89
|
+
| `a` | — | Open a note on the current selection, or on the current row if nothing is selected. |
|
|
90
|
+
| `Enter` | — | Save the note. |
|
|
91
|
+
| `Esc` | — | Discard the note draft, or clear the current selection. |
|
|
92
|
+
| `Tab` | — | Cycle the focused note. |
|
|
93
|
+
| `d` | — | Delete the focused note. |
|
|
94
|
+
| `Space` | click a fold | Expand or collapse a fold row. |
|
|
95
|
+
| `u` | — | Swap between the split layout and the unified layout. |
|
|
96
|
+
| `Ctrl+s` | — | Send the notes. The hook denies the edit. |
|
|
97
|
+
| `Ctrl+q` `Ctrl+c` | — | Quit. With no notes, the edit applies. With notes pending, asks: `s` sends, `d` discards and quits, `Esc` cancels. |
|
|
98
|
+
| `?` | — | Toggle the keymap overlay. |
|
|
99
|
+
|
|
100
|
+
zellij consumes the mouse scroll wheel, so pair mode never depends on it. `Ctrl+d` and
|
|
101
|
+
`Ctrl+u` page instead.
|
|
102
|
+
|
|
103
|
+
Holding `shift` during a click or drag passes the mouse event straight through to the
|
|
104
|
+
terminal, which then selects text for copy — the same as any other terminal program.
|
|
105
|
+
|
|
106
|
+
## Multiplexers
|
|
107
|
+
|
|
108
|
+
Claude Code and Codex run their hooks with no controlling terminal. `/dev/tty` returns
|
|
109
|
+
`ENXIO` there. Pane mode needs zellij or tmux to open an editor pane under those two
|
|
110
|
+
CLIs.
|
|
111
|
+
|
|
112
|
+
Session mode removes the requirement. See the next section.
|
|
113
|
+
|
|
114
|
+
## Session modes
|
|
115
|
+
|
|
116
|
+
Pane mode is the default. The hook opens an editor in a floating pane and blocks. That
|
|
117
|
+
needs a multiplexer under Claude Code and Codex.
|
|
118
|
+
|
|
119
|
+
Session mode moves the review out of the agent's process. The hook posts to a Unix
|
|
120
|
+
socket and waits. A client you start renders the review and answers. There are two
|
|
121
|
+
clients, and both remove the multiplexer requirement.
|
|
122
|
+
|
|
123
|
+
Set `transport` to `"session"`, then pick a client.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
pair-mode config transport session
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`pair-mode config` with no argument prints every setting and its value. `pair-mode
|
|
130
|
+
config <key>` prints one. `pair-mode config <key> <value>` changes one and validates it
|
|
131
|
+
before it writes.
|
|
132
|
+
|
|
133
|
+
**Watch mode** reviews in a terminal you own.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
pair-mode watch # in any terminal, at the repo root
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The watcher paints an idle screen between reviews and opens the pair TUI for each one.
|
|
140
|
+
`q` on the idle screen quits and releases the socket.
|
|
141
|
+
|
|
142
|
+
**Web mode** reviews in a browser and needs no terminal at all.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
pair-mode on --web # prints a link
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
That spawns a detached watcher, binds an HTTP server on `127.0.0.1`, and prints a link
|
|
149
|
+
carrying a random token. Drag across any text in the diff. A popup opens under the
|
|
150
|
+
selection. Type the note and press Enter. `pair-mode off` stops the watcher.
|
|
151
|
+
|
|
152
|
+
The watcher process binds the socket, so there is no separate daemon to manage. If the
|
|
153
|
+
watcher dies, the socket goes away and every hook fails open at once.
|
|
154
|
+
|
|
155
|
+
A hook that finds no watcher applies the edit rather than hanging. So does a hook whose
|
|
156
|
+
watcher never answers within `session.timeout`.
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
Pair mode reads `$XDG_CONFIG_HOME/pair-mode/config.json`, or `~/.config/pair-mode/config.json`
|
|
161
|
+
when `XDG_CONFIG_HOME` is not set.
|
|
162
|
+
|
|
163
|
+
| Key | Type | Default | Meaning |
|
|
164
|
+
| ----------------- | --------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- |
|
|
165
|
+
| `editor` | `"auto" \| "pair" \| "micro" \| "nvim" \| "vim" \| "nano"`, or an array of editor names | `"auto"` | Which editor opens the review pane. |
|
|
166
|
+
| `multiplexer` | `"auto" \| "zellij" \| "tmux" \| "none"` | `"auto"` | Which multiplexer hosts the pane. |
|
|
167
|
+
| `transport` | `"pane" \| "session"` | `"pane"` | Open a pane per edit, or post to a watcher you started. |
|
|
168
|
+
| `session.timeout` | integer, 1 or more | `300` | Seconds a hook waits for a verdict before it applies the edit. |
|
|
169
|
+
| `web.enabled` | boolean | `false` | Serve the review in a browser whenever a watcher starts. |
|
|
170
|
+
| `web.port` | integer, 0 to 65535 | `0` | `0` asks the operating system for a free port. |
|
|
171
|
+
| `layout` | `"split" \| "inline"` | `"split"` | Side-by-side columns, or one column. |
|
|
172
|
+
| `notes` | `"panel" \| "anchored"` | `"panel"` | A docked notes panel, or a note inline at its anchor. |
|
|
173
|
+
| `context` | integer, 1 or more | `5` | Unchanged lines kept around a change before folding. |
|
|
174
|
+
| `minFold` | integer, 1 or more | `4` | Minimum run of unchanged lines that folds. |
|
|
175
|
+
| `pane.width` | string | `"95%"` | |
|
|
176
|
+
| `pane.height` | string | `"95%"` | |
|
|
177
|
+
| `theme.add` | 6-digit hex colour | `"#1e3a1e"` | |
|
|
178
|
+
| `theme.del` | 6-digit hex colour | `"#3a1e1e"` | |
|
|
179
|
+
| `theme.fold` | 6-digit hex colour | `"#2a2a2a"` | |
|
|
180
|
+
| `theme.rowBand` | boolean | `true` | Paint the whole changed row, not just the changed span, in pair. |
|
|
181
|
+
| `syntax` | boolean | `true` | Load Shiki for syntax colour in pair. |
|
|
182
|
+
| `trace` | boolean | `false` | |
|
|
183
|
+
| `autoApprove` | boolean | `true` | |
|
|
184
|
+
|
|
185
|
+
An `editor` array lists editor names in order of preference. Pair mode tries each in
|
|
186
|
+
turn and uses the first one it finds on the machine. `auto` tries `pair` first, so an
|
|
187
|
+
existing config with no `editor` key now opens pair mode's own pane by default.
|
|
188
|
+
|
|
189
|
+
`notes` and `layout` both describe position, and they are easy to confuse. `layout`
|
|
190
|
+
controls the diff itself — split into two columns, or one inline column. `notes`
|
|
191
|
+
controls only where a note you write renders — in a docked panel, or anchored next to
|
|
192
|
+
the span it annotates. Setting one does not affect the other.
|
|
193
|
+
|
|
194
|
+
## Limits
|
|
195
|
+
|
|
196
|
+
- Pair mode opens one pane per tool call. It cannot batch a changeset, because a hook
|
|
197
|
+
returns one verdict per call and an allow cannot be withdrawn.
|
|
198
|
+
- The pane reviews. It does not edit the proposal.
|
|
199
|
+
- Syntax colour in pair needs `shiki` installed. A missing package disables colour and
|
|
200
|
+
the pane still works.
|
|
201
|
+
- A client answers one review at a time. It cannot skip ahead in the queue.
|
|
202
|
+
- Web mode does not defend against another user on the same machine. The token sits in
|
|
203
|
+
the URL, and a local user can read the process list.
|
|
204
|
+
- Watch mode needs the watcher running before the agent edits. A missing watcher fails
|
|
205
|
+
open, so an unattended agent applies its edits.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
filetype: c
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "(\\.(c|C)$|\\.(h|H)$|\\.ii?$|\\.(def)$)"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
|
8
|
+
- type: "\\b(_Atomic|_BitInt|float|double|_Decimal32|_Decimal64|_Decimal128|_Complex|complex|_Imaginary|imaginary|_Bool|bool|char|int|short|long|enum|void|struct|union|typedef|typeof|typeof_unqual|(un)?signed|inline|_Noreturn)\\b"
|
|
9
|
+
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32)|wchar)_t\\b"
|
|
10
|
+
# GCC float/decimal/fixed types
|
|
11
|
+
- type: "\\b(_Float16|__fp16|_Float32|_Float32x|_Float64|_Float64x|__float80|_Float128|_Float128x|__float128|__ibm128|__int128|_Fract|_Sat|_Accum)\\b"
|
|
12
|
+
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
|
13
|
+
- statement: "\\b(auto|volatile|register|restrict|_Alignas|alignas|_Alignof|alignof|static|const|constexpr|extern|_Thread_local|thread_local)\\b"
|
|
14
|
+
- statement: "\\b(for|if|while|do|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b"
|
|
15
|
+
- statement: "\\b(goto|continue|break|return)\\b"
|
|
16
|
+
- statement: "\\b(asm|fortran)\\b"
|
|
17
|
+
- preproc: "^[[:space:]]*#[[:space:]]*(define|embed|pragma|include|(un|ifn?)def|endif|el(if|ifdef|ifndef|se)|if|line|warning|error|__has_include|__has_embed|__has_c_attribute)"
|
|
18
|
+
- preproc: "^[[:space:]]*_Pragma\\b"
|
|
19
|
+
# GCC builtins
|
|
20
|
+
- statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)"
|
|
21
|
+
- statement: "__(aligned|asm|builtin|extension|hidden|inline|packed|restrict|section|typeof|weak)__"
|
|
22
|
+
# Operator Color
|
|
23
|
+
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b"
|
|
24
|
+
- symbol.brackets: "[(){}]|\\[|\\]"
|
|
25
|
+
# Integer Constants
|
|
26
|
+
- constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)"
|
|
27
|
+
# Decimal Floating Constants
|
|
28
|
+
- constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)"
|
|
29
|
+
# Hexadecimal Floating Constants
|
|
30
|
+
- constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)"
|
|
31
|
+
- constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)"
|
|
32
|
+
|
|
33
|
+
- constant.string:
|
|
34
|
+
start: '"'
|
|
35
|
+
end: '"'
|
|
36
|
+
skip: "\\\\."
|
|
37
|
+
rules:
|
|
38
|
+
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
|
|
39
|
+
|
|
40
|
+
- constant.string:
|
|
41
|
+
start: "'"
|
|
42
|
+
end: "'"
|
|
43
|
+
skip: "\\\\."
|
|
44
|
+
rules:
|
|
45
|
+
# TODO: Revert back to - error: "..+" once #3127 is merged
|
|
46
|
+
- error: "[[:graph:]]{2,}'"
|
|
47
|
+
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
|
|
48
|
+
|
|
49
|
+
- comment:
|
|
50
|
+
start: "//"
|
|
51
|
+
end: "$"
|
|
52
|
+
rules:
|
|
53
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
54
|
+
|
|
55
|
+
- comment:
|
|
56
|
+
start: "/\\*"
|
|
57
|
+
end: "\\*/"
|
|
58
|
+
rules:
|
|
59
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
filetype: css
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "\\.(css|scss)$"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
# Classes and IDs
|
|
8
|
+
- statement: "(?i)."
|
|
9
|
+
# - normal:
|
|
10
|
+
# start: "\\{"
|
|
11
|
+
# end: "\\}"
|
|
12
|
+
# rules: []
|
|
13
|
+
# css commands
|
|
14
|
+
- type: "(align-content|align-items|alignment-baseline|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|appearance|azimuth|backdrop-filter|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|baseline-shift|bookmark-label|bookmark-level|bookmark-state|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-boundary|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-decoration-break|box-shadow|box-sizing|box-snap|box-suppress|break-after|break-before|break-inside|caption-side|caret|caret-animation|caret-color|caret-shape|chains|clear|clip|clip-path|clip-rule|color|color-interpolation-filters|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|columns|column-span|column-width|content|continue|counter-increment|counter-reset|counter-set|cue|cue-after|cue-before|cursor|direction|display|dominant-baseline|elevation|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|float-defer|float-offset|float-reference|flood-color|flood-opacity|flow|flow-from|flow-into|font|font-family|font-feature-settings|font-kerning|font-language-override|font-size|font-size-adjust|font-stretch|font-style|font-synthesis|font-variant|font-variant-alternates|font-variant-caps|font-variant-east-asian|font-variant-ligatures|font-variant-numeric|font-variant-position|font-weight|footnote-display|footnote-policy|gap|glyph-orientation-vertical|grid|grid-area|grid-auto-columns|grid-auto-flow|grid-auto-rows|grid-column|grid-column-end|grid-column-gap|grid-column-start|grid-gap|grid-row|grid-row-end|grid-row-gap|grid-row-start|grid-template|grid-template-areas|grid-template-columns|grid-template-rows|hanging-punctuation|height|hyphenate-character|hyphenate-limit-chars|hyphenate-limit-last|hyphenate-limit-lines|hyphenate-limit-zone|hyphens|image-orientation|image-rendering|image-resolution|initial-letter|initial-letter-align|initial-letter-wrap|isolation|justify-content|justify-items|justify-self|left|letter-spacing|lighting-color|line-break|line-grid|line-height|line-snap|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|marker|marker-end|marker-knockout-left|marker-knockout-right|marker-mid|marker-pattern|marker-segment|marker-side|marker-start|marquee-direction|marquee-loop|marquee-speed|marquee-style|mask|mask-border|mask-border-mode|mask-border-outset|mask-border-repeat|mask-border-slice|mask-border-source|mask-border-width|mask-clip|mask-composite|mask-image|mask-mode|mask-origin|mask-position|mask-repeat|mask-size|mask-type|max-height|max-lines|max-width|min-height|min-width|mix-blend-mode|motion|motion-offset|motion-path|motion-rotation|nav-down|nav-left|nav-right|nav-up|object-fit|object-position|offset-after|offset-before|offset-end|offset-start|opacity|order|orphans|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-style|overflow-wrap|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page|page-break-after|page-break-before|page-break-inside|pause|pause-after|pause-before|perspective|perspective-origin|pitch|pitch-range|play-during|pointer-events|polar-anchor|polar-angle|polar-distance|polar-origin|position|presentation-level|quotes|region-fragment|resize|rest|rest-after|rest-before|richness|right|rotation|rotation-point|ruby-align|ruby-merge|ruby-position|running|scrollbar-color|scroll-behavior|scroll-snap-align|scroll-snap-margin|scroll-snap-margin-block|scroll-snap-margin-block-end|scroll-snap-margin-block-start|scroll-snap-margin-bottom|scroll-snap-margin-inline|scroll-snap-margin-inline-end|scroll-snap-margin-inline-start|scroll-snap-margin-left|scroll-snap-margin-right|scroll-snap-margin-top|scroll-snap-padding|scroll-snap-padding-block|scroll-snap-padding-block-end|scroll-snap-padding-block-start|scroll-snap-padding-bottom|scroll-snap-padding-inline|scroll-snap-padding-inline-end|scroll-snap-padding-inline-start|scroll-snap-padding-left|scroll-snap-padding-right|scroll-snap-padding-top|scroll-snap-type|shape-image-threshold|shape-inside|shape-margin|shape-outside|size|speak|speak-as|speak-header|speak-numeral|speak-punctuation|speech-rate|stress|string-set|stroke|stroke-alignment|stroke-dashadjust|stroke-dasharray|stroke-dashcorner|stroke-dashoffset|stroke-linecap|stroke-linejoin|stroke-miterlimit|stroke-opacity|stroke-width|table-layout|tab-size|text-align|text-align-all|text-align-last|text-combine-upright|text-decoration|text-decoration-color|text-decoration-line|text-decoration-skip|text-decoration-style|text-emphasis|text-emphasis-color|text-emphasis-position|text-emphasis-style|text-indent|text-justify|text-orientation|text-overflow|text-shadow|text-space-collapse|text-space-trim|text-spacing|text-transform|text-underline-offset|text-underline-position|text-wrap|top|transform|transform-box|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|user-select|vertical-align|visibility|voice-balance|voice-duration|voice-family|voice-pitch|voice-range|voice-rate|voice-stress|voice-volume|volume|white-space|widows|width|will-change|word-break|word-spacing|word-wrap|wrap-after|wrap-before|wrap-flow|wrap-inside|wrap-through|writing-mode|z-index):"
|
|
15
|
+
# - default:
|
|
16
|
+
# start: ":"
|
|
17
|
+
# end: "[;^\\{]"
|
|
18
|
+
# rules: []
|
|
19
|
+
- special: "!important"
|
|
20
|
+
- identifier: ":active|:focus|:hover|:link|:visited|:link|:after|:before|$"
|
|
21
|
+
- special: "(\\{|\\}|\\(|\\)|\\;|:|\\]|~|<|>|,)"
|
|
22
|
+
# SCSS Varaibles
|
|
23
|
+
- statement: "@import|@mixin|@extend"
|
|
24
|
+
# Strings
|
|
25
|
+
- constant.string:
|
|
26
|
+
start: '"'
|
|
27
|
+
end: '"'
|
|
28
|
+
skip: "\\\\."
|
|
29
|
+
rules:
|
|
30
|
+
- constant.specialChar: "\\\\."
|
|
31
|
+
- constant.string:
|
|
32
|
+
start: "'"
|
|
33
|
+
end: "'"
|
|
34
|
+
skip: "\\\\."
|
|
35
|
+
rules:
|
|
36
|
+
- constant.specialChar: "\\\\."
|
|
37
|
+
- special: '"|'''
|
|
38
|
+
# Comments & TODOs
|
|
39
|
+
- comment:
|
|
40
|
+
start: "\\/\\*"
|
|
41
|
+
end: "\\*\\/"
|
|
42
|
+
rules:
|
|
43
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
filetype: dockerfile
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "((Docker|Container)file[^/]*$|\\.(docker|container)file$)"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
## Keywords
|
|
8
|
+
- type.keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]"
|
|
9
|
+
|
|
10
|
+
## Brackets & parenthesis
|
|
11
|
+
- statement: "(\\(|\\)|\\[|\\])"
|
|
12
|
+
|
|
13
|
+
## Double ampersand
|
|
14
|
+
- special: "&&"
|
|
15
|
+
|
|
16
|
+
## Comments
|
|
17
|
+
- comment:
|
|
18
|
+
start: "#"
|
|
19
|
+
end: "$"
|
|
20
|
+
rules:
|
|
21
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
22
|
+
|
|
23
|
+
- constant.string:
|
|
24
|
+
start: '"'
|
|
25
|
+
end: '"'
|
|
26
|
+
skip: "\\\\."
|
|
27
|
+
rules:
|
|
28
|
+
- constant.specialChar: "\\\\."
|
|
29
|
+
|
|
30
|
+
- constant.string:
|
|
31
|
+
start: "'"
|
|
32
|
+
end: "'"
|
|
33
|
+
skip: "\\\\."
|
|
34
|
+
rules:
|
|
35
|
+
- constant.specialChar: "\\\\."
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
filetype: elixir
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "\\.ex$|\\.exs$"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
- statement: "\\b(abs|trunc|rem|div|round|max|min|and|or|not|throw|raise|reraise|hd|tl|in|length|elem|put_elem|destructure|to_(string|charlist)|is_(atom|binary|bitstring|boolean|float|function|integer|list|map|nil|number|pid|port|reference|tuple)|(bit|byte|map|tuple)_size|binary_part|def(delegate|exception|guard|guardp|impl|macro|macrop|module|overridable|p|protocol|struct)?|sigil_[crswCRSWDNT]|if|else|unless|cond|binding|node|self|spawn|spawn_link|spawn_monitor|send|exit|struct|get_and_update_in|get_in|put_in|pop_in|update_in|apply|inspect|make_ref|use|do|end)\\b"
|
|
8
|
+
- statement: "\\b(alias|import|require|case|fn|receive|after|try|catch|rescue|super|quote|unquote|unquote_splicing|for|with)\\b"
|
|
9
|
+
|
|
10
|
+
- constant: "\\b\\[A-Z]+\\b"
|
|
11
|
+
- constant.number: "\\b[0-9]+\\b"
|
|
12
|
+
|
|
13
|
+
- constant.string: "`[^`]*`|%x\\{[^}]*\\}"
|
|
14
|
+
- constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!"
|
|
15
|
+
- constant.string: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!"
|
|
16
|
+
|
|
17
|
+
- symbol.brackets: "\\{|\\}|\\[|\\]|\\(|\\)"
|
|
18
|
+
|
|
19
|
+
- comment: "#[^{].*$|#$"
|
|
20
|
+
- comment.bright: "##[^{].*$|##$"
|
|
21
|
+
|
|
22
|
+
- type.keyword: "\\:[a-zA-Z][a-zA-Z0-9_]*"
|
|
23
|
+
- type.keyword: "\\b(describe|test)"
|
|
24
|
+
- statement: "\\b(expected|assert|assert_raise|assert_in_delta|assert_received|catch_error|catch_throw|flunk|refute|refute_in_delta|refute_received)\\b"
|
|
25
|
+
- symbol.tag: "^\\s*\\@[a-zA-Z][a-zA-Z0-9_]*\\b"
|
|
26
|
+
|
|
27
|
+
- identifier.macro: "\\b(__CALLER__|__DIR__|__ENV__|__MODULE__|__aliases__|__block__|defmacro)\\b"
|
|
28
|
+
|
|
29
|
+
- todo: "(XXX|TODO|FIXME|\\?\\?\\?)"
|
|
30
|
+
- preproc.shebang: "\\W*#!.+?( |$)"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
filetype: fish
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "\\.fish$"
|
|
5
|
+
header: "^#!.*/(env +)?fish( |$)"
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
# Numbers
|
|
9
|
+
- constant: "\\b[0-9]+\\b"
|
|
10
|
+
|
|
11
|
+
# Conditionals and control flow
|
|
12
|
+
- statement: "\\b(and|begin|break|case|continue|else|end|for|function|if|in|not|or|return|select|shift|switch|while)\\b"
|
|
13
|
+
- special: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|^|!|=|&|\\|)"
|
|
14
|
+
|
|
15
|
+
# Fish commands
|
|
16
|
+
- type: "\\b(bg|bind|block|breakpoint|builtin|cd|count|command|commandline|complete|dirh|dirs|echo|emit|eval|exec|exit|fg|fish|fish_config|fish_ident|fish_pager|fish_prompt|fish_right_prompt|fish_update_completions|fishd|funced|funcsave|functions|help|history|jobs|math|mimedb|nextd|open|popd|prevd|psub|pushd|pwd|random|read|set|set_color|source|status|string|trap|type|ulimit|umask|vared)\\b"
|
|
17
|
+
|
|
18
|
+
# Common linux commands
|
|
19
|
+
- type: "\\b((g|ig)?awk|bash|dash|find|\\w{0,4}grep|kill|killall|\\w{0,4}less|make|pkill|sed|sh|tar)\\b"
|
|
20
|
+
|
|
21
|
+
# Coreutils commands
|
|
22
|
+
- type: "\\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|time|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\\b"
|
|
23
|
+
|
|
24
|
+
# Conditional flags
|
|
25
|
+
- statement: "--[a-z-]+"
|
|
26
|
+
- statement: "\\ -[a-z]+"
|
|
27
|
+
|
|
28
|
+
- identifier: "(?i)\\{?\\$[0-9A-Z_!@#$*?-]+\\}?"
|
|
29
|
+
|
|
30
|
+
- constant.string:
|
|
31
|
+
start: '"'
|
|
32
|
+
end: '"'
|
|
33
|
+
skip: "\\\\."
|
|
34
|
+
rules:
|
|
35
|
+
- constant.specialChar: "\\\\."
|
|
36
|
+
|
|
37
|
+
- constant.string:
|
|
38
|
+
start: "'"
|
|
39
|
+
end: "'"
|
|
40
|
+
skip: "\\\\."
|
|
41
|
+
rules: []
|
|
42
|
+
|
|
43
|
+
- comment:
|
|
44
|
+
start: "#"
|
|
45
|
+
end: "$"
|
|
46
|
+
rules:
|
|
47
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
filetype: go
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "\\.go$"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
# Conditionals and control flow
|
|
8
|
+
- special: "\\b(break|case|continue|default|go|goto|range|return|println|fallthrough)\\b"
|
|
9
|
+
- statement: "\\b(else|for|if|switch|select)\\b"
|
|
10
|
+
- preproc: "\\b(package|import|const|var|type|struct|func|defer|iota|make|new|copy|len|cap|panic|append|close|delete|print|recover)\\b"
|
|
11
|
+
- symbol.operator: "[-+/*=<>!~%&|^]|:="
|
|
12
|
+
|
|
13
|
+
# Types
|
|
14
|
+
- symbol: "(,|\\.)"
|
|
15
|
+
- type: "\\b(u?int(8|16|32|64)?|float(32|64)|complex(64|128))\\b"
|
|
16
|
+
- type: "\\b(uintptr|byte|rune|string|interface|bool|map|chan|error)\\b"
|
|
17
|
+
- type.keyword: "\\b(struct)\\b"
|
|
18
|
+
- constant.bool: "\\b(true|false|nil)\\b"
|
|
19
|
+
|
|
20
|
+
# Brackets
|
|
21
|
+
- symbol.brackets: "(\\{|\\})"
|
|
22
|
+
- symbol.brackets: "(\\(|\\))"
|
|
23
|
+
- symbol.brackets: "(\\[|\\])"
|
|
24
|
+
|
|
25
|
+
# Numbers and strings
|
|
26
|
+
- constant.number: "\\b([0-9]+|0x[0-9a-fA-F]*)\\b|'.'"
|
|
27
|
+
|
|
28
|
+
- constant.string:
|
|
29
|
+
start: '"'
|
|
30
|
+
end: '"'
|
|
31
|
+
skip: "\\\\."
|
|
32
|
+
rules:
|
|
33
|
+
- constant.specialChar: "%."
|
|
34
|
+
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
|
|
35
|
+
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
|
|
36
|
+
|
|
37
|
+
- constant.string:
|
|
38
|
+
start: "'"
|
|
39
|
+
end: "'"
|
|
40
|
+
skip: "\\\\."
|
|
41
|
+
rules:
|
|
42
|
+
- error: "..+"
|
|
43
|
+
- constant.specialChar: "%."
|
|
44
|
+
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
|
|
45
|
+
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
|
|
46
|
+
|
|
47
|
+
- constant.string:
|
|
48
|
+
start: "`"
|
|
49
|
+
end: "`"
|
|
50
|
+
rules: []
|
|
51
|
+
|
|
52
|
+
- comment:
|
|
53
|
+
start: "//"
|
|
54
|
+
end: "$"
|
|
55
|
+
rules:
|
|
56
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
57
|
+
|
|
58
|
+
- comment:
|
|
59
|
+
start: "/\\*"
|
|
60
|
+
end: "\\*/"
|
|
61
|
+
rules:
|
|
62
|
+
- todo: "(TODO|XXX|FIXME):?"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
filetype: html
|
|
2
|
+
|
|
3
|
+
detect:
|
|
4
|
+
filename: "\\.htm[l]?$"
|
|
5
|
+
|
|
6
|
+
rules:
|
|
7
|
+
# Doctype is case-insensitive
|
|
8
|
+
- preproc: "<!(?i)(DOCTYPE html.*)>"
|
|
9
|
+
# Opening tag
|
|
10
|
+
- symbol.tag:
|
|
11
|
+
start: "<(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|main|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
|
12
|
+
end: ">"
|
|
13
|
+
rules:
|
|
14
|
+
- identifier: "\\b(placeholder|style|alt|bgcolor|height|href|id|(aria|data)\\-.+|label|longdesc|name|on(click|focus|load|mouseover)|size|span|src|target|type|value|width|class|charset|content|rel|integrity|crossorigin|for|onsubmit|lang|role)\\b"
|
|
15
|
+
- special: "\\b(required)\\b"
|
|
16
|
+
# Match double-quote strings
|
|
17
|
+
- constant.string:
|
|
18
|
+
start: '"'
|
|
19
|
+
end: '"'
|
|
20
|
+
skip: "\\\\."
|
|
21
|
+
rules:
|
|
22
|
+
- constant.specialChar: "\\\\."
|
|
23
|
+
- constant.string.url: "((ftp(s)?|http(s)?|git|chrome)://[^\\s]+)"
|
|
24
|
+
# Match single-quote strings
|
|
25
|
+
- constant.string:
|
|
26
|
+
start: "'"
|
|
27
|
+
end: "'"
|
|
28
|
+
skip: "\\\\."
|
|
29
|
+
rules:
|
|
30
|
+
- constant.specialChar: "\\\\."
|
|
31
|
+
- constant.string.url: "((ftp(s)?|http(s)?|git|chrome)://[^\\s]+)"
|
|
32
|
+
# Highlight the equals and any colon between words
|
|
33
|
+
- symbol: "\\b(=|:\\b)"
|
|
34
|
+
|
|
35
|
+
# Closing tag
|
|
36
|
+
- symbol.tag:
|
|
37
|
+
start: "</(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|main|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
|
38
|
+
end: ">"
|
|
39
|
+
rules:
|
|
40
|
+
# Anything in the closing tag is an error
|
|
41
|
+
- error: "."
|
|
42
|
+
|
|
43
|
+
# Reserved entities like a and Ī
|
|
44
|
+
- special: "(([a-zA-Z]&#[0-9]+|&[a-zA-Z]+|&#[a-zA-Z0-9]+);)"
|
|
45
|
+
|
|
46
|
+
# TODO: Add `limit-rules` to both the `default` rules below once it's implemented into Micro
|
|
47
|
+
- default:
|
|
48
|
+
start: "<script.*?>"
|
|
49
|
+
end: "</script.*?>"
|
|
50
|
+
limit-group: symbol.tag
|
|
51
|
+
rules:
|
|
52
|
+
- include: "javascript"
|
|
53
|
+
|
|
54
|
+
- default:
|
|
55
|
+
start: "<style.*?>"
|
|
56
|
+
end: "</style.*?>"
|
|
57
|
+
limit-group: symbol.tag
|
|
58
|
+
rules:
|
|
59
|
+
- include: "css"
|
|
60
|
+
|
|
61
|
+
# This weird empty comment thing is technically valid
|
|
62
|
+
- comment: "<!>"
|
|
63
|
+
|
|
64
|
+
- comment.block:
|
|
65
|
+
start: "<!\\-\\-"
|
|
66
|
+
end: "\\-\\->"
|
|
67
|
+
rules:
|
|
68
|
+
- todo: "(FIXME|NOTE|TODO):?"
|
|
69
|
+
# While technically not a "true" error, these are recommended to not be used inside a comment
|
|
70
|
+
- error: "(\\-\\-|>)"
|