@citisen/litearea 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -18
- package/README.zh.md +25 -11
- package/dist/types/core/types.d.ts +5 -5
- package/dist/types/index.d.ts.map +1 -1
- package/docs/architecture.md +10 -6
- package/docs/completion.md +3 -3
- package/docs/grammar.md +16 -15
- package/package.json +1 -6
- package/scripts/browser-check.mjs +133 -54
- package/scripts/verify-package.mjs +51 -27
- package/src/core/types.ts +5 -5
- package/src/index.ts +6 -5
- package/dist/grammars.cjs +0 -1228
- package/dist/grammars.cjs.map +0 -1
- package/dist/grammars.js +0 -1213
- package/dist/grammars.js.map +0 -1
- package/dist/types/grammars/dshFont.d.ts +0 -127
- package/dist/types/grammars/dshFont.d.ts.map +0 -1
- package/dist/types/grammars/dshSentry.d.ts +0 -84
- package/dist/types/grammars/dshSentry.d.ts.map +0 -1
- package/dist/types/grammars/index.d.ts +0 -3
- package/dist/types/grammars/index.d.ts.map +0 -1
- package/src/grammars/dshFont.ts +0 -1004
- package/src/grammars/dshSentry.ts +0 -742
- package/src/grammars/index.ts +0 -57
package/README.md
CHANGED
|
@@ -59,15 +59,17 @@ So this library makes the opposite choices, and each one is load-bearing:
|
|
|
59
59
|
npm install @citisen/litearea
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Three entry points
|
|
62
|
+
Three entry points:
|
|
63
63
|
|
|
64
64
|
| Import | What it is |
|
|
65
65
|
| --- | --- |
|
|
66
66
|
| `@citisen/litearea` | The pure engine plus the DOM layer (`createEditor`, `LiteArea`) |
|
|
67
67
|
| `@citisen/litearea/react` | The React binding (`LiteAreaEditor`) |
|
|
68
|
-
| `@citisen/litearea/grammars` | The two worked example grammars |
|
|
69
68
|
| `@citisen/litearea/styles.css` | The same stylesheet the editor injects, for hosts that link CSS |
|
|
70
69
|
|
|
70
|
+
There is no grammar entry point. The library ships no syntax at all — a caller
|
|
71
|
+
supplies the rules — and nothing in `src/core/` imports a language.
|
|
72
|
+
|
|
71
73
|
React is an optional peer dependency (`react >= 18`) and the only peer. The
|
|
72
74
|
package has no runtime dependencies at all. The stylesheet is injected into the
|
|
73
75
|
document once, on the first editor, unless `injectStyles: false` is passed.
|
|
@@ -299,25 +301,44 @@ diagnostic vocabulary, and an end-to-end walkthrough that adds `analyze`,
|
|
|
299
301
|
`checks`, and `validate` to this same language — is in
|
|
300
302
|
[docs/grammar.md](docs/grammar.md).
|
|
301
303
|
|
|
302
|
-
##
|
|
304
|
+
## No grammar is shipped
|
|
303
305
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
that already exists:
|
|
306
|
+
There is no built-in language, no language identifier to switch on, no bundled
|
|
307
|
+
tokenizer, and no grammar to import: nothing in `src/core/` knows what a font
|
|
308
|
+
stack is, and the package publishes no grammar entry point. A caller supplies the
|
|
309
|
+
rules, and the two examples in this file are the whole of what this repository
|
|
310
|
+
offers as a worked language.
|
|
310
311
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
That is a deliberate boundary rather than an omission. The editor came out of two
|
|
313
|
+
plugins with real DSLs — dsh-sentry's appearance document and dsh-font's font
|
|
314
|
+
query — and each of those grammars now lives beside the plugin that owns it, as a
|
|
315
|
+
grammar object the plugin hands to `createEditor` (or to `LiteAreaEditor`). The
|
|
316
|
+
shape of one is worth showing, because it is what a grammar written against a real
|
|
317
|
+
product looks like, and because it says plainly whose code it is:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
// In the dsh-font plugin, NOT in litearea. The plugin owns the language; the
|
|
321
|
+
// library owns the engine that reads it, and ships no language of its own.
|
|
322
|
+
export const fontQueryGrammar = defineGrammar({
|
|
323
|
+
id: 'dsh-font-query',
|
|
324
|
+
// `-apple-system` must be ONE word, or a completion would replace half of it.
|
|
325
|
+
wordChars: /[\p{L}\p{N}_-]/u,
|
|
326
|
+
rules: [
|
|
327
|
+
{ kind: 'words', words: (context) => context.state.catalogue },
|
|
328
|
+
{ kind: 'match', scope: 'family.generic', pattern: /monospace|sans-serif|serif/ },
|
|
329
|
+
{ kind: 'match', scope: 'weight', pattern: /thin|light|regular|medium|bold/ },
|
|
330
|
+
{ kind: 'match', scope: 'separator', pattern: /,/ },
|
|
331
|
+
],
|
|
332
|
+
})
|
|
333
|
+
```
|
|
315
334
|
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
Shipping either grammar would be a scaling trap as much as an inconsistency: every
|
|
336
|
+
consumer that inlines the library would carry every language, so adding a
|
|
337
|
+
JavaScript, CSS, HTML, or Rust grammar later would grow the bundle of hosts that
|
|
338
|
+
use none of them. A grammar belongs where the language is parsed.
|
|
318
339
|
|
|
319
|
-
Two decisions in
|
|
320
|
-
found by comparing the grammar against the parser it edits for:
|
|
340
|
+
Two decisions in that plugin's own grammar are worth stating anyway, because both
|
|
341
|
+
were found by comparing the grammar against the parser it edits for:
|
|
321
342
|
|
|
322
343
|
- **It is deliberately stricter than the host parser.** `parseStyle` in the
|
|
323
344
|
plugin does no value checking for a known option: it writes `shape=bogus` into
|
|
@@ -403,7 +424,7 @@ Colours, spacing, and the type scale come from custom properties on the wrapper:
|
|
|
403
424
|
| `--litearea-accent` / `--litearea-accent-soft` / `--litearea-selection` | `#4d6bfe` and two alpha variants |
|
|
404
425
|
| `--litearea-error` / `--litearea-warning` / `--litearea-info` / `--litearea-hint` | The four severity colours |
|
|
405
426
|
| `--litearea-shadow` | The floating panel's shadow |
|
|
406
|
-
| `--litearea-scope-*` | One colour per scope the
|
|
427
|
+
| `--litearea-scope-*` | One colour per scope name in the shipped palette, so a scope a grammar invents still has a themed colour to fall back on |
|
|
407
428
|
|
|
408
429
|
A dark scheme is applied automatically from `prefers-color-scheme`.
|
|
409
430
|
|
package/README.zh.md
CHANGED
|
@@ -32,15 +32,16 @@
|
|
|
32
32
|
npm install @citisen/litearea
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
三个入口:
|
|
36
36
|
|
|
37
37
|
| 导入 | 是什么 |
|
|
38
38
|
| --- | --- |
|
|
39
39
|
| `@citisen/litearea` | 纯引擎加上 DOM 层(`createEditor`、`LiteArea`) |
|
|
40
40
|
| `@citisen/litearea/react` | React 绑定(`LiteAreaEditor`) |
|
|
41
|
-
| `@citisen/litearea/grammars` | 两个作为范例的 grammar |
|
|
42
41
|
| `@citisen/litearea/styles.css` | 编辑器自己注入的那份样式表,给想用 `<link>` 的宿主 |
|
|
43
42
|
|
|
43
|
+
没有 grammar 入口。这个库一点语法都不带 —— 规则由调用方提供 —— `src/core/` 里也不导入任何语言。
|
|
44
|
+
|
|
44
45
|
React 是可选的 peer 依赖(`react >= 18`),也是唯一的 peer 依赖。这个包没有任何运行时依赖。样式表在第一个编辑器创建时注入文档一次,除非传 `injectStyles: false`。
|
|
45
46
|
|
|
46
47
|
## 快速开始
|
|
@@ -256,18 +257,31 @@ export const formSchema = defineGrammar({
|
|
|
256
257
|
|
|
257
258
|
完整参考 —— 每种规则的全部字段、优先级、诊断词汇,以及在同一门语言上补出 `analyze`、`checks`、`validate` 的端到端走查 —— 在 [docs/grammar.md](docs/grammar.md)。
|
|
258
259
|
|
|
259
|
-
##
|
|
260
|
+
## 不带任何 grammar
|
|
260
261
|
|
|
261
|
-
|
|
262
|
+
没有内置语言、没有可以 switch 的语言标识、没有捆绑的分词器,也没有可以导入的 grammar:`src/core/` 里没有任何代码知道字体栈是什么,包里也不发布 grammar 入口。规则由调用方提供,本文里的两个例子就是这个仓库给出的全部"写好的语言"。
|
|
262
263
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
这是一条刻意划下的边界,而不是遗漏。这个编辑器出自两个带真实 DSL 的插件 —— dsh-sentry 的外观文档和 dsh-font 的字体查询 —— 而这两个 grammar 现在都待在拥有它们的插件身边,作为插件交给 `createEditor`(或 `LiteAreaEditor`)的 grammar 对象。其中一个的样子值得看一眼,因为它展示了"针对真实产品写的 grammar"长什么样,也因为它清楚地说明了这段代码归谁:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
// 在 dsh-font 插件里,不在 litearea 里。语言归插件所有;库只拥有读它的引擎,
|
|
268
|
+
// 自己一点语言都不带。
|
|
269
|
+
export const fontQueryGrammar = defineGrammar({
|
|
270
|
+
id: 'dsh-font-query',
|
|
271
|
+
// `-apple-system` 必须算作一个词,否则补全会只替换它的一半。
|
|
272
|
+
wordChars: /[\p{L}\p{N}_-]/u,
|
|
273
|
+
rules: [
|
|
274
|
+
{ kind: 'words', words: (context) => context.state.catalogue },
|
|
275
|
+
{ kind: 'match', scope: 'family.generic', pattern: /monospace|sans-serif|serif/ },
|
|
276
|
+
{ kind: 'match', scope: 'weight', pattern: /thin|light|regular|medium|bold/ },
|
|
277
|
+
{ kind: 'match', scope: 'separator', pattern: /,/ },
|
|
278
|
+
],
|
|
279
|
+
})
|
|
280
|
+
```
|
|
267
281
|
|
|
268
|
-
|
|
282
|
+
把其中任何一个 grammar 打进包里,既是扩展性陷阱,也是前后不一致:每个内联了这个库的消费者都会背上所有语言,以后再想加 JavaScript、CSS、HTML 或 Rust 的 grammar,就会让完全用不到它们的宿主包体变大。语言应该待在解析它的地方。
|
|
269
283
|
|
|
270
|
-
|
|
284
|
+
不过,那个插件自己的 grammar 里有两个决定仍然值得单独说明,因为它们都是拿 grammar 和被编辑的解析器对照之后发现的:
|
|
271
285
|
|
|
272
286
|
- **它刻意比宿主解析器更严格。** 插件里的 `parseStyle` 对已知选项不做取值检查:它把 `shape=bogus` 原样写进规则,之后由 `resolveLook` 悄悄换成出厂默认值,于是拼错的表现只是"图标怎么都不变"。这个 grammar 会把它报出来 —— 报成 warning 而不是 error,因为文档仍然能用,只是它说的不是它想说的。
|
|
273
287
|
- **它不接受 `fallback` 作为一个状态。** 插件自己的模块注释里写着 `fallback none`,但 `STYLE_STATES` 只有那四个状态,`fallback` 是内部从 `STYLE_FALLBACK_LOOK` 推导出来的,从来就没有被解析过。那句注释是过期的,把它照抄进 grammar,只会让编辑器和它服务的解析器互相矛盾。
|
|
@@ -327,7 +341,7 @@ export const formSchema = defineGrammar({
|
|
|
327
341
|
| `--litearea-accent` / `--litearea-accent-soft` / `--litearea-selection` | `#4d6bfe` 以及两个带透明度的变体 |
|
|
328
342
|
| `--litearea-error` / `--litearea-warning` / `--litearea-info` / `--litearea-hint` | 四种严重级别的颜色 |
|
|
329
343
|
| `--litearea-shadow` | 浮层的阴影 |
|
|
330
|
-
| `--litearea-scope-*` |
|
|
344
|
+
| `--litearea-scope-*` | 出厂调色板里每个 scope 名字一个颜色,所以 grammar 自己新造的 scope 也有主题色可回退 |
|
|
331
345
|
|
|
332
346
|
深色方案由 `prefers-color-scheme` 自动应用。
|
|
333
347
|
|
|
@@ -36,9 +36,9 @@ export interface Token extends Range {
|
|
|
36
36
|
* tokens and they are deliberately not tokens here.
|
|
37
37
|
*
|
|
38
38
|
* The distinction earns its keep. A token is what the *characters* are; a
|
|
39
|
-
* decoration is what they *mean*, and the two change on different schedules.
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* decoration is what they *mean*, and the two change on different schedules. A
|
|
40
|
+
* grammar can paint `Geist Mono` as a family from the characters alone, but which
|
|
41
|
+
* family is *in effect* depends on a catalogue the HOST supplied — the same
|
|
42
42
|
* characters mean something else on another machine. Painting that as a token
|
|
43
43
|
* would mean re-lexing the document whenever the catalogue changed; painting it
|
|
44
44
|
* as a decoration means recomputing one range list, which is what it is.
|
|
@@ -431,8 +431,8 @@ export interface CompletionContext<State = unknown> {
|
|
|
431
431
|
* `runn|` is no longer "at the start of the line" in the strict sense, but it is
|
|
432
432
|
* unmistakably completing the first word. A source that asked `firstOnLine` would
|
|
433
433
|
* switch itself off after the very first keystroke — which is exactly the bug this
|
|
434
|
-
* field was added to fix, and exactly the kind of thing a
|
|
435
|
-
* for finding.
|
|
434
|
+
* field was added to fix, and exactly the kind of thing a grammar written against
|
|
435
|
+
* a real document is for finding.
|
|
436
436
|
*/
|
|
437
437
|
firstWord: boolean;
|
|
438
438
|
/** The first non-whitespace token on the caret's line, when there is one. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAgBA,YAAY,EACV,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,IAAI,EACJ,WAAW,EACX,SAAS,EACT,KAAK,EACL,SAAS,EACT,QAAQ,EACR,cAAc,EACd,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAA;AAExB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEpE,OAAO,EACL,KAAK,EACL,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,IAAI,EACJ,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAElF,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EACL,eAAe,EACf,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE/D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEnE,OAAO,EACL,eAAe,EACf,eAAe,EACf,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAA;AAEpB,cAAc,gBAAgB,CAAA"}
|
package/docs/architecture.md
CHANGED
|
@@ -265,11 +265,15 @@ of them are exactly why `scripts/browser-check.mjs` exists: a textarea's undo
|
|
|
265
265
|
stack has no API to read and no way to fake, so the only honest test is to make an
|
|
266
266
|
edit and undo it in a browser that has a real one.
|
|
267
267
|
|
|
268
|
-
The harness bundles the **shipped** `dist
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
The harness bundles the **shipped** `dist/index.js` — the package's one JavaScript
|
|
269
|
+
entry point — rather than `src/`, so what it drives is the artifact a host
|
|
270
|
+
installs, and it inlines it as an IIFE (Chromium refuses to fetch an ES module
|
|
271
|
+
from a `file://` URL). The language the checks run against is defined inside the
|
|
272
|
+
checklist itself, because the package ships none: proving that a particular
|
|
273
|
+
product DSL works in a browser belongs beside the plugin that owns it, while the
|
|
274
|
+
questions here are all about the engine. It drives the public API and the public
|
|
275
|
+
DOM only: it mounts editors, types through `execCommand('insertText')`, presses
|
|
276
|
+
keys, dispatches a `mousedown` on a row, and hovers a squiggle. The page
|
|
273
277
|
reports through `document.title`, with `error` and `unhandledrejection` listeners
|
|
274
278
|
that write to the same channel, because a page whose only output channel is its
|
|
275
279
|
title has to report its own failures through that channel — otherwise a syntax
|
|
@@ -311,6 +315,6 @@ again", which is all most hosts need.
|
|
|
311
315
|
| `contenteditable` | It makes the document a DOM tree that the browser edits in ways you do not control — nested elements, `<br>` for a blank line, pasted HTML — so the value would have to be re-serialised on every keystroke, which is the same re-render problem with more failure modes and no native undo semantics worth having |
|
|
312
316
|
| A second highlighting layer per range list (one for scopes, one for decorations, one for squiggles) | Each layer would have to position its own spans, so each would need its own idea of where a range starts, and a span positioned by measuring drifts as soon as the font, the wrapping, or the padding is even slightly different from what was measured. One merged stream cannot drift because nothing is positioned |
|
|
313
317
|
| Measuring the live field (`scrollHeight` after collapsing it) | It reflows the page and flickers on every keystroke — the measurement that is supposed to decide the height is what makes the height unstable. The offscreen mirror pays one extra layout and keeps the field's geometry out of the question |
|
|
314
|
-
| A bundled grammar (a default language, or a language registry) | The claim of the library is that the core knows no syntax. A default grammar would make that claim false, and it would be the thing every host inherited. The
|
|
318
|
+
| A bundled grammar (a default language, or a language registry) | The claim of the library is that the core knows no syntax. A default grammar would make that claim false, and it would be the thing every host inherited. The package therefore ships no grammar at all: a DSL lives beside the plugin that owns it and is handed to the editor as a value |
|
|
315
319
|
| Synthetic `beforeinput`/`InputEvent` edits | Untrusted, so the browser will not run them through the editing pipeline: the text changes and the history does not, which is worse than a documented `'direct'` fallback because it fails silently |
|
|
316
320
|
| Tracking the caret as a character index in one module and a screen position in another | Every position that crosses a module boundary is a character offset; pixels enter only in `src/dom/`, only to place a floating element, and never travel back inward. Two coordinate systems for one caret is what produced the range that "had already moved" |
|
package/docs/completion.md
CHANGED
|
@@ -163,9 +163,9 @@ eligible while that head is being spelled out. `runn\|` is no longer "at the sta
|
|
|
163
163
|
of the line" in the strict sense — the line already has content — but it is
|
|
164
164
|
unmistakably completing the first word. A source that asked `firstOnLine` would
|
|
165
165
|
switch itself off after the very first keystroke, which is the bug the second field
|
|
166
|
-
exists to prevent.
|
|
167
|
-
reason, and it is the difference between a list that
|
|
168
|
-
disappearing.
|
|
166
|
+
exists to prevent. A line-oriented grammar's first-word completion asks
|
|
167
|
+
`firstWord` for exactly this reason, and it is the difference between a list that
|
|
168
|
+
helps and a list that keeps disappearing.
|
|
169
169
|
|
|
170
170
|
## Ranking
|
|
171
171
|
|
package/docs/grammar.md
CHANGED
|
@@ -76,9 +76,9 @@ A function is handed everything a lexical decision could need:
|
|
|
76
76
|
| `state` | Whatever `analyze` returned |
|
|
77
77
|
|
|
78
78
|
The `state` field is the one that earns the function form: it is how a scope can
|
|
79
|
-
depend on something the characters do not say.
|
|
80
|
-
paint a weight word as `weight` or `weight.missing`, which
|
|
81
|
-
|
|
79
|
+
depend on something the characters do not say. A grammar for a host's installed
|
|
80
|
+
fonts uses it to paint a weight word as `weight` or `weight.missing`, which
|
|
81
|
+
depends on the faces the host reported, not on the word.
|
|
82
82
|
|
|
83
83
|
### `match`: a regular expression
|
|
84
84
|
|
|
@@ -116,7 +116,7 @@ states the host actually has — costs one call, not one per token.
|
|
|
116
116
|
|
|
117
117
|
| Field | Default | Notes |
|
|
118
118
|
| --- | --- | --- |
|
|
119
|
-
| `max` | 4 | The most words one entry may span. A span longer than `max` is never attempted, and four covers
|
|
119
|
+
| `max` | 4 | The most words one entry may span. A span longer than `max` is never attempted, and four covers a name like `Source Han Serif SC`, which is four words |
|
|
120
120
|
|
|
121
121
|
The engine tries the longest span first and keeps the longest member it finds, so
|
|
122
122
|
a catalogue holding both `IBM Plex` and `IBM Plex Mono` resolves the longer name.
|
|
@@ -346,10 +346,10 @@ decides three things at once:
|
|
|
346
346
|
3. **Where a double click puts the selection**, because that is the platform's
|
|
347
347
|
own word test and the browser uses the same notion of a word the field does.
|
|
348
348
|
|
|
349
|
-
A language whose names contain a dot or a hyphen must say so.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
349
|
+
A language whose names contain a dot or a hyphen must say so. A font query uses
|
|
350
|
+
`/[\p{L}\p{N}_-]/u` so `-apple-system` is one word; a grammar whose names never
|
|
351
|
+
contain a dot leaves it out, so a stray `circle.` is not read as one unknown word
|
|
352
|
+
and reported as a name the user never typed.
|
|
353
353
|
|
|
354
354
|
The flags are normalised: `g` and `y` are stripped from the predicate, because
|
|
355
355
|
`RegExp.prototype.test` advances `lastIndex` on a global pattern and a predicate
|
|
@@ -442,7 +442,8 @@ diagnostic's `source` is always the grammar's `id`.
|
|
|
442
442
|
|
|
443
443
|
`report` is also the place a structural walk that ran in `analyze` publishes what
|
|
444
444
|
it found — record the ranges while the structure is known, and report them here.
|
|
445
|
-
|
|
445
|
+
That is the usual arrangement: `analyze` decides what is wrong and `validate`
|
|
446
|
+
hands the ranges over.
|
|
446
447
|
|
|
447
448
|
### Codes and severities
|
|
448
449
|
|
|
@@ -476,12 +477,12 @@ detail and the body. Decorations are clamped into the document, dropped when the
|
|
|
476
477
|
come out empty, and sorted — a grammar computing them from a stale parse cannot
|
|
477
478
|
paint a span nobody can see.
|
|
478
479
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
480
|
+
A host's "in effect" mark is the worked case. `Geist Mono` is painted as a family
|
|
481
|
+
from the characters alone — that is a token. Which family is *in effect* depends
|
|
482
|
+
on the host's installed catalogue, so the same characters mean something else on
|
|
483
|
+
another computer. Painting that as a token would mean re-lexing the document
|
|
484
|
+
whenever the catalogue changed; as a decoration it is one recomputed range list,
|
|
485
|
+
which is what it actually is.
|
|
485
486
|
|
|
486
487
|
Reach for `decorate` when the mark depends on something outside the document. If
|
|
487
488
|
nothing in your language does, do not add one — a decoration that could have been
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@citisen/litearea",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A lightweight textarea-based code editor: native undo/redo, fully custom syntax highlighting, VSCode-style completion, diagnostics, and hover tooltips. No CodeMirror, no Monaco.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,11 +27,6 @@
|
|
|
27
27
|
"import": "./dist/react.js",
|
|
28
28
|
"require": "./dist/react.cjs"
|
|
29
29
|
},
|
|
30
|
-
"./grammars": {
|
|
31
|
-
"types": "./dist/types/grammars/index.d.ts",
|
|
32
|
-
"import": "./dist/grammars.js",
|
|
33
|
-
"require": "./dist/grammars.cjs"
|
|
34
|
-
},
|
|
35
30
|
"./styles.css": "./dist/styles.css",
|
|
36
31
|
"./package.json": "./package.json"
|
|
37
32
|
},
|