@cosense-toolbox/textmate 0.1.0-beta.5 → 0.1.0-beta.7

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 CHANGED
@@ -2,8 +2,10 @@
2
2
 
3
3
  > **beta.** スコープ名はまだ変わりえます。安定するまではバージョン固定での利用を推奨します。
4
4
 
5
- Cosense 記法の TextMate 文法。`.csn` 用の `cosense` と、コンポーネント行も読む `.csnx` 用の `cosenseX` がある。
6
- Shiki の `LanguageRegistration` としてそのまま渡せるほか、`.tmLanguage.json` としても同梱している。
5
+ Cosense 記法に対応する TextMate 文法です。`.csn` 用の `cosense` と、コンポーネント行も認識する `.csnx` 用の `cosenseX` を提供します。
6
+ Shiki の `LanguageRegistration` として使えるほか、`.tmLanguage.json` も同梱しています。
7
+
8
+ **ドキュメント → <https://cosense-toolbox.qaynam.dev/textmate/>**
7
9
 
8
10
  ```sh
9
11
  npm i @cosense-toolbox/textmate
@@ -35,27 +37,28 @@ VS Code 拡張の `contributes.grammars` などには、このファイルを指
35
37
  どのテーマでも色が付くように、スコープはテーマが既に塗っている名前から始めている
36
38
  (`markup.bold`、`string.other.link`、`markup.raw` など)。一覧は `SCOPES` で取れる。
37
39
 
38
- | 記法 | スコープ |
39
- | -------------------------------- | ------------------------------------------------------------------------------------- |
40
- | タイトル (1 行目) | `markup.heading.cosense` |
41
- | `[ページ]` | `string.other.link.internal.cosense` |
42
- | `[/project/ページ]` | `string.other.link.project.cosense` |
43
- | URL、`[ラベル URL]` | `markup.underline.link.external.cosense` |
44
- | 画像 | `markup.underline.link.image.cosense` |
45
- | `[user.icon]` | `string.other.link.icon.cosense` |
46
- | `#tag` | `entity.name.tag.hashtag.cosense` |
47
- | `` `code` `` | `markup.inline.raw.cosense` |
48
- | `code:` ブロック | `markup.raw.block.cosense` |
49
- | `table:` ブロック | `markup.other.table.cosense` |
50
- | `[$ 数式]` | `constant.other.formula.cosense` |
51
- | `>` 引用 | 行全体に `markup.quote.cosense`、記号に `punctuation.definition.quote.begin.cosense` |
52
- | `[* ]` / `[** ]` / `[*** ]` 以上 | `markup.bold.cosense` / `markup.bold.level2.cosense` / `markup.bold.level3.cosense` |
53
- | `[/ ]` `[- ]` `[_ ]` | `markup.italic.cosense` / `markup.strikethrough.cosense` / `markup.underline.cosense` |
54
- | 先頭の `---` で囲んだ YAML | `comment.block.frontmatter.cosense` |
55
- | コンポーネントのタグ名 (`.csnx`) | `support.class.component.cosense` |
56
- | 属性名 | `entity.other.attribute-name.cosense` |
57
- | 引用符で囲んだ属性値 | `string.quoted.attribute-value.cosense` |
58
- | `{ }` で囲んだ属性値 | `meta.embedded.expression.cosense` (中の数値は `constant.numeric.cosense`) |
40
+ | 記法 | スコープ |
41
+ | ---------------------------------------- | ------------------------------------------------------------------------------------- |
42
+ | タイトル (1 行目) | `markup.heading.cosense` |
43
+ | `[ページ]` | `string.other.link.internal.cosense` |
44
+ | `[/project/ページ]` | `string.other.link.project.cosense` |
45
+ | URL、`[ラベル URL]` | `markup.underline.link.external.cosense` |
46
+ | 画像 | `markup.underline.link.image.cosense` |
47
+ | 動画・音声・地図・YouTube などの埋め込み | `markup.underline.link.media.cosense` |
48
+ | `[user.icon]` | `string.other.link.icon.cosense` |
49
+ | `#tag` | `entity.name.tag.hashtag.cosense` |
50
+ | `` `code` `` | `markup.inline.raw.cosense` |
51
+ | `code:` ブロック | `markup.raw.block.cosense` |
52
+ | `table:` ブロック | `markup.other.table.cosense` |
53
+ | `[$ 数式]` | `constant.other.formula.cosense` |
54
+ | `>` 引用 | 行全体に `markup.quote.cosense`、記号に `punctuation.definition.quote.begin.cosense` |
55
+ | `[* ]` / `[** ]` / `[*** ]` 以上 | `markup.bold.cosense` / `markup.bold.level2.cosense` / `markup.bold.level3.cosense` |
56
+ | `[/ ]` `[- ]` `[_ ]` | `markup.italic.cosense` / `markup.strikethrough.cosense` / `markup.underline.cosense` |
57
+ | 先頭の `---` で囲んだ YAML | `comment.block.frontmatter.cosense` |
58
+ | コンポーネントのタグ名 (`.csnx`) | `support.class.component.cosense` |
59
+ | 属性名 | `entity.other.attribute-name.cosense` |
60
+ | 引用符で囲んだ属性値 | `string.quoted.attribute-value.cosense` |
61
+ | `{ }` で囲んだ属性値 | `meta.embedded.expression.cosense` (中の数値は `constant.numeric.cosense`) |
59
62
 
60
63
  `[-* x]` のように記号を重ねると、それぞれのスコープが全部付く。
61
64
 
package/dist/index.d.mts CHANGED
@@ -50,6 +50,7 @@ declare const SCOPES: {
50
50
  readonly strike: "markup.strikethrough.cosense";
51
51
  readonly underline: "markup.underline.cosense";
52
52
  readonly image: "markup.underline.link.image.cosense";
53
+ readonly media: "markup.underline.link.media.cosense";
53
54
  readonly table: "markup.other.table.cosense";
54
55
  readonly bold2: "markup.bold.level2.cosense";
55
56
  readonly bold3: "markup.bold.level3.cosense";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/grammar.ts","../src/scopes.ts","../src/index.ts"],"mappings":";;UAoBiB;EACf;EACA;EACA;EACA;EACA,WAAW,eAAe;EAC1B,gBAAgB,eAAe;EAC/B,cAAc,eAAe;EAC7B,WAAW;EACX;EACA;;;UAIe;EACf,MAAM;EACN;EACA,mBAAmB;EACnB;EACA;EACA,UAAU;EACV,YAAY,eAAe;;;KA8cjB;;;;;;;;;;;cCzeC;;;;;;;;;;;;;;;;;;;;;;;;;KA+BD,wBAAwB;;;cClCvB,SAAS;cAET,UAAU"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/grammar.ts","../src/scopes.ts","../src/index.ts"],"mappings":";;UAoBiB;EACf;EACA;EACA;EACA;EACA,WAAW,eAAe;EAC1B,gBAAgB,eAAe;EAC/B,cAAc,eAAe;EAC7B,WAAW;EACX;EACA;;;UAIe;EACf,MAAM;EACN;EACA,mBAAmB;EACnB;EACA;EACA,UAAU;EACV,YAAY,eAAe;;;KA4iBjB;;;;;;;;;;;cCvkBC;;;;;;;;;;;;;;;;;;;;;;;;;;KAgCD,wBAAwB;;;cCnCvB,SAAS;cAET,UAAU"}
package/dist/index.mjs CHANGED
@@ -24,6 +24,7 @@ const SCOPES = {
24
24
  strike: "markup.strikethrough.cosense",
25
25
  underline: "markup.underline.cosense",
26
26
  image: "markup.underline.link.image.cosense",
27
+ media: "markup.underline.link.media.cosense",
27
28
  table: "markup.other.table.cosense",
28
29
  bold2: "markup.bold.level2.cosense",
29
30
  bold3: "markup.bold.level3.cosense",
@@ -66,8 +67,39 @@ const IMAGE_URL = oneOf([
66
67
  re`(?i:https?)://[^\s\[\]#]*#[^\s\[\]]*${IMAGE_EXT}`
67
68
  ]);
68
69
  const URL = re`(?i:https?)://[^\s\[\]]+`;
70
+ const VIDEO_EXT = re`\.(?i:mp4|webm|mov)`;
71
+ /** A URL that is a video on its own (`[x]`, `[[x]]`): no query allowed. */
72
+ const VIDEO_URL = re`(?i:https?)://[^\s\[\]]+${VIDEO_EXT}`;
73
+ /** The video of a linked video (`[link video]`), which may carry a query. */
74
+ const LINKED_VIDEO_URL = re`(?i:https?)://[^\s\[\]]*${VIDEO_EXT}(?:\?[^\s\[\]]+)?`;
75
+ const AUDIO_URL = re`(?i:https?)://[^\s\[\]]*\.(?i:wav|mp3|weba|ogg|aac)`;
76
+ /** A map's coordinates (`N35.68,E139.76,Z14`): capital letters only, and no spaces. */
77
+ const COORDINATES = re`[NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?`;
78
+ /**
79
+ * A URL Cosense embeds a player for, as the parser reads them: YouTube (case-sensitive, as
80
+ * in Cosense Web), Vimeo, Spotify, and anchor.fm or its successor.
81
+ */
82
+ const EMBED_URL = oneOf([
83
+ re`https?://(?:www\.|music\.|)youtube\.com/watch\?(?:[^\s\[\]]+&|)v=[a-zA-Z\d_-]+(?:&[^\s\[\]]+|)`,
84
+ re`https?://youtu\.be/[a-zA-Z\d_-]+(?:\?[^\s\[\]]{0,100}|)`,
85
+ re`https?://(?:www\.|)youtube\.com/(?:shorts|live)/[a-zA-Z\d_-]+(?:\?[^\s\[\]]+|)`,
86
+ re`https?://(?:www\.|music\.|)youtube\.com/playlist\?(?:[^\s\[\]]+&|)list=[a-zA-Z\d_-]+(?:&[^\s\[\]]+|)`,
87
+ re`(?i:https?://vimeo\.com/[0-9]+(?:/[a-z0-9]+)?(?:\?[^\s\[\]]+|))`,
88
+ re`(?i:https?://open\.spotify\.com/(?:[^/\s\[\]]+/|)(?:track|artist|playlist|album|episode|show)/[a-zA-Z\d_-]+(?:\?[^\s\[\]]{0,100}|))`,
89
+ re`(?i:https?://(?:anchor\.fm|podcasters\.spotify\.com/pod/show)/[a-zA-Z\d_-]+/episodes/[a-zA-Z\d_-]+(?:/[a-zA-Z\d_-]+)?(?:\?[^\s\[\]]{0,100}|))`
90
+ ]);
69
91
  /** What a bracket must end with somewhere, or it is plain text and not worth a rule. */
70
92
  const CLOSES = re`(?=.*\])`;
93
+ /**
94
+ * No inline code starts inside the bracket that opens here. Cosense reads code before
95
+ * brackets, so a bracket with a code span starting inside it is no notation (`[a `b` c]`).
96
+ * A span starts at a backtick with another one anywhere after it. One level of brackets is
97
+ * stepped over, as emphasis holds them (`[* a [b] `c`]`). The backtick is written `\x60`,
98
+ * which both regex engines read the same.
99
+ */
100
+ const NO_CODE_INSIDE = re`(?!(?:\[[^\[\]]*\]|[^\]\x60])*\x60.*\x60)`;
101
+ /** The same for `[[x]]`, which closes on the first `]]`. */
102
+ const NO_CODE_INSIDE_DOUBLE = re`(?!(?:(?!\]\])[^\x60])*\x60.*\x60)`;
71
103
  /** A bracket closes on its own line or not at all. */
72
104
  const CLOSE_ON_LINE = re`\]|(?=$)`;
73
105
  /**
@@ -165,35 +197,50 @@ const markerConditions = (emphasis) => Regex([
165
197
  */
166
198
  const emphasisRules = Array.map(MARKER_SETS, (emphasis) => region({
167
199
  scopes: emphasisScopes(emphasis),
168
- begin: re`\[${markerConditions(emphasis)}(?=${MARKERS}+\s.*\])${MARKERS}+\s+`,
200
+ begin: re`\[${NO_CODE_INSIDE}${markerConditions(emphasis)}(?=${MARKERS}+\s.*\])${MARKERS}+\s+`,
169
201
  end: CLOSE_ON_LINE,
170
202
  patterns: [include("inline-in-emphasis"), include("nested-bracket")]
171
203
  }));
172
204
  /** Anything up to, but not over, a `]]`. */
173
205
  const UNTIL_DOUBLE_CLOSE = re`(?:(?!\]\]).)`;
174
- /** `[[x]]`: an image when x is one, bold otherwise. Closes on the first `]]`, depth or not. */
175
- const strongRules = [single(re`\[\[(?:${IMAGE_URL}|${UNTIL_DOUBLE_CLOSE}*?${IMAGE_EXT}(?:[?#]${UNTIL_DOUBLE_CLOSE}*)?)\]\]`, { scopes: [SCOPES.image] }), single(re`\[\[(${UNTIL_DOUBLE_CLOSE}+)\]\]`, {
176
- scopes: [SCOPES.bold],
177
- captures: { 1: readAs(include("inline-in-emphasis")) }
178
- })];
179
- /** `[$ x^2]`. Its body is TeX, not notation, and may hold brackets of its own. */
206
+ /**
207
+ * `[[x]]`: an image or a video when x is one, bold otherwise. Closes on the first `]]`, depth
208
+ * or not. A sound or an embedded player is not made large: it is a link inside bold.
209
+ */
210
+ const strongRules = [
211
+ single(re`\[\[${NO_CODE_INSIDE_DOUBLE}${VIDEO_URL}\]\]`, { scopes: [SCOPES.media] }),
212
+ single(re`\[\[${NO_CODE_INSIDE_DOUBLE}(?:${IMAGE_URL}|${UNTIL_DOUBLE_CLOSE}*?${IMAGE_EXT}(?:[?#]${UNTIL_DOUBLE_CLOSE}*)?)\]\]`, { scopes: [SCOPES.image] }),
213
+ single(re`\[\[${NO_CODE_INSIDE_DOUBLE}(${UNTIL_DOUBLE_CLOSE}+)\]\]`, {
214
+ scopes: [SCOPES.bold],
215
+ captures: { 1: readAs(include("inline-in-emphasis")) }
216
+ })
217
+ ];
218
+ /**
219
+ * `[$ x^2]`. Its body is TeX, not notation, and may hold brackets of its own. Like any
220
+ * bracket, it is no formula when inline code starts inside it (`[$ a``]`).
221
+ */
180
222
  const formulaRule = region({
181
223
  scopes: [SCOPES.formula],
182
- begin: re`\[(?=\$)${CLOSES}`,
224
+ begin: re`\[${NO_CODE_INSIDE}(?=\$)${CLOSES}`,
183
225
  end: CLOSE_ON_LINE,
184
226
  patterns: [include("bare-bracket")]
185
227
  });
186
228
  /**
187
- * Brackets whose body holds no brackets, tried in the parser's order: an icon before a
188
- * URL before an image path before a project link, and anything left is a page link.
229
+ * Brackets whose body holds no brackets, tried in the parser's order: an icon before media
230
+ * before a URL before an image path before a project link, and anything left is a page link.
189
231
  */
190
232
  const simpleTargetRules = (allowImagePath) => Array.getSomes([
191
- Option.some(single(re`\[[^\[\]]+\.icon(?:\*\d+)?\]`, { scopes: [SCOPES.icon] })),
192
- Option.some(single(re`\[\s*(?:${URL}\s+)*(?:${IMAGE_URL})(?=[\s\]])(?:\s+${URL})*\s*\]`, { scopes: [SCOPES.image] })),
193
- Option.some(single(re`\[[^\[\]]*${URL}[^\[\]]*\]`, { scopes: [SCOPES.externalLink] })),
194
- onlyIf(allowImagePath, single(re`\[[^\[\]]*${IMAGE_EXT}\]`, { scopes: [SCOPES.image] })),
195
- Option.some(single(re`\[/[^\[\]]*\]`, { scopes: [SCOPES.projectLink] })),
196
- Option.some(single(re`\[(?=[^\[\]]*[^\s\[\]])[^\[\]]+\]`, { scopes: [SCOPES.link] }))
233
+ Option.some(single(re`\[${NO_CODE_INSIDE}[^\[\]]+\.icon(?:\*\d+)?\]`, { scopes: [SCOPES.icon] })),
234
+ Option.some(single(re`\[${NO_CODE_INSIDE}(?:${COORDINATES}(?:\s+[^\[\]]+)?|[^\[\]]+?\s+${COORDINATES})\]`, { scopes: [SCOPES.media] })),
235
+ Option.some(single(re`\[${NO_CODE_INSIDE}(?:${EMBED_URL}|${VIDEO_URL}|${AUDIO_URL})\]`, { scopes: [SCOPES.media] })),
236
+ Option.some(single(re`\[${NO_CODE_INSIDE}(?:${URL}\s+${LINKED_VIDEO_URL}|${LINKED_VIDEO_URL}\s+${URL})\]`, { scopes: [SCOPES.media] })),
237
+ Option.some(single(re`\[${NO_CODE_INSIDE}${AUDIO_URL}\s+(?!\s*(?:${IMAGE_URL})\s*\])[^\[\]]*\]`, { scopes: [SCOPES.media] })),
238
+ Option.some(single(re`\[${NO_CODE_INSIDE}(?!\s*(?:${IMAGE_URL})\s+${AUDIO_URL}\])[^\[\]]+\s${AUDIO_URL}\]`, { scopes: [SCOPES.media] })),
239
+ Option.some(single(re`\[${NO_CODE_INSIDE}\s*(?:${URL}\s+)*(?:${IMAGE_URL})(?=[\s\]])(?:\s+${URL})*\s*\]`, { scopes: [SCOPES.image] })),
240
+ Option.some(single(re`\[${NO_CODE_INSIDE}[^\[\]]*${URL}[^\[\]]*\]`, { scopes: [SCOPES.externalLink] })),
241
+ onlyIf(allowImagePath, single(re`\[${NO_CODE_INSIDE}[^\[\]]*${IMAGE_EXT}\]`, { scopes: [SCOPES.image] })),
242
+ Option.some(single(re`\[${NO_CODE_INSIDE}/[^\[\]]*\]`, { scopes: [SCOPES.projectLink] })),
243
+ Option.some(single(re`\[${NO_CODE_INSIDE}(?=[^\[\]]*[^\s\[\]])[^\[\]]+\]`, { scopes: [SCOPES.link] }))
197
244
  ]);
198
245
  /** What follows the bracketed notation in both contexts, in the parser's order. */
199
246
  const unbracketedRules = [
@@ -273,6 +320,7 @@ const REPOSITORY = {
273
320
  single(re`\b(?:true|false|null|undefined)\b`, { scopes: ["constant.language.cosense"] })
274
321
  ]
275
322
  }),
323
+ command: single(re`^${INDENT}([$%] .+)$`, { captures: { 1: scoped(SCOPES.code) } }),
276
324
  quote: region({
277
325
  scopes: ["markup.quote.cosense"],
278
326
  begin: re`^${INDENT}(>[> ]*)`,
@@ -348,6 +396,7 @@ const linePatterns = (dialect) => Array.getSomes([
348
396
  Option.some(include("code-block")),
349
397
  Option.some(include("table-block")),
350
398
  onlyIf(dialect.components, include("component")),
399
+ Option.some(include("command")),
351
400
  Option.some(include("quote")),
352
401
  Option.some(include("inline"))
353
402
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["Arr","Rec"],"sources":["../src/scopes.ts","../src/grammar.ts","../src/index.ts"],"sourcesContent":["/**\n * A scope this grammar gives. Every one ends in `.cosense`, which is how a theme or a\n * reader tells them from the scopes of a language embedded next to Cosense.\n */\nexport type Scope = `${string}.cosense`\n\n/**\n * The TextMate scope each Cosense notation is drawn as.\n *\n * Keys are the token names @cosense-toolbox/lsp reasons in, so the two\n * highlighters describe the same things; the parity test holds them to it. Each scope\n * starts with a name themes already colour (`markup.bold`, `string.other.link`, ...), so\n * any Shiki or VS Code theme draws Cosense without knowing about it.\n */\nexport const SCOPES = {\n title: \"markup.heading.cosense\",\n link: \"string.other.link.internal.cosense\",\n projectLink: \"string.other.link.project.cosense\",\n externalLink: \"markup.underline.link.external.cosense\",\n hashtag: \"entity.name.tag.hashtag.cosense\",\n code: \"markup.inline.raw.cosense\",\n codeBlock: \"markup.raw.block.cosense\",\n formula: \"constant.other.formula.cosense\",\n icon: \"string.other.link.icon.cosense\",\n // Only the `>` marker, as the language server marks it. The whole line also carries\n // `markup.quote.cosense` so themes can tint it.\n quote: \"punctuation.definition.quote.begin.cosense\",\n bold: \"markup.bold.cosense\",\n italic: \"markup.italic.cosense\",\n strike: \"markup.strikethrough.cosense\",\n underline: \"markup.underline.cosense\",\n image: \"markup.underline.link.image.cosense\",\n table: \"markup.other.table.cosense\",\n // `[** x]` and louder: still bold to a theme, but a level apart for anyone who wants it.\n bold2: \"markup.bold.level2.cosense\",\n bold3: \"markup.bold.level3.cosense\",\n // `.csnx` only: the tag that opens a component line, read as JSX. The whole line also\n // carries `meta.tag.component.cosense`.\n component: \"support.class.component.cosense\",\n attribute: \"entity.other.attribute-name.cosense\",\n attributeValue: \"string.quoted.attribute-value.cosense\",\n expression: \"meta.embedded.expression.cosense\",\n frontmatter: \"comment.block.frontmatter.cosense\",\n} as const satisfies Record<string, Scope>\n\nexport type Notation = keyof typeof SCOPES\n","/**\n * The Cosense grammar, built from code rather than written as JSON: emphasis alone takes\n * one rule per combination of markers, and those are generated.\n *\n * The rules follow @cosense-toolbox/parser, and where they cannot (TextMate reads line by\n * line with regexes, the parser reads the whole page) they approximate it. The parity test\n * measures how close that is against the language server.\n *\n * Rules are first built as a tagged union (`Pattern`) and only then encoded as TextMate\n * JSON. The union is what keeps the grammar honest: a rule is a match or a region and never\n * half of each, every `include` names a repository entry that exists, and every scope ends\n * in `.cosense`. A typo in any of those fails to compile instead of silently colouring nothing.\n */\nimport { Array as Arr, Brand, Match, Option, pipe, Record as Rec } from \"effect\"\n\nimport { type Scope, SCOPES } from \"./scopes\"\n\n// --- The output: what Shiki and VS Code read ------------------------------------------\n\n/** A TextMate rule as JSON. Assignable to Shiki's and VS Code's own types. */\nexport interface Rule {\n name?: string\n match?: string\n begin?: string\n end?: string\n captures?: Record<string, Rule>\n beginCaptures?: Record<string, Rule>\n endCaptures?: Record<string, Rule>\n patterns?: Rule[]\n include?: string\n applyEndPatternLast?: boolean\n}\n\n/** A grammar as Shiki takes it (`LanguageRegistration`), which is also a `.tmLanguage.json`. */\nexport interface Grammar {\n name: Dialect\n displayName: string\n scopeName: `text.${Dialect}`\n aliases: string[]\n fileTypes: string[]\n patterns: Rule[]\n repository: Record<string, Rule>\n}\n\n// --- Regexes ----------------------------------------------------------------------------\n\n/** A regex in Oniguruma syntax. Branded so a scope or a label cannot be passed as one. */\ntype Regex = string & Brand.Brand<\"Regex\">\nconst Regex = Brand.nominal<Regex>()\n\n/** A regex written raw: backslashes are the regex's, not the string literal's. */\nconst re = (strings: TemplateStringsArray, ...parts: ReadonlyArray<Regex | number>): Regex =>\n Regex(String.raw(strings, ...parts))\n\nconst oneOf = (alternatives: Arr.NonEmptyReadonlyArray<Regex>): Regex =>\n Regex(alternatives.map((alternative) => `(?:${alternative})`).join(\"|\"))\n\n/** Indentation, as the parser counts it: full-width spaces (U+3000) indent too. */\nconst INDENT = re`[ \\t\\u3000]*`\n\nconst IMAGE_EXT = re`\\.(?i:png|jpe?g|gif|webp|svg|bmp|avif)`\n\n/**\n * A URL that is drawn as an image, inside a bracket (so without `[`, `]` or spaces):\n * a Gyazo page, a path ending in an image extension before any query, or a fragment\n * ending in one (`#.svg`, Cosense's way of saying \"this is an image\").\n */\nconst IMAGE_URL = oneOf([\n re`(?i:https?://(?:i\\.)?gyazo\\.com/[0-9a-f]{20,})[^\\s\\[\\]]*`,\n re`(?i:https?)://[^\\s\\[\\]?#]*${IMAGE_EXT}(?:[?#][^\\s\\[\\]]*)?`,\n re`(?i:https?)://[^\\s\\[\\]#]*#[^\\s\\[\\]]*${IMAGE_EXT}`,\n])\n\nconst URL = re`(?i:https?)://[^\\s\\[\\]]+`\n\n/** What a bracket must end with somewhere, or it is plain text and not worth a rule. */\nconst CLOSES = re`(?=.*\\])`\n\n/** A bracket closes on its own line or not at all. */\nconst CLOSE_ON_LINE = re`\\]|(?=$)`\n\n/**\n * A decoration marker: any of Cosense's, as the parser reads them. Only `* / - _` have a look\n * of their own; the rest (`[! 注意]`) still make the bracket a decoration and not a link.\n */\nconst MARKERS = re`[!\"#%&'()*+,\\-./{|}<>_~=]`\n\nconst NOT_STAR = re`[!\"#%&'()+,\\-./{|}<>_~=]`\n\n/**\n * A line that opens with a component tag (`.csnx`), as the language server tells one:\n * `<` or `</`, a capitalised name, then a space or the end of the tag.\n */\nconst COMPONENT_LINE = re`\\s*</?[A-Z][A-Za-z0-9_.]*(?:[^\\S\\n]|/?>)`\n\n// --- Rules, typed -----------------------------------------------------------------------\n\ntype RepositoryKey =\n | \"head\"\n | \"frontmatter\"\n | \"code-block\"\n | \"table-block\"\n | \"component\"\n | \"component-tag\"\n | \"expression\"\n | \"quote\"\n | \"inline\"\n | \"inline-in-emphasis\"\n | \"nested-bracket\"\n | \"bare-bracket\"\n\n/** `0` is the whole match; this grammar never needs more than three groups. */\ntype CaptureGroup = \"0\" | \"1\" | \"2\" | \"3\"\n\ninterface Capture {\n readonly scopes: ReadonlyArray<Scope>\n readonly patterns: ReadonlyArray<Pattern>\n}\n\ntype Captures = Readonly<Partial<Record<CaptureGroup, Capture>>>\n\ninterface Include {\n readonly _tag: \"Include\"\n readonly key: RepositoryKey\n}\n\n/** A single-line match. */\ninterface Single {\n readonly _tag: \"Single\"\n readonly scopes: ReadonlyArray<Scope>\n readonly regex: Regex\n readonly captures: Captures\n}\n\n/** From `begin` to `end`, with `patterns` tried in between. */\ninterface Region {\n readonly _tag: \"Region\"\n readonly scopes: ReadonlyArray<Scope>\n readonly begin: Regex\n readonly end: Regex\n readonly beginCaptures: Captures\n readonly endCaptures: Captures\n readonly patterns: ReadonlyArray<Pattern>\n /** Try `patterns` before `end` when both match at the same place. */\n readonly endPatternLast: boolean\n}\n\n/** Several patterns tried in order, with no scope of their own. */\ninterface Group {\n readonly _tag: \"Group\"\n readonly patterns: ReadonlyArray<Pattern>\n}\n\ntype Pattern = Include | Single | Region | Group\n\nconst include = (key: RepositoryKey): Pattern => ({ _tag: \"Include\", key })\n\nconst single = (\n regex: Regex,\n options: { readonly scopes?: ReadonlyArray<Scope>; readonly captures?: Captures } = {},\n): Pattern => ({\n _tag: \"Single\",\n regex,\n scopes: options.scopes ?? [],\n captures: options.captures ?? {},\n})\n\nconst region = (options: {\n readonly begin: Regex\n readonly end: Regex\n readonly scopes?: ReadonlyArray<Scope>\n readonly beginCaptures?: Captures\n readonly endCaptures?: Captures\n readonly patterns?: ReadonlyArray<Pattern>\n readonly endPatternLast?: boolean\n}): Pattern => ({\n _tag: \"Region\",\n begin: options.begin,\n end: options.end,\n scopes: options.scopes ?? [],\n beginCaptures: options.beginCaptures ?? {},\n endCaptures: options.endCaptures ?? {},\n patterns: options.patterns ?? [],\n endPatternLast: options.endPatternLast ?? false,\n})\n\nconst group = (patterns: ReadonlyArray<Pattern>): Pattern => ({ _tag: \"Group\", patterns })\n\nconst scoped = (...scopes: ReadonlyArray<Scope>): Capture => ({ scopes, patterns: [] })\n\nconst readAs = (...patterns: ReadonlyArray<Pattern>): Capture => ({ scopes: [], patterns })\n\nconst onlyIf = <A>(condition: boolean, value: A): Option.Option<A> =>\n condition ? Option.some(value) : Option.none()\n\n// --- Inline notation --------------------------------------------------------------------\n\ntype Weight = 0 | 1 | 2 | 3\n\n/** One combination of emphasis markers: how many `*`, and which of `/`, `-`, `_`. */\ninterface Emphasis {\n readonly stars: Weight\n readonly italic: boolean\n readonly strike: boolean\n readonly underline: boolean\n}\n\nconst WEIGHTS: ReadonlyArray<Weight> = [0, 1, 2, 3]\n\nconst SWITCHES = [false, true] as const\n\n/**\n * Every combination of markers. The one with none of `* / - _` is a run of the others alone\n * (`[! x]`), which colours nothing but still keeps the bracket from reading as a link.\n */\nconst MARKER_SETS: ReadonlyArray<Emphasis> = pipe(\n WEIGHTS,\n Arr.flatMap((stars) => Arr.map(SWITCHES, (italic) => ({ stars, italic }))),\n Arr.flatMap((set) => Arr.map(SWITCHES, (strike) => ({ ...set, strike }))),\n Arr.flatMap((set) => Arr.map(SWITCHES, (underline) => ({ ...set, underline }))),\n)\n\n/** `[*** x]` and louder are one size: Cosense caps the size, and so does the parser. */\nconst WEIGHT_SCOPE: { readonly [W in Weight]: Option.Option<Scope> } = {\n 0: Option.none(),\n 1: Option.some(SCOPES.bold),\n 2: Option.some(SCOPES.bold2),\n 3: Option.some(SCOPES.bold3),\n}\n\n/** All the scopes a run carries, since Cosense applies every marker: `[-* x]` is both. */\nconst emphasisScopes = (emphasis: Emphasis): ReadonlyArray<Scope> =>\n Arr.getSomes([\n onlyIf(emphasis.underline, SCOPES.underline),\n onlyIf(emphasis.strike, SCOPES.strike),\n onlyIf(emphasis.italic, SCOPES.italic),\n WEIGHT_SCOPE[emphasis.stars],\n ])\n\n/** A lookahead over the marker run (`[-** x]` -> `-**`) that holds for exactly this many `*`. */\nconst starCondition = (stars: Weight): Regex =>\n Match.value(stars).pipe(\n Match.when(0, () => re`(?!${MARKERS}*?\\*)`),\n Match.when(3, () => re`(?=(?:${NOT_STAR}*\\*){3})`),\n Match.orElse((count) => re`(?=(?:${NOT_STAR}*\\*){${count}}${NOT_STAR}*\\s)`),\n )\n\nconst markerCondition = (marker: Regex, wanted: boolean): Regex =>\n wanted ? re`(?=${MARKERS}*?${marker})` : re`(?!${MARKERS}*?${marker})`\n\n/** Lookaheads over the marker run that hold only for this combination. */\nconst markerConditions = (emphasis: Emphasis): Regex =>\n Regex(\n [\n starCondition(emphasis.stars),\n markerCondition(re`/`, emphasis.italic),\n markerCondition(re`\\-`, emphasis.strike),\n markerCondition(re`_`, emphasis.underline),\n ].join(\"\"),\n )\n\n/**\n * `[<markers> body]`, one rule per combination. The body is read again for links and icons\n * but not for emphasis, which does not nest in Cosense (`[* [* x]]` is bold around a link).\n */\nconst emphasisRules: ReadonlyArray<Pattern> = Arr.map(MARKER_SETS, (emphasis) =>\n region({\n scopes: emphasisScopes(emphasis),\n begin: re`\\[${markerConditions(emphasis)}(?=${MARKERS}+\\s.*\\])${MARKERS}+\\s+`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"inline-in-emphasis\"), include(\"nested-bracket\")],\n }),\n)\n\n/** Anything up to, but not over, a `]]`. */\nconst UNTIL_DOUBLE_CLOSE = re`(?:(?!\\]\\]).)`\n\n/** `[[x]]`: an image when x is one, bold otherwise. Closes on the first `]]`, depth or not. */\nconst strongRules: ReadonlyArray<Pattern> = [\n single(\n re`\\[\\[(?:${IMAGE_URL}|${UNTIL_DOUBLE_CLOSE}*?${IMAGE_EXT}(?:[?#]${UNTIL_DOUBLE_CLOSE}*)?)\\]\\]`,\n { scopes: [SCOPES.image] },\n ),\n single(re`\\[\\[(${UNTIL_DOUBLE_CLOSE}+)\\]\\]`, {\n scopes: [SCOPES.bold],\n captures: { 1: readAs(include(\"inline-in-emphasis\")) },\n }),\n]\n\n/** `[$ x^2]`. Its body is TeX, not notation, and may hold brackets of its own. */\nconst formulaRule: Pattern = region({\n scopes: [SCOPES.formula],\n begin: re`\\[(?=\\$)${CLOSES}`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"bare-bracket\")],\n})\n\n/**\n * Brackets whose body holds no brackets, tried in the parser's order: an icon before a\n * URL before an image path before a project link, and anything left is a page link.\n */\nconst simpleTargetRules = (allowImagePath: boolean): ReadonlyArray<Pattern> =>\n Arr.getSomes([\n Option.some(single(re`\\[[^\\[\\]]+\\.icon(?:\\*\\d+)?\\]`, { scopes: [SCOPES.icon] })),\n // Only URLs, and one of them an image: the image, linking to another URL if there is one.\n Option.some(\n single(re`\\[\\s*(?:${URL}\\s+)*(?:${IMAGE_URL})(?=[\\s\\]])(?:\\s+${URL})*\\s*\\]`, {\n scopes: [SCOPES.image],\n }),\n ),\n // Any other URL makes a link, labelled or not.\n Option.some(single(re`\\[[^\\[\\]]*${URL}[^\\[\\]]*\\]`, { scopes: [SCOPES.externalLink] })),\n // `[a.png]`, but not inside emphasis: there Cosense links to a page of that name.\n onlyIf(allowImagePath, single(re`\\[[^\\[\\]]*${IMAGE_EXT}\\]`, { scopes: [SCOPES.image] })),\n Option.some(single(re`\\[/[^\\[\\]]*\\]`, { scopes: [SCOPES.projectLink] })),\n Option.some(single(re`\\[(?=[^\\[\\]]*[^\\s\\[\\]])[^\\[\\]]+\\]`, { scopes: [SCOPES.link] })),\n ])\n\n/** What follows the bracketed notation in both contexts, in the parser's order. */\nconst unbracketedRules: ReadonlyArray<Pattern> = [\n single(Regex(\"`[^`]*`\"), { scopes: [SCOPES.code] }),\n // `#tag`, only at the start or after a space: a `#` inside a word is just a character.\n single(re`(?:^|(?<=[ \\t\\u3000]))#[^\\s\\[\\]#]+`, { scopes: [SCOPES.hashtag] }),\n // A URL outside brackets is always a link, even to an image.\n single(re`(?i:https?)://[^\\s\\]]+`, { scopes: [SCOPES.externalLink] }),\n]\n\n// --- Lines and blocks -------------------------------------------------------------------\n\n/**\n * A block is its header and every line indented deeper. Deeper is approximated as\n * \"the header's indentation, then more\", which is exact when a page indents one way.\n */\nconst block = (keyword: \"code\" | \"table\", scope: Scope, patterns: ReadonlyArray<Pattern>) =>\n region({\n scopes: [scope],\n begin: re`^(${INDENT})(${Regex(keyword)}:)(.+)$`,\n beginCaptures: {\n 2: scoped(\"keyword.other.block.cosense\"),\n 3: scoped(\"entity.name.section.block.cosense\"),\n },\n end: re`^(?!\\1[ \\t\\u3000])`,\n patterns,\n })\n\n/**\n * The first line is the title, unless the file opens with YAML: then it is the first line\n * after the fence. Nothing on the title line is notation. In `.csnx` a first line that is\n * a component tag is that tag, and the page has no title line.\n */\nconst head = (dialect: DialectInfo): Pattern => {\n const notComponent = dialect.components ? re`(?!${COMPONENT_LINE})` : re``\n return group([\n region({\n begin: re`\\A(?=---[ \\t]*$)`,\n patterns: [include(\"frontmatter\")],\n // Ends on the line after the fence either way; only a title line is captured.\n end: dialect.components ? re`^(?:(?=${COMPONENT_LINE})|(.*)$)` : re`^(.*)$`,\n endCaptures: { 1: scoped(SCOPES.title) },\n // At the very start the fence and the title line both match: the fence wins.\n endPatternLast: true,\n }),\n single(re`\\A${notComponent}.*$`, { scopes: [SCOPES.title] }),\n ])\n}\n\n/** Every other entry the grammar can `include`. The mapped type makes a missing one an error. */\nconst REPOSITORY: { readonly [K in Exclude<RepositoryKey, \"head\">]: Pattern } = {\n frontmatter: region({\n scopes: [SCOPES.frontmatter],\n begin: re`\\A---[ \\t]*$`,\n end: re`^---[ \\t]*$`,\n }),\n \"code-block\": block(\"code\", SCOPES.codeBlock, []),\n \"table-block\": block(\"table\", SCOPES.table, [\n single(re`\\t`, { scopes: [\"punctuation.separator.table-cell.cosense\"] }),\n ]),\n // A component tag owns its line: nothing on it is Cosense notation, and the text after\n // the tag is plain.\n component: region({\n scopes: [\"meta.tag.component.cosense\"],\n begin: re`^(?=${COMPONENT_LINE})`,\n end: re`$`,\n patterns: [include(\"component-tag\")],\n }),\n // The tag itself, read as JSX.\n \"component-tag\": region({\n begin: re`^(\\s*)(</?)([A-Z][A-Za-z0-9_.]*)`,\n beginCaptures: {\n 2: scoped(\"punctuation.definition.tag.begin.cosense\"),\n 3: scoped(SCOPES.component),\n },\n end: re`(/?>)|(?=$)`,\n endCaptures: { 1: scoped(\"punctuation.definition.tag.end.cosense\") },\n patterns: [\n single(re`[A-Za-z_:][A-Za-z0-9_:.\\-]*`, { scopes: [SCOPES.attribute] }),\n single(re`=`, { scopes: [\"punctuation.separator.key-value.cosense\"] }),\n single(re`\"[^\"]*(?:\"|$)|'[^']*(?:'|$)`, { scopes: [SCOPES.attributeValue] }),\n include(\"expression\"),\n ],\n }),\n // `{...}`: a JavaScript expression. Braces nest; a few literals are told apart.\n expression: region({\n scopes: [SCOPES.expression],\n begin: re`\\{`,\n end: re`\\}|(?=$)`,\n beginCaptures: { 0: scoped(\"punctuation.section.embedded.begin.cosense\") },\n endCaptures: { 0: scoped(\"punctuation.section.embedded.end.cosense\") },\n patterns: [\n include(\"expression\"),\n single(re`\"[^\"]*\"|'[^']*'`, { scopes: [\"string.quoted.cosense\"] }),\n single(re`\\b\\d+(?:\\.\\d+)?\\b`, { scopes: [\"constant.numeric.cosense\"] }),\n single(re`\\b(?:true|false|null|undefined)\\b`, { scopes: [\"constant.language.cosense\"] }),\n ],\n }),\n quote: region({\n scopes: [\"markup.quote.cosense\"],\n begin: re`^${INDENT}(>[> ]*)`,\n beginCaptures: { 1: scoped(SCOPES.quote) },\n end: re`$`,\n patterns: [include(\"inline\")],\n }),\n inline: group([\n ...strongRules,\n formulaRule,\n ...emphasisRules,\n ...simpleTargetRules(true),\n ...unbracketedRules,\n ]),\n \"inline-in-emphasis\": group([\n ...strongRules,\n formulaRule,\n ...simpleTargetRules(false),\n ...unbracketedRules,\n ]),\n // A bracket that is no notation, but whose `]` must not close the one around it:\n // in `[* a [] b]` the emphasis runs to the last `]`, as the parser counts depth.\n \"nested-bracket\": region({\n begin: re`\\[`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"inline-in-emphasis\"), include(\"nested-bracket\")],\n }),\n \"bare-bracket\": region({\n begin: re`\\[`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"bare-bracket\")],\n }),\n}\n\n// --- Encoding ---------------------------------------------------------------------------\n\n/** A field only when there is something to put in it: TextMate reads absent and empty alike. */\nconst field = <K extends keyof Rule>(key: K, value: Option.Option<Rule[K]>): Partial<Rule> =>\n Option.match(value, { onNone: () => ({}), onSome: (v) => ({ [key]: v }) })\n\nconst nonEmpty = <A>(items: ReadonlyArray<A>): Option.Option<ReadonlyArray<A>> =>\n Option.liftPredicate(items, (list) => list.length > 0)\n\n/** TextMate gives a rule several scopes by separating them with spaces. */\nconst nameField = (scopes: ReadonlyArray<Scope>): Partial<Rule> =>\n field(\n \"name\",\n Option.map(nonEmpty(scopes), (list) => list.join(\" \")),\n )\n\nconst patternsField = (patterns: ReadonlyArray<Pattern>): Partial<Rule> =>\n field(\"patterns\", Option.map(nonEmpty(patterns), Arr.map(encode)))\n\nconst encodeCaptures = (captures: Captures): Option.Option<Record<string, Rule>> =>\n pipe(\n Object.entries(captures),\n Arr.map(\n ([group, capture]) =>\n [group, { ...nameField(capture.scopes), ...patternsField(capture.patterns) }] as const,\n ),\n nonEmpty,\n Option.map(Rec.fromEntries),\n )\n\nconst encode = (pattern: Pattern): Rule =>\n Match.value(pattern).pipe(\n Match.tag(\"Include\", ({ key }) => ({ include: `#${key}` })),\n Match.tag(\"Group\", ({ patterns }) => patternsField(patterns)),\n Match.tag(\"Single\", (rule) => ({\n ...nameField(rule.scopes),\n match: rule.regex,\n ...field(\"captures\", encodeCaptures(rule.captures)),\n })),\n Match.tag(\"Region\", (rule) => ({\n ...nameField(rule.scopes),\n begin: rule.begin,\n end: rule.end,\n ...field(\"beginCaptures\", encodeCaptures(rule.beginCaptures)),\n ...field(\"endCaptures\", encodeCaptures(rule.endCaptures)),\n ...patternsField(rule.patterns),\n ...field(\"applyEndPatternLast\", onlyIf(rule.endPatternLast, true)),\n })),\n Match.exhaustive,\n )\n\n// --- Dialects ---------------------------------------------------------------------------\n\n/** `.csn`, and `.csnx`, which also reads a line that is one component tag. */\nexport type Dialect = \"cosense\" | \"cosense-x\"\n\ninterface DialectInfo {\n readonly displayName: string\n readonly extension: string\n readonly components: boolean\n}\n\nconst DIALECTS: { readonly [D in Dialect]: DialectInfo } = {\n cosense: { displayName: \"Cosense\", extension: \"csn\", components: false },\n \"cosense-x\": { displayName: \"Cosense X\", extension: \"csnx\", components: true },\n}\n\n/** What a line can be, in the order the parser decides it. */\nconst linePatterns = (dialect: DialectInfo): ReadonlyArray<Pattern> =>\n Arr.getSomes([\n Option.some(include(\"head\")),\n Option.some(include(\"code-block\")),\n Option.some(include(\"table-block\")),\n onlyIf(dialect.components, include(\"component\")),\n Option.some(include(\"quote\")),\n Option.some(include(\"inline\")),\n ])\n\nexport const buildGrammar = (dialect: Dialect): Grammar => {\n const info = DIALECTS[dialect]\n return {\n name: dialect,\n displayName: info.displayName,\n scopeName: `text.${dialect}`,\n aliases: [info.extension],\n fileTypes: [info.extension],\n patterns: Arr.map(linePatterns(info), encode),\n repository: Rec.map({ head: head(info), ...REPOSITORY }, encode),\n }\n}\n","/**\n * TextMate grammars for Cosense notation: `.csn` and, with component lines, `.csnx`.\n *\n * Each export is a Shiki `LanguageRegistration` as it stands, and the same objects are\n * published as `.tmLanguage.json` for editors that read TextMate grammars.\n */\nimport { buildGrammar, type Grammar } from \"./grammar\"\n\nexport type { Dialect, Grammar, Rule } from \"./grammar\"\nexport { type Notation, SCOPES } from \"./scopes\"\n\nexport const cosense: Grammar = buildGrammar(\"cosense\")\n\nexport const cosenseX: Grammar = buildGrammar(\"cosense-x\")\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["Arr","Rec"],"sources":["../src/scopes.ts","../src/grammar.ts","../src/index.ts"],"sourcesContent":["/**\n * A scope this grammar gives. Every one ends in `.cosense`, which is how a theme or a\n * reader tells them from the scopes of a language embedded next to Cosense.\n */\nexport type Scope = `${string}.cosense`\n\n/**\n * The TextMate scope each Cosense notation is drawn as.\n *\n * Keys are the token names @cosense-toolbox/lsp reasons in, so the two\n * highlighters describe the same things; the parity test holds them to it. Each scope\n * starts with a name themes already colour (`markup.bold`, `string.other.link`, ...), so\n * any Shiki or VS Code theme draws Cosense without knowing about it.\n */\nexport const SCOPES = {\n title: \"markup.heading.cosense\",\n link: \"string.other.link.internal.cosense\",\n projectLink: \"string.other.link.project.cosense\",\n externalLink: \"markup.underline.link.external.cosense\",\n hashtag: \"entity.name.tag.hashtag.cosense\",\n code: \"markup.inline.raw.cosense\",\n codeBlock: \"markup.raw.block.cosense\",\n formula: \"constant.other.formula.cosense\",\n icon: \"string.other.link.icon.cosense\",\n // Only the `>` marker, as the language server marks it. The whole line also carries\n // `markup.quote.cosense` so themes can tint it.\n quote: \"punctuation.definition.quote.begin.cosense\",\n bold: \"markup.bold.cosense\",\n italic: \"markup.italic.cosense\",\n strike: \"markup.strikethrough.cosense\",\n underline: \"markup.underline.cosense\",\n image: \"markup.underline.link.image.cosense\",\n media: \"markup.underline.link.media.cosense\",\n table: \"markup.other.table.cosense\",\n // `[** x]` and louder: still bold to a theme, but a level apart for anyone who wants it.\n bold2: \"markup.bold.level2.cosense\",\n bold3: \"markup.bold.level3.cosense\",\n // `.csnx` only: the tag that opens a component line, read as JSX. The whole line also\n // carries `meta.tag.component.cosense`.\n component: \"support.class.component.cosense\",\n attribute: \"entity.other.attribute-name.cosense\",\n attributeValue: \"string.quoted.attribute-value.cosense\",\n expression: \"meta.embedded.expression.cosense\",\n frontmatter: \"comment.block.frontmatter.cosense\",\n} as const satisfies Record<string, Scope>\n\nexport type Notation = keyof typeof SCOPES\n","/**\n * The Cosense grammar, built from code rather than written as JSON: emphasis alone takes\n * one rule per combination of markers, and those are generated.\n *\n * The rules follow @cosense-toolbox/parser, and where they cannot (TextMate reads line by\n * line with regexes, the parser reads the whole page) they approximate it. The parity test\n * measures how close that is against the language server.\n *\n * Rules are first built as a tagged union (`Pattern`) and only then encoded as TextMate\n * JSON. The union is what keeps the grammar honest: a rule is a match or a region and never\n * half of each, every `include` names a repository entry that exists, and every scope ends\n * in `.cosense`. A typo in any of those fails to compile instead of silently colouring nothing.\n */\nimport { Array as Arr, Brand, Match, Option, pipe, Record as Rec } from \"effect\"\n\nimport { type Scope, SCOPES } from \"./scopes\"\n\n// --- The output: what Shiki and VS Code read ------------------------------------------\n\n/** A TextMate rule as JSON. Assignable to Shiki's and VS Code's own types. */\nexport interface Rule {\n name?: string\n match?: string\n begin?: string\n end?: string\n captures?: Record<string, Rule>\n beginCaptures?: Record<string, Rule>\n endCaptures?: Record<string, Rule>\n patterns?: Rule[]\n include?: string\n applyEndPatternLast?: boolean\n}\n\n/** A grammar as Shiki takes it (`LanguageRegistration`), which is also a `.tmLanguage.json`. */\nexport interface Grammar {\n name: Dialect\n displayName: string\n scopeName: `text.${Dialect}`\n aliases: string[]\n fileTypes: string[]\n patterns: Rule[]\n repository: Record<string, Rule>\n}\n\n// --- Regexes ----------------------------------------------------------------------------\n\n/** A regex in Oniguruma syntax. Branded so a scope or a label cannot be passed as one. */\ntype Regex = string & Brand.Brand<\"Regex\">\nconst Regex = Brand.nominal<Regex>()\n\n/** A regex written raw: backslashes are the regex's, not the string literal's. */\nconst re = (strings: TemplateStringsArray, ...parts: ReadonlyArray<Regex | number>): Regex =>\n Regex(String.raw(strings, ...parts))\n\nconst oneOf = (alternatives: Arr.NonEmptyReadonlyArray<Regex>): Regex =>\n Regex(alternatives.map((alternative) => `(?:${alternative})`).join(\"|\"))\n\n/** Indentation, as the parser counts it: full-width spaces (U+3000) indent too. */\nconst INDENT = re`[ \\t\\u3000]*`\n\nconst IMAGE_EXT = re`\\.(?i:png|jpe?g|gif|webp|svg|bmp|avif)`\n\n/**\n * A URL that is drawn as an image, inside a bracket (so without `[`, `]` or spaces):\n * a Gyazo page, a path ending in an image extension before any query, or a fragment\n * ending in one (`#.svg`, Cosense's way of saying \"this is an image\").\n */\nconst IMAGE_URL = oneOf([\n re`(?i:https?://(?:i\\.)?gyazo\\.com/[0-9a-f]{20,})[^\\s\\[\\]]*`,\n re`(?i:https?)://[^\\s\\[\\]?#]*${IMAGE_EXT}(?:[?#][^\\s\\[\\]]*)?`,\n re`(?i:https?)://[^\\s\\[\\]#]*#[^\\s\\[\\]]*${IMAGE_EXT}`,\n])\n\nconst URL = re`(?i:https?)://[^\\s\\[\\]]+`\n\nconst VIDEO_EXT = re`\\.(?i:mp4|webm|mov)`\n\n/** A URL that is a video on its own (`[x]`, `[[x]]`): no query allowed. */\nconst VIDEO_URL = re`(?i:https?)://[^\\s\\[\\]]+${VIDEO_EXT}`\n\n/** The video of a linked video (`[link video]`), which may carry a query. */\nconst LINKED_VIDEO_URL = re`(?i:https?)://[^\\s\\[\\]]*${VIDEO_EXT}(?:\\?[^\\s\\[\\]]+)?`\n\nconst AUDIO_URL = re`(?i:https?)://[^\\s\\[\\]]*\\.(?i:wav|mp3|weba|ogg|aac)`\n\n/** A map's coordinates (`N35.68,E139.76,Z14`): capital letters only, and no spaces. */\nconst COORDINATES = re`[NS]\\d+(?:\\.\\d+)?,[EW]\\d+(?:\\.\\d+)?(?:,Z\\d+)?`\n\n/**\n * A URL Cosense embeds a player for, as the parser reads them: YouTube (case-sensitive, as\n * in Cosense Web), Vimeo, Spotify, and anchor.fm or its successor.\n */\nconst EMBED_URL = oneOf([\n re`https?://(?:www\\.|music\\.|)youtube\\.com/watch\\?(?:[^\\s\\[\\]]+&|)v=[a-zA-Z\\d_-]+(?:&[^\\s\\[\\]]+|)`,\n re`https?://youtu\\.be/[a-zA-Z\\d_-]+(?:\\?[^\\s\\[\\]]{0,100}|)`,\n re`https?://(?:www\\.|)youtube\\.com/(?:shorts|live)/[a-zA-Z\\d_-]+(?:\\?[^\\s\\[\\]]+|)`,\n re`https?://(?:www\\.|music\\.|)youtube\\.com/playlist\\?(?:[^\\s\\[\\]]+&|)list=[a-zA-Z\\d_-]+(?:&[^\\s\\[\\]]+|)`,\n re`(?i:https?://vimeo\\.com/[0-9]+(?:/[a-z0-9]+)?(?:\\?[^\\s\\[\\]]+|))`,\n re`(?i:https?://open\\.spotify\\.com/(?:[^/\\s\\[\\]]+/|)(?:track|artist|playlist|album|episode|show)/[a-zA-Z\\d_-]+(?:\\?[^\\s\\[\\]]{0,100}|))`,\n re`(?i:https?://(?:anchor\\.fm|podcasters\\.spotify\\.com/pod/show)/[a-zA-Z\\d_-]+/episodes/[a-zA-Z\\d_-]+(?:/[a-zA-Z\\d_-]+)?(?:\\?[^\\s\\[\\]]{0,100}|))`,\n])\n\n/** What a bracket must end with somewhere, or it is plain text and not worth a rule. */\nconst CLOSES = re`(?=.*\\])`\n\n/**\n * No inline code starts inside the bracket that opens here. Cosense reads code before\n * brackets, so a bracket with a code span starting inside it is no notation (`[a `b` c]`).\n * A span starts at a backtick with another one anywhere after it. One level of brackets is\n * stepped over, as emphasis holds them (`[* a [b] `c`]`). The backtick is written `\\x60`,\n * which both regex engines read the same.\n */\nconst NO_CODE_INSIDE = re`(?!(?:\\[[^\\[\\]]*\\]|[^\\]\\x60])*\\x60.*\\x60)`\n\n/** The same for `[[x]]`, which closes on the first `]]`. */\nconst NO_CODE_INSIDE_DOUBLE = re`(?!(?:(?!\\]\\])[^\\x60])*\\x60.*\\x60)`\n\n/** A bracket closes on its own line or not at all. */\nconst CLOSE_ON_LINE = re`\\]|(?=$)`\n\n/**\n * A decoration marker: any of Cosense's, as the parser reads them. Only `* / - _` have a look\n * of their own; the rest (`[! 注意]`) still make the bracket a decoration and not a link.\n */\nconst MARKERS = re`[!\"#%&'()*+,\\-./{|}<>_~=]`\n\nconst NOT_STAR = re`[!\"#%&'()+,\\-./{|}<>_~=]`\n\n/**\n * A line that opens with a component tag (`.csnx`), as the language server tells one:\n * `<` or `</`, a capitalised name, then a space or the end of the tag.\n */\nconst COMPONENT_LINE = re`\\s*</?[A-Z][A-Za-z0-9_.]*(?:[^\\S\\n]|/?>)`\n\n// --- Rules, typed -----------------------------------------------------------------------\n\ntype RepositoryKey =\n | \"head\"\n | \"frontmatter\"\n | \"code-block\"\n | \"table-block\"\n | \"component\"\n | \"component-tag\"\n | \"expression\"\n | \"quote\"\n | \"command\"\n | \"inline\"\n | \"inline-in-emphasis\"\n | \"nested-bracket\"\n | \"bare-bracket\"\n\n/** `0` is the whole match; this grammar never needs more than three groups. */\ntype CaptureGroup = \"0\" | \"1\" | \"2\" | \"3\"\n\ninterface Capture {\n readonly scopes: ReadonlyArray<Scope>\n readonly patterns: ReadonlyArray<Pattern>\n}\n\ntype Captures = Readonly<Partial<Record<CaptureGroup, Capture>>>\n\ninterface Include {\n readonly _tag: \"Include\"\n readonly key: RepositoryKey\n}\n\n/** A single-line match. */\ninterface Single {\n readonly _tag: \"Single\"\n readonly scopes: ReadonlyArray<Scope>\n readonly regex: Regex\n readonly captures: Captures\n}\n\n/** From `begin` to `end`, with `patterns` tried in between. */\ninterface Region {\n readonly _tag: \"Region\"\n readonly scopes: ReadonlyArray<Scope>\n readonly begin: Regex\n readonly end: Regex\n readonly beginCaptures: Captures\n readonly endCaptures: Captures\n readonly patterns: ReadonlyArray<Pattern>\n /** Try `patterns` before `end` when both match at the same place. */\n readonly endPatternLast: boolean\n}\n\n/** Several patterns tried in order, with no scope of their own. */\ninterface Group {\n readonly _tag: \"Group\"\n readonly patterns: ReadonlyArray<Pattern>\n}\n\ntype Pattern = Include | Single | Region | Group\n\nconst include = (key: RepositoryKey): Pattern => ({ _tag: \"Include\", key })\n\nconst single = (\n regex: Regex,\n options: { readonly scopes?: ReadonlyArray<Scope>; readonly captures?: Captures } = {},\n): Pattern => ({\n _tag: \"Single\",\n regex,\n scopes: options.scopes ?? [],\n captures: options.captures ?? {},\n})\n\nconst region = (options: {\n readonly begin: Regex\n readonly end: Regex\n readonly scopes?: ReadonlyArray<Scope>\n readonly beginCaptures?: Captures\n readonly endCaptures?: Captures\n readonly patterns?: ReadonlyArray<Pattern>\n readonly endPatternLast?: boolean\n}): Pattern => ({\n _tag: \"Region\",\n begin: options.begin,\n end: options.end,\n scopes: options.scopes ?? [],\n beginCaptures: options.beginCaptures ?? {},\n endCaptures: options.endCaptures ?? {},\n patterns: options.patterns ?? [],\n endPatternLast: options.endPatternLast ?? false,\n})\n\nconst group = (patterns: ReadonlyArray<Pattern>): Pattern => ({ _tag: \"Group\", patterns })\n\nconst scoped = (...scopes: ReadonlyArray<Scope>): Capture => ({ scopes, patterns: [] })\n\nconst readAs = (...patterns: ReadonlyArray<Pattern>): Capture => ({ scopes: [], patterns })\n\nconst onlyIf = <A>(condition: boolean, value: A): Option.Option<A> =>\n condition ? Option.some(value) : Option.none()\n\n// --- Inline notation --------------------------------------------------------------------\n\ntype Weight = 0 | 1 | 2 | 3\n\n/** One combination of emphasis markers: how many `*`, and which of `/`, `-`, `_`. */\ninterface Emphasis {\n readonly stars: Weight\n readonly italic: boolean\n readonly strike: boolean\n readonly underline: boolean\n}\n\nconst WEIGHTS: ReadonlyArray<Weight> = [0, 1, 2, 3]\n\nconst SWITCHES = [false, true] as const\n\n/**\n * Every combination of markers. The one with none of `* / - _` is a run of the others alone\n * (`[! x]`), which colours nothing but still keeps the bracket from reading as a link.\n */\nconst MARKER_SETS: ReadonlyArray<Emphasis> = pipe(\n WEIGHTS,\n Arr.flatMap((stars) => Arr.map(SWITCHES, (italic) => ({ stars, italic }))),\n Arr.flatMap((set) => Arr.map(SWITCHES, (strike) => ({ ...set, strike }))),\n Arr.flatMap((set) => Arr.map(SWITCHES, (underline) => ({ ...set, underline }))),\n)\n\n/** `[*** x]` and louder are one size: Cosense caps the size, and so does the parser. */\nconst WEIGHT_SCOPE: { readonly [W in Weight]: Option.Option<Scope> } = {\n 0: Option.none(),\n 1: Option.some(SCOPES.bold),\n 2: Option.some(SCOPES.bold2),\n 3: Option.some(SCOPES.bold3),\n}\n\n/** All the scopes a run carries, since Cosense applies every marker: `[-* x]` is both. */\nconst emphasisScopes = (emphasis: Emphasis): ReadonlyArray<Scope> =>\n Arr.getSomes([\n onlyIf(emphasis.underline, SCOPES.underline),\n onlyIf(emphasis.strike, SCOPES.strike),\n onlyIf(emphasis.italic, SCOPES.italic),\n WEIGHT_SCOPE[emphasis.stars],\n ])\n\n/** A lookahead over the marker run (`[-** x]` -> `-**`) that holds for exactly this many `*`. */\nconst starCondition = (stars: Weight): Regex =>\n Match.value(stars).pipe(\n Match.when(0, () => re`(?!${MARKERS}*?\\*)`),\n Match.when(3, () => re`(?=(?:${NOT_STAR}*\\*){3})`),\n Match.orElse((count) => re`(?=(?:${NOT_STAR}*\\*){${count}}${NOT_STAR}*\\s)`),\n )\n\nconst markerCondition = (marker: Regex, wanted: boolean): Regex =>\n wanted ? re`(?=${MARKERS}*?${marker})` : re`(?!${MARKERS}*?${marker})`\n\n/** Lookaheads over the marker run that hold only for this combination. */\nconst markerConditions = (emphasis: Emphasis): Regex =>\n Regex(\n [\n starCondition(emphasis.stars),\n markerCondition(re`/`, emphasis.italic),\n markerCondition(re`\\-`, emphasis.strike),\n markerCondition(re`_`, emphasis.underline),\n ].join(\"\"),\n )\n\n/**\n * `[<markers> body]`, one rule per combination. The body is read again for links and icons\n * but not for emphasis, which does not nest in Cosense (`[* [* x]]` is bold around a link).\n */\nconst emphasisRules: ReadonlyArray<Pattern> = Arr.map(MARKER_SETS, (emphasis) =>\n region({\n scopes: emphasisScopes(emphasis),\n begin: re`\\[${NO_CODE_INSIDE}${markerConditions(emphasis)}(?=${MARKERS}+\\s.*\\])${MARKERS}+\\s+`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"inline-in-emphasis\"), include(\"nested-bracket\")],\n }),\n)\n\n/** Anything up to, but not over, a `]]`. */\nconst UNTIL_DOUBLE_CLOSE = re`(?:(?!\\]\\]).)`\n\n/**\n * `[[x]]`: an image or a video when x is one, bold otherwise. Closes on the first `]]`, depth\n * or not. A sound or an embedded player is not made large: it is a link inside bold.\n */\nconst strongRules: ReadonlyArray<Pattern> = [\n single(re`\\[\\[${NO_CODE_INSIDE_DOUBLE}${VIDEO_URL}\\]\\]`, { scopes: [SCOPES.media] }),\n single(\n re`\\[\\[${NO_CODE_INSIDE_DOUBLE}(?:${IMAGE_URL}|${UNTIL_DOUBLE_CLOSE}*?${IMAGE_EXT}(?:[?#]${UNTIL_DOUBLE_CLOSE}*)?)\\]\\]`,\n { scopes: [SCOPES.image] },\n ),\n single(re`\\[\\[${NO_CODE_INSIDE_DOUBLE}(${UNTIL_DOUBLE_CLOSE}+)\\]\\]`, {\n scopes: [SCOPES.bold],\n captures: { 1: readAs(include(\"inline-in-emphasis\")) },\n }),\n]\n\n/**\n * `[$ x^2]`. Its body is TeX, not notation, and may hold brackets of its own. Like any\n * bracket, it is no formula when inline code starts inside it (`[$ a``]`).\n */\nconst formulaRule: Pattern = region({\n scopes: [SCOPES.formula],\n begin: re`\\[${NO_CODE_INSIDE}(?=\\$)${CLOSES}`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"bare-bracket\")],\n})\n\n/**\n * Brackets whose body holds no brackets, tried in the parser's order: an icon before media\n * before a URL before an image path before a project link, and anything left is a page link.\n */\nconst simpleTargetRules = (allowImagePath: boolean): ReadonlyArray<Pattern> =>\n Arr.getSomes([\n Option.some(\n single(re`\\[${NO_CODE_INSIDE}[^\\[\\]]+\\.icon(?:\\*\\d+)?\\]`, { scopes: [SCOPES.icon] }),\n ),\n // A player, a map, a video or a sound, alone or linked: tried before images, as the parser does.\n Option.some(\n single(\n re`\\[${NO_CODE_INSIDE}(?:${COORDINATES}(?:\\s+[^\\[\\]]+)?|[^\\[\\]]+?\\s+${COORDINATES})\\]`,\n {\n scopes: [SCOPES.media],\n },\n ),\n ),\n Option.some(\n single(re`\\[${NO_CODE_INSIDE}(?:${EMBED_URL}|${VIDEO_URL}|${AUDIO_URL})\\]`, {\n scopes: [SCOPES.media],\n }),\n ),\n Option.some(\n single(\n re`\\[${NO_CODE_INSIDE}(?:${URL}\\s+${LINKED_VIDEO_URL}|${LINKED_VIDEO_URL}\\s+${URL})\\]`,\n { scopes: [SCOPES.media] },\n ),\n ),\n // A sound with words before or after it, unless the words are one image URL: that makes\n // an image linking to the sound.\n Option.some(\n single(re`\\[${NO_CODE_INSIDE}${AUDIO_URL}\\s+(?!\\s*(?:${IMAGE_URL})\\s*\\])[^\\[\\]]*\\]`, {\n scopes: [SCOPES.media],\n }),\n ),\n Option.some(\n single(\n re`\\[${NO_CODE_INSIDE}(?!\\s*(?:${IMAGE_URL})\\s+${AUDIO_URL}\\])[^\\[\\]]+\\s${AUDIO_URL}\\]`,\n { scopes: [SCOPES.media] },\n ),\n ),\n // Only URLs, and one of them an image: the image, linking to another URL if there is one.\n Option.some(\n single(\n re`\\[${NO_CODE_INSIDE}\\s*(?:${URL}\\s+)*(?:${IMAGE_URL})(?=[\\s\\]])(?:\\s+${URL})*\\s*\\]`,\n {\n scopes: [SCOPES.image],\n },\n ),\n ),\n // Any other URL makes a link, labelled or not.\n Option.some(\n single(re`\\[${NO_CODE_INSIDE}[^\\[\\]]*${URL}[^\\[\\]]*\\]`, { scopes: [SCOPES.externalLink] }),\n ),\n // `[a.png]`, but not inside emphasis: there Cosense links to a page of that name.\n onlyIf(\n allowImagePath,\n single(re`\\[${NO_CODE_INSIDE}[^\\[\\]]*${IMAGE_EXT}\\]`, { scopes: [SCOPES.image] }),\n ),\n Option.some(single(re`\\[${NO_CODE_INSIDE}/[^\\[\\]]*\\]`, { scopes: [SCOPES.projectLink] })),\n Option.some(\n single(re`\\[${NO_CODE_INSIDE}(?=[^\\[\\]]*[^\\s\\[\\]])[^\\[\\]]+\\]`, { scopes: [SCOPES.link] }),\n ),\n ])\n\n/** What follows the bracketed notation in both contexts, in the parser's order. */\nconst unbracketedRules: ReadonlyArray<Pattern> = [\n single(Regex(\"`[^`]*`\"), { scopes: [SCOPES.code] }),\n // `#tag`, only at the start or after a space: a `#` inside a word is just a character.\n single(re`(?:^|(?<=[ \\t\\u3000]))#[^\\s\\[\\]#]+`, { scopes: [SCOPES.hashtag] }),\n // A URL outside brackets is always a link, even to an image.\n single(re`(?i:https?)://[^\\s\\]]+`, { scopes: [SCOPES.externalLink] }),\n]\n\n// --- Lines and blocks -------------------------------------------------------------------\n\n/**\n * A block is its header and every line indented deeper. Deeper is approximated as\n * \"the header's indentation, then more\", which is exact when a page indents one way.\n */\nconst block = (keyword: \"code\" | \"table\", scope: Scope, patterns: ReadonlyArray<Pattern>) =>\n region({\n scopes: [scope],\n begin: re`^(${INDENT})(${Regex(keyword)}:)(.+)$`,\n beginCaptures: {\n 2: scoped(\"keyword.other.block.cosense\"),\n 3: scoped(\"entity.name.section.block.cosense\"),\n },\n end: re`^(?!\\1[ \\t\\u3000])`,\n patterns,\n })\n\n/**\n * The first line is the title, unless the file opens with YAML: then it is the first line\n * after the fence. Nothing on the title line is notation. In `.csnx` a first line that is\n * a component tag is that tag, and the page has no title line.\n */\nconst head = (dialect: DialectInfo): Pattern => {\n const notComponent = dialect.components ? re`(?!${COMPONENT_LINE})` : re``\n return group([\n region({\n begin: re`\\A(?=---[ \\t]*$)`,\n patterns: [include(\"frontmatter\")],\n // Ends on the line after the fence either way; only a title line is captured.\n end: dialect.components ? re`^(?:(?=${COMPONENT_LINE})|(.*)$)` : re`^(.*)$`,\n endCaptures: { 1: scoped(SCOPES.title) },\n // At the very start the fence and the title line both match: the fence wins.\n endPatternLast: true,\n }),\n single(re`\\A${notComponent}.*$`, { scopes: [SCOPES.title] }),\n ])\n}\n\n/** Every other entry the grammar can `include`. The mapped type makes a missing one an error. */\nconst REPOSITORY: { readonly [K in Exclude<RepositoryKey, \"head\">]: Pattern } = {\n frontmatter: region({\n scopes: [SCOPES.frontmatter],\n begin: re`\\A---[ \\t]*$`,\n end: re`^---[ \\t]*$`,\n }),\n \"code-block\": block(\"code\", SCOPES.codeBlock, []),\n \"table-block\": block(\"table\", SCOPES.table, [\n single(re`\\t`, { scopes: [\"punctuation.separator.table-cell.cosense\"] }),\n ]),\n // A component tag owns its line: nothing on it is Cosense notation, and the text after\n // the tag is plain.\n component: region({\n scopes: [\"meta.tag.component.cosense\"],\n begin: re`^(?=${COMPONENT_LINE})`,\n end: re`$`,\n patterns: [include(\"component-tag\")],\n }),\n // The tag itself, read as JSX.\n \"component-tag\": region({\n begin: re`^(\\s*)(</?)([A-Z][A-Za-z0-9_.]*)`,\n beginCaptures: {\n 2: scoped(\"punctuation.definition.tag.begin.cosense\"),\n 3: scoped(SCOPES.component),\n },\n end: re`(/?>)|(?=$)`,\n endCaptures: { 1: scoped(\"punctuation.definition.tag.end.cosense\") },\n patterns: [\n single(re`[A-Za-z_:][A-Za-z0-9_:.\\-]*`, { scopes: [SCOPES.attribute] }),\n single(re`=`, { scopes: [\"punctuation.separator.key-value.cosense\"] }),\n single(re`\"[^\"]*(?:\"|$)|'[^']*(?:'|$)`, { scopes: [SCOPES.attributeValue] }),\n include(\"expression\"),\n ],\n }),\n // `{...}`: a JavaScript expression. Braces nest; a few literals are told apart.\n expression: region({\n scopes: [SCOPES.expression],\n begin: re`\\{`,\n end: re`\\}|(?=$)`,\n beginCaptures: { 0: scoped(\"punctuation.section.embedded.begin.cosense\") },\n endCaptures: { 0: scoped(\"punctuation.section.embedded.end.cosense\") },\n patterns: [\n include(\"expression\"),\n single(re`\"[^\"]*\"|'[^']*'`, { scopes: [\"string.quoted.cosense\"] }),\n single(re`\\b\\d+(?:\\.\\d+)?\\b`, { scopes: [\"constant.numeric.cosense\"] }),\n single(re`\\b(?:true|false|null|undefined)\\b`, { scopes: [\"constant.language.cosense\"] }),\n ],\n }),\n // `$ ls` or `% ls` after the indent: a command, code as a whole. Nothing in it is notation.\n command: single(re`^${INDENT}([$%] .+)$`, { captures: { 1: scoped(SCOPES.code) } }),\n quote: region({\n scopes: [\"markup.quote.cosense\"],\n begin: re`^${INDENT}(>[> ]*)`,\n beginCaptures: { 1: scoped(SCOPES.quote) },\n end: re`$`,\n patterns: [include(\"inline\")],\n }),\n inline: group([\n ...strongRules,\n formulaRule,\n ...emphasisRules,\n ...simpleTargetRules(true),\n ...unbracketedRules,\n ]),\n \"inline-in-emphasis\": group([\n ...strongRules,\n formulaRule,\n ...simpleTargetRules(false),\n ...unbracketedRules,\n ]),\n // A bracket that is no notation, but whose `]` must not close the one around it:\n // in `[* a [] b]` the emphasis runs to the last `]`, as the parser counts depth.\n \"nested-bracket\": region({\n begin: re`\\[`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"inline-in-emphasis\"), include(\"nested-bracket\")],\n }),\n \"bare-bracket\": region({\n begin: re`\\[`,\n end: CLOSE_ON_LINE,\n patterns: [include(\"bare-bracket\")],\n }),\n}\n\n// --- Encoding ---------------------------------------------------------------------------\n\n/** A field only when there is something to put in it: TextMate reads absent and empty alike. */\nconst field = <K extends keyof Rule>(key: K, value: Option.Option<Rule[K]>): Partial<Rule> =>\n Option.match(value, { onNone: () => ({}), onSome: (v) => ({ [key]: v }) })\n\nconst nonEmpty = <A>(items: ReadonlyArray<A>): Option.Option<ReadonlyArray<A>> =>\n Option.liftPredicate(items, (list) => list.length > 0)\n\n/** TextMate gives a rule several scopes by separating them with spaces. */\nconst nameField = (scopes: ReadonlyArray<Scope>): Partial<Rule> =>\n field(\n \"name\",\n Option.map(nonEmpty(scopes), (list) => list.join(\" \")),\n )\n\nconst patternsField = (patterns: ReadonlyArray<Pattern>): Partial<Rule> =>\n field(\"patterns\", Option.map(nonEmpty(patterns), Arr.map(encode)))\n\nconst encodeCaptures = (captures: Captures): Option.Option<Record<string, Rule>> =>\n pipe(\n Object.entries(captures),\n Arr.map(\n ([group, capture]) =>\n [group, { ...nameField(capture.scopes), ...patternsField(capture.patterns) }] as const,\n ),\n nonEmpty,\n Option.map(Rec.fromEntries),\n )\n\nconst encode = (pattern: Pattern): Rule =>\n Match.value(pattern).pipe(\n Match.tag(\"Include\", ({ key }) => ({ include: `#${key}` })),\n Match.tag(\"Group\", ({ patterns }) => patternsField(patterns)),\n Match.tag(\"Single\", (rule) => ({\n ...nameField(rule.scopes),\n match: rule.regex,\n ...field(\"captures\", encodeCaptures(rule.captures)),\n })),\n Match.tag(\"Region\", (rule) => ({\n ...nameField(rule.scopes),\n begin: rule.begin,\n end: rule.end,\n ...field(\"beginCaptures\", encodeCaptures(rule.beginCaptures)),\n ...field(\"endCaptures\", encodeCaptures(rule.endCaptures)),\n ...patternsField(rule.patterns),\n ...field(\"applyEndPatternLast\", onlyIf(rule.endPatternLast, true)),\n })),\n Match.exhaustive,\n )\n\n// --- Dialects ---------------------------------------------------------------------------\n\n/** `.csn`, and `.csnx`, which also reads a line that is one component tag. */\nexport type Dialect = \"cosense\" | \"cosense-x\"\n\ninterface DialectInfo {\n readonly displayName: string\n readonly extension: string\n readonly components: boolean\n}\n\nconst DIALECTS: { readonly [D in Dialect]: DialectInfo } = {\n cosense: { displayName: \"Cosense\", extension: \"csn\", components: false },\n \"cosense-x\": { displayName: \"Cosense X\", extension: \"csnx\", components: true },\n}\n\n/** What a line can be, in the order the parser decides it. */\nconst linePatterns = (dialect: DialectInfo): ReadonlyArray<Pattern> =>\n Arr.getSomes([\n Option.some(include(\"head\")),\n Option.some(include(\"code-block\")),\n Option.some(include(\"table-block\")),\n onlyIf(dialect.components, include(\"component\")),\n Option.some(include(\"command\")),\n Option.some(include(\"quote\")),\n Option.some(include(\"inline\")),\n ])\n\nexport const buildGrammar = (dialect: Dialect): Grammar => {\n const info = DIALECTS[dialect]\n return {\n name: dialect,\n displayName: info.displayName,\n scopeName: `text.${dialect}`,\n aliases: [info.extension],\n fileTypes: [info.extension],\n patterns: Arr.map(linePatterns(info), encode),\n repository: Rec.map({ head: head(info), ...REPOSITORY }, encode),\n }\n}\n","/**\n * TextMate grammars for Cosense notation: `.csn` and, with component lines, `.csnx`.\n *\n * Each export is a Shiki `LanguageRegistration` as it stands, and the same objects are\n * published as `.tmLanguage.json` for editors that read TextMate grammars.\n */\nimport { buildGrammar, type Grammar } from \"./grammar\"\n\nexport type { Dialect, Grammar, Rule } from \"./grammar\"\nexport { type Notation, SCOPES } from \"./scopes\"\n\nexport const cosense: Grammar = buildGrammar(\"cosense\")\n\nexport const cosenseX: Grammar = buildGrammar(\"cosense-x\")\n"],"mappings":"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"}