|
|
@@ -0,0 +1,153 @@
|
|
|
+# @enesis/lezer-comark
|
|
|
+
|
|
|
+Lezer extensions for Comark block and inline component syntax.
|
|
|
+
|
|
|
+## Status
|
|
|
+
|
|
|
+Active development. The grammar is functional but not yet wired into the editor's decoration pipeline.
|
|
|
+
|
|
|
+## What it does
|
|
|
+
|
|
|
+Adds four syntaxes to `@lezer/markdown`:
|
|
|
+
|
|
|
+| Syntax | Lezer node | Example |
|
|
|
+|---|---|---|
|
|
|
+| Block component | `BlockComponent` | `::query{type="TODO"}...::` |
|
|
|
+| Inline component | `InlineComponent` | `:priority{value="high"}` |
|
|
|
+| Inline component (with body) | `InlineComponent` | `:badge[Active]{color="green"}` |
|
|
|
+| Span attributes | `Span` | `[text]{.class #id key="val"}` |
|
|
|
+
|
|
|
+## Usage
|
|
|
+
|
|
|
+```typescript
|
|
|
+import { parser } from "@lezer/markdown"
|
|
|
+import { GFM } from "@lezer/markdown"
|
|
|
+import { createComarkExtensions } from "@enesis/lezer-comark"
|
|
|
+
|
|
|
+const fullParser = parser.configure([
|
|
|
+ GFM,
|
|
|
+ ...createComarkExtensions(),
|
|
|
+])
|
|
|
+```
|
|
|
+
|
|
|
+## Package structure
|
|
|
+
|
|
|
+```
|
|
|
+src/
|
|
|
+ index.ts createComarkExtensions() — barrel export
|
|
|
+ types.ts Attrs, InlineComponentData, BlockComponentData
|
|
|
+ attributes.ts parseAttrs() — parses {key="val" .class #id}
|
|
|
+ block-component.ts LeafBlockParser for ::name{...}...::
|
|
|
+ inline-component.ts InlineParser for :name{...} and :name[...]{...}
|
|
|
+ span-attr.ts InlineParser for [text]{.class #id}
|
|
|
+```
|
|
|
+
|
|
|
+## Syntax reference
|
|
|
+
|
|
|
+### Block component
|
|
|
+
|
|
|
+```
|
|
|
+::component-name{key="val"}
|
|
|
+Content lines
|
|
|
+::
|
|
|
+```
|
|
|
+
|
|
|
+Children: `ComponentName`, `ComponentAttrs`, `ComponentBody`
|
|
|
+
|
|
|
+### Inline component (props only)
|
|
|
+
|
|
|
+```
|
|
|
+:priority{value="high"}
|
|
|
+```
|
|
|
+
|
|
|
+### Inline component (content + props)
|
|
|
+
|
|
|
+```
|
|
|
+:badge[Active]{color="green"}
|
|
|
+```
|
|
|
+
|
|
|
+### Span attributes
|
|
|
+
|
|
|
+```
|
|
|
+[highlighted text]{.highlight .yellow #ref-1}
|
|
|
+```
|
|
|
+
|
|
|
+Nested markdown is supported inside spans:
|
|
|
+
|
|
|
+```
|
|
|
+[**Bold** and *italic* text]{.emphasized}
|
|
|
+```
|
|
|
+
|
|
|
+### Attribute syntax
|
|
|
+
|
|
|
+| Syntax | Result |
|
|
|
+|---|---|
|
|
|
+| `{key="value"}` | `{ key: "value" }` |
|
|
|
+| `{boolflag}` | `{ boolflag: true }` |
|
|
|
+| `{.class-one .class-two}` | `{ class: "class-one class-two" }` |
|
|
|
+| `{#my-id}` | `{ id: "my-id" }` |
|
|
|
+
|
|
|
+## What's implemented vs deferred
|
|
|
+
|
|
|
+### ✅ Implemented
|
|
|
+
|
|
|
+| Feature | Nodes |
|
|
|
+|---|---|
|
|
|
+| `::component{attrs}...::` blocks | `BlockComponent`, `ComponentName`, `ComponentAttrs`, `ComponentBody` |
|
|
|
+| `:component{attrs}` inline | `InlineComponent`, `ComponentName`, `ComponentAttrs` |
|
|
|
+| `:component[body]{attrs}` inline | `InlineComponent`, `ComponentName`, `ComponentBody`, `ComponentAttrs` |
|
|
|
+| `[text]{.class #id}` spans | `Span`, `SpanBody`, `SpanAttrs` |
|
|
|
+| `{key="val" .class #id bool}` attrs | `parseAttrs()` utility |
|
|
|
+
|
|
|
+### ❌ Not yet implemented (future)
|
|
|
+
|
|
|
+| Feature | Priority | Notes |
|
|
|
+|---|---|---|
|
|
|
+| `:emoji:` | Low | Simple pattern extension when needed |
|
|
|
+| `<!-- comments -->` | Low | Simple when needed |
|
|
|
+| Frontmatter `---\nkey: val\n---` | Medium | Useful for property schemas in documents |
|
|
|
+| Block `{attrs}` on `# h1`, `- list` | Low | Trailing `{...}` on standard elements |
|
|
|
+| Code block `{filename}` `{highlights}` | Low | Comark code block metadata |
|
|
|
+| Component `#slot` | Low | Named slots for complex components |
|
|
|
+| YAML block props inside components | Medium | `\`\`\`yaml [props]\nkey: val\n\`\`\`` inside components |
|
|
|
+| Nested components `:::outer\n:::inner\n:::\n::` | Low | Component nesting |
|
|
|
+| Property `<`/`>`/`==` queries | Medium | Type-aware comparison ops in query attrs |
|
|
|
+
|
|
|
+## What the editor already handles (GFM + custom)
|
|
|
+
|
|
|
+The editor's `@lezer/markdown` parser is configured with `GFM` (tables, task lists, strikethrough, autolinks) plus custom extensions for page refs, block refs, tags, highlights, inline math, and task states. Comark extensions add on top of these — they don't replace them.
|
|
|
+
|
|
|
+## Roadmap
|
|
|
+
|
|
|
+### Phase 1 — Grammar (this package)
|
|
|
+- [x] Block component parser
|
|
|
+- [x] Inline component parser
|
|
|
+- [x] Span attribute parser
|
|
|
+- [x] Attrs utility
|
|
|
+- [x] TypeScript types
|
|
|
+
|
|
|
+### Phase 2 — Editor integration (in @enesis/editor)
|
|
|
+- [ ] Wire `createComarkExtensions()` into `markdown-extensions.ts`
|
|
|
+- [ ] Create `createBlockComponentRule()` and `createInlineComponentRule()` in the rule engine
|
|
|
+- [ ] Build component registry (`comark-registry.ts`)
|
|
|
+- [ ] Build `::note`/`::tip`/`::warning`/`::caution` callout renderers
|
|
|
+- [ ] Build `::query` renderer with GraphService
|
|
|
+- [ ] Add `:` trigger to pattern plugin for property autocomplete
|
|
|
+
|
|
|
+### Phase 3 — Typed properties
|
|
|
+- [ ] Add `type` column to properties table
|
|
|
+- [ ] Index typed properties from `:priority{type="enum" value="high"}`
|
|
|
+- [ ] Property schema definitions (`::schema` block or frontmatter)
|
|
|
+- [ ] Property autocomplete with type-aware values
|
|
|
+
|
|
|
+### Phase 4 — Advanced comark features
|
|
|
+- [ ] Block `{attrs}` on headings, lists, paragraphs, blockquotes
|
|
|
+- [ ] Element `{attrs}` on bold, italic, links, images, inline code
|
|
|
+- [ ] Span vs. block attribute disambiguation (`[span]{attrs}` vs `[text] {attrs}`)
|
|
|
+- [ ] Frontmatter parsing
|
|
|
+- [ ] YAML block props
|
|
|
+- [ ] Named slots (`#slot-name` inside block components)
|
|
|
+- [ ] Nested components (`:::outer\n:::inner:::`)
|
|
|
+- [ ] Emoji (`:smile:`)
|
|
|
+- [ ] Comments (`<!-- -->`)
|
|
|
+- [ ] Data binding (`:` prefixed attrs — semantic, not parsing)
|