content: Markdown table import + 5 tests (round-trip, 460/460)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m6s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m6s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,12 @@
|
|||||||
;; A line-based parser, the inverse of markdown.sx's asMarkdown. Confined to the
|
;; A line-based parser, the inverse of markdown.sx's asMarkdown. Confined to the
|
||||||
;; adapter boundary: the core knows nothing about Markdown. Handles ATX headings
|
;; adapter boundary: the core knows nothing about Markdown. Handles ATX headings
|
||||||
;; (#..######), fenced code (```lang), blockquotes (> ), unordered (- / * ) and
|
;; (#..######), fenced code (```lang), blockquotes (> ), unordered (- / * ) and
|
||||||
;; ordered (1. ) lists, thematic breaks (--- / ***), and paragraphs (consecutive
|
;; ordered (1. ) lists, thematic breaks (--- / ***), pipe tables (header row +
|
||||||
;; plain lines joined with a space). Block ids are assigned sequentially b0,b1…
|
;; --- separator row + body rows), and paragraphs (consecutive plain lines joined
|
||||||
|
;; with a space). Block ids are assigned sequentially b0,b1…
|
||||||
;;
|
;;
|
||||||
;; Requires (loaded by harness): block.sx, doc.sx (and markdown.sx for the
|
;; Requires (loaded by harness): block.sx, doc.sx, table.sx (mk-table); and
|
||||||
;; adapter's export side).
|
;; markdown.sx for the adapter's export side.
|
||||||
|
|
||||||
(define md/-id (fn (i) (str "b" i)))
|
(define md/-id (fn (i) (str "b" i)))
|
||||||
(define md/-blank? (fn (s) (= s "")))
|
(define md/-blank? (fn (s) (= s "")))
|
||||||
@@ -118,6 +119,70 @@
|
|||||||
md/-drop-ul
|
md/-drop-ul
|
||||||
(fn (line) (substring line 2 (string-length line))))
|
(fn (line) (substring line 2 (string-length line))))
|
||||||
|
|
||||||
|
;; ── table detection ──
|
||||||
|
(define md/-pipe-row? (fn (line) (ct-starts-with? (trim line) "|")))
|
||||||
|
(define md/-sep-char? (fn (ch) (ct-in? ch (list "-" ":" "|" " "))))
|
||||||
|
(define
|
||||||
|
md/-all-sep?
|
||||||
|
(fn
|
||||||
|
(s i)
|
||||||
|
(if
|
||||||
|
(>= i (string-length s))
|
||||||
|
true
|
||||||
|
(if
|
||||||
|
(md/-sep-char? (substring s i (+ i 1)))
|
||||||
|
(md/-all-sep? s (+ i 1))
|
||||||
|
false))))
|
||||||
|
(define
|
||||||
|
md/-has-dash?
|
||||||
|
(fn
|
||||||
|
(s i)
|
||||||
|
(if
|
||||||
|
(>= i (string-length s))
|
||||||
|
false
|
||||||
|
(if
|
||||||
|
(= (substring s i (+ i 1)) "-")
|
||||||
|
true
|
||||||
|
(md/-has-dash? s (+ i 1))))))
|
||||||
|
(define
|
||||||
|
md/-sep-row?
|
||||||
|
(fn
|
||||||
|
(line)
|
||||||
|
(and
|
||||||
|
(md/-pipe-row? line)
|
||||||
|
(md/-all-sep? (trim line) 0)
|
||||||
|
(md/-has-dash? line 0))))
|
||||||
|
(define
|
||||||
|
md/-table-start?
|
||||||
|
(fn
|
||||||
|
(lines)
|
||||||
|
(and
|
||||||
|
(md/-pipe-row? (first lines))
|
||||||
|
(> (len lines) 1)
|
||||||
|
(md/-sep-row? (nth lines 1)))))
|
||||||
|
(define
|
||||||
|
md/-strip-pipes
|
||||||
|
(fn
|
||||||
|
(s0)
|
||||||
|
(let
|
||||||
|
((s (trim s0)))
|
||||||
|
(let
|
||||||
|
((a (if (ct-starts-with? s "|") (substring s 1 (string-length s)) s)))
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(> (string-length a) 0)
|
||||||
|
(=
|
||||||
|
(substring
|
||||||
|
a
|
||||||
|
(- (string-length a) 1)
|
||||||
|
(string-length a))
|
||||||
|
"|"))
|
||||||
|
(substring a 0 (- (string-length a) 1))
|
||||||
|
a)))))
|
||||||
|
(define
|
||||||
|
md/-cells
|
||||||
|
(fn (line) (map (fn (c) (trim c)) (split (md/-strip-pipes line) "|"))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
md/-plain?
|
md/-plain?
|
||||||
(fn
|
(fn
|
||||||
@@ -168,6 +233,41 @@
|
|||||||
(else
|
(else
|
||||||
(md/-code-collect (rest lines) lang (cons (first lines) body) i acc)))))
|
(md/-code-collect (rest lines) lang (cons (first lines) body) i acc)))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
md/-table-body
|
||||||
|
(fn
|
||||||
|
(lines headers rows i acc)
|
||||||
|
(if
|
||||||
|
(= (len lines) 0)
|
||||||
|
(md/-walk
|
||||||
|
lines
|
||||||
|
(+ i 1)
|
||||||
|
(cons (mk-table (md/-id i) headers (reverse rows)) acc))
|
||||||
|
(let
|
||||||
|
((line (first lines)))
|
||||||
|
(if
|
||||||
|
(md/-pipe-row? line)
|
||||||
|
(md/-table-body
|
||||||
|
(rest lines)
|
||||||
|
headers
|
||||||
|
(cons (md/-cells line) rows)
|
||||||
|
i
|
||||||
|
acc)
|
||||||
|
(md/-walk
|
||||||
|
lines
|
||||||
|
(+ i 1)
|
||||||
|
(cons (mk-table (md/-id i) headers (reverse rows)) acc)))))))
|
||||||
|
(define
|
||||||
|
md/-table
|
||||||
|
(fn
|
||||||
|
(lines i acc)
|
||||||
|
(md/-table-body
|
||||||
|
(rest (rest lines))
|
||||||
|
(md/-cells (first lines))
|
||||||
|
(list)
|
||||||
|
i
|
||||||
|
acc)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
md/-list-collect
|
md/-list-collect
|
||||||
(fn
|
(fn
|
||||||
@@ -256,6 +356,7 @@
|
|||||||
(rest lines)
|
(rest lines)
|
||||||
(+ i 1)
|
(+ i 1)
|
||||||
(cons (mk-divider (md/-id i)) acc)))
|
(cons (mk-divider (md/-id i)) acc)))
|
||||||
|
((md/-table-start? lines) (md/-table lines i acc))
|
||||||
((md/-ul? line) (md/-list-collect lines (list) i acc false))
|
((md/-ul? line) (md/-list-collect lines (list) i acc false))
|
||||||
((md/-ol? line) (md/-list-collect lines (list) i acc true))
|
((md/-ol? line) (md/-list-collect lines (list) i acc true))
|
||||||
(else (md/-para-collect lines (list) i acc)))))))
|
(else (md/-para-collect lines (list) i acc)))))))
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
"crdt": {"pass": 34, "fail": 0},
|
"crdt": {"pass": 34, "fail": 0},
|
||||||
"crdt-store": {"pass": 14, "fail": 0},
|
"crdt-store": {"pass": 14, "fail": 0},
|
||||||
"sync": {"pass": 14, "fail": 0},
|
"sync": {"pass": 14, "fail": 0},
|
||||||
"md-import": {"pass": 24, "fail": 0},
|
"md-import": {"pass": 29, "fail": 0},
|
||||||
"fed": {"pass": 20, "fail": 0}
|
"fed": {"pass": 20, "fail": 0}
|
||||||
},
|
},
|
||||||
"total_pass": 455,
|
"total_pass": 460,
|
||||||
"total_fail": 0,
|
"total_fail": 0,
|
||||||
"total": 455
|
"total": 460
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| crdt | 34 | 0 | 34 |
|
| crdt | 34 | 0 | 34 |
|
||||||
| crdt-store | 14 | 0 | 14 |
|
| crdt-store | 14 | 0 | 14 |
|
||||||
| sync | 14 | 0 | 14 |
|
| sync | 14 | 0 | 14 |
|
||||||
| md-import | 24 | 0 | 24 |
|
| md-import | 29 | 0 | 29 |
|
||||||
| fed | 20 | 0 | 20 |
|
| fed | 20 | 0 | 20 |
|
||||||
| **Total** | **455** | **0** | **455** |
|
| **Total** | **460** | **0** | **460** |
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
(st-bootstrap-classes!)
|
(st-bootstrap-classes!)
|
||||||
(content/bootstrap!)
|
(content/bootstrap!)
|
||||||
(content-bootstrap-markdown!)
|
(content-bootstrap-markdown!)
|
||||||
|
(content-bootstrap-table!)
|
||||||
|
|
||||||
(define nl (str "\n"))
|
(define nl (str "\n"))
|
||||||
|
|
||||||
@@ -81,6 +82,54 @@
|
|||||||
(doc-ids (md/import (str nl nl) "d"))
|
(doc-ids (md/import (str nl nl) "d"))
|
||||||
(list))
|
(list))
|
||||||
|
|
||||||
|
;; ── pipe tables ──
|
||||||
|
(define
|
||||||
|
dt
|
||||||
|
(md/import
|
||||||
|
(str
|
||||||
|
"| Name | Age |"
|
||||||
|
nl
|
||||||
|
"| --- | --- |"
|
||||||
|
nl
|
||||||
|
"| Ada | 36 |"
|
||||||
|
nl
|
||||||
|
"| Al | 40 |")
|
||||||
|
"d"))
|
||||||
|
(content-test "table import type" (doc-types dt) (list "table"))
|
||||||
|
(content-test
|
||||||
|
"table headers"
|
||||||
|
(table-headers (doc-find dt "b0"))
|
||||||
|
(list "Name" "Age"))
|
||||||
|
(content-test
|
||||||
|
"table rows"
|
||||||
|
(table-rows (doc-find dt "b0"))
|
||||||
|
(list (list "Ada" "36") (list "Al" "40")))
|
||||||
|
(content-test
|
||||||
|
"table round-trip"
|
||||||
|
(asMarkdown
|
||||||
|
(md/import (str "| A | B |" nl "| --- | --- |" nl "| 1 | 2 |") "d"))
|
||||||
|
(str "| A | B |" nl "| --- | --- |" nl "| 1 | 2 |"))
|
||||||
|
(define
|
||||||
|
dmix
|
||||||
|
(md/import
|
||||||
|
(str
|
||||||
|
"# Title"
|
||||||
|
nl
|
||||||
|
nl
|
||||||
|
"| H1 | H2 |"
|
||||||
|
nl
|
||||||
|
"| --- | --- |"
|
||||||
|
nl
|
||||||
|
"| a | b |"
|
||||||
|
nl
|
||||||
|
nl
|
||||||
|
"para")
|
||||||
|
"d"))
|
||||||
|
(content-test
|
||||||
|
"table mixed types"
|
||||||
|
(doc-types dmix)
|
||||||
|
(list "heading" "table" "text"))
|
||||||
|
|
||||||
;; ── round-trip: import . export == identity (canonical markdown) ──
|
;; ── round-trip: import . export == identity (canonical markdown) ──
|
||||||
(define
|
(define
|
||||||
src
|
src
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ injected adapter, not core.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/content/conformance.sh` → **455/455** (Phases 1–4 COMPLETE + 13 extensions: HTML/SX escaping, Markdown render+import, CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees, doc stats, table block, HTML page wrapper)
|
`bash lib/content/conformance.sh` → **460/460** (Phases 1–4 COMPLETE + 13 extensions: HTML/SX escaping, Markdown render + import incl. tables, CRDT replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees, doc stats, table block, HTML page wrapper)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
|||||||
- [x] Markdown render mode (`asMarkdown:` / `content/render doc "md"`)
|
- [x] Markdown render mode (`asMarkdown:` / `content/render doc "md"`)
|
||||||
- [x] durable CRDT replication (`crdt-store.sx`: ops on persist, replay + converge)
|
- [x] durable CRDT replication (`crdt-store.sx`: ops on persist, replay + converge)
|
||||||
- [x] document validation (`validate.sx`: ids, per-type fields, duplicate ids; tree-aware — descends into sections, tree-wide dup ids, section field check)
|
- [x] document validation (`validate.sx`: ids, per-type fields, duplicate ids; tree-aware — descends into sections, tree-wide dup ids, section field check)
|
||||||
- [x] Markdown import adapter (`md-import.sx`: text → blocks, round-trips export)
|
- [x] Markdown import adapter (`md-import.sx`: text → blocks, round-trips export; incl. pipe tables)
|
||||||
- [x] snapshot cache over replay (`snapshot.sx`: cache-not-primary, transparent)
|
- [x] snapshot cache over replay (`snapshot.sx`: cache-not-primary, transparent)
|
||||||
- [x] document metadata (`meta.sx`: title/slug/tags + Ghost title plumbing)
|
- [x] document metadata (`meta.sx`: title/slug/tags + Ghost title plumbing)
|
||||||
- [x] plain-text render + excerpt (`text.sx`: asText, content/excerpt)
|
- [x] plain-text render + excerpt (`text.sx`: asText, content/excerpt)
|
||||||
@@ -92,6 +92,11 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- 2026-06-07 — Extension: Markdown table import. `md-import.sx` now recognizes a
|
||||||
|
`| … |` header row followed by a `| --- |` separator and parses a `CtTable`
|
||||||
|
(cells trimmed, mixed with other blocks via blank-line separation), completing
|
||||||
|
the Markdown table round-trip (import∘export == identity). +5 tests; suite
|
||||||
|
460/460.
|
||||||
- 2026-06-07 — Extension: HTML page wrapper (`page.sx`). `content/page` composes
|
- 2026-06-07 — Extension: HTML page wrapper (`page.sx`). `content/page` composes
|
||||||
metadata + render into a minimal valid HTML5 document — escaped `<title>` from
|
metadata + render into a minimal valid HTML5 document — escaped `<title>` from
|
||||||
doc metadata (falling back to id) and the rendered blocks as the body.
|
doc metadata (falling back to id) and the rendered blocks as the body.
|
||||||
|
|||||||
Reference in New Issue
Block a user