ElyMark: a table stays a table whatever its spacing; edits keep the document's table style (R2.13)
This commit is contained in:
5 files changed
+31
-7
No files matched your search
@@ -1,5 +1,5 @@
|
||||
// Blocks Milkdown cannot write back stay raw text (§14.3).
|
||||
import { editorViewCtx, remarkCtx, schemaCtx, serializerCtx } from '@milkdown/kit/core'
|
||||
import { editorViewCtx, parserCtx, remarkCtx, schemaCtx, serializerCtx } from '@milkdown/kit/core'
|
||||
import { Fragment } from '@milkdown/kit/prose/model'
|
||||
import { Selection } from '@milkdown/kit/prose/state'
|
||||
import { $nodeSchema } from '@milkdown/kit/utils'
|
||||
@@ -51,6 +51,10 @@ export function faithful(ctx, original) {
|
||||
const schema = ctx.get(schemaCtx)
|
||||
const serializer = ctx.get(serializerCtx)
|
||||
const written = (nodes) => serializer(schema.topNodeType.create(null, nodes)).replace(/\n+$/, '')
|
||||
const parser = ctx.get(parserCtx)
|
||||
// A table's spacing is free; its cells must match.
|
||||
const kept = (node, text) =>
|
||||
written([node]) === text || (node.type.name === 'table' && parser(text).firstChild?.eq(node))
|
||||
const rawOf = ([from, to]) => {
|
||||
const text = original.slice(from, to)
|
||||
return schema.nodes.ely_raw.create(null, text ? schema.text(text) : null)
|
||||
@@ -68,8 +72,7 @@ export function faithful(ctx, original) {
|
||||
if (!blank)
|
||||
parsed.forEach((node, _, index) => {
|
||||
gap(last, at[index][0])
|
||||
const exact = written([node]) === original.slice(...at[index])
|
||||
nodes.push(exact ? node : rawOf(at[index]))
|
||||
nodes.push(kept(node, original.slice(...at[index])) ? node : rawOf(at[index]))
|
||||
places.push(at[index])
|
||||
last = at[index][1]
|
||||
})
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// The document's own markers, so blocks write back unchanged.
|
||||
import { unified } from 'unified'
|
||||
import remarkParse from 'remark-parse'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { visit } from 'unist-util-visit'
|
||||
|
||||
/** remark-stringify options most of `text` already uses. */
|
||||
/** Writing options most of `text` already uses; tables' for remark-gfm. */
|
||||
export function style(text) {
|
||||
const votes = {}
|
||||
const vote = (key, value) => {
|
||||
votes[key] ??= new Map()
|
||||
votes[key].set(value, (votes[key].get(value) ?? 0) + 1)
|
||||
}
|
||||
visit(unified().use(remarkParse).parse(text), (node) => {
|
||||
visit(unified().use(remarkParse).use(remarkGfm).parse(text), (node) => {
|
||||
const at = node.position?.start.offset
|
||||
if (at === undefined) return
|
||||
const head = text.slice(at, at + 12)
|
||||
@@ -34,6 +35,16 @@ export function style(text) {
|
||||
case 'heading':
|
||||
if (node.depth <= 2) vote('setext', head[0] !== '#')
|
||||
break
|
||||
case 'table': {
|
||||
const rows = text.slice(at, node.position.end.offset).split('\n').map((row) => row.trim())
|
||||
const padded = /\| /.test(rows[0])
|
||||
const cells = rows.flatMap((row) => row.replace(/^\||\|$/g, '').split('|'))
|
||||
const inner = (cell) => (padded ? cell.replace(/^ | $/g, '') : cell)
|
||||
vote('tableCellPadding', padded)
|
||||
// Aligned cells carry spaces beyond the padding.
|
||||
vote('tablePipeAlign', cells.some((cell) => /^\s|\s$/.test(inner(cell))))
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
const most = (key) => [...votes[key].entries()].sort((a, b) => b[1] - a[1])[0][0]
|
||||
|
||||
@@ -19,7 +19,7 @@ import { keymap } from '@milkdown/kit/prose/keymap'
|
||||
import { liftListItem, sinkListItem } from '@milkdown/kit/prose/schema-list'
|
||||
import { redo, redoDepth, undoDepth } from '@milkdown/kit/prose/history'
|
||||
import { $prose, callCommand } from '@milkdown/kit/utils'
|
||||
import { insertTableCommand } from '@milkdown/kit/preset/gfm'
|
||||
import { insertTableCommand, remarkGFMPlugin } from '@milkdown/kit/preset/gfm'
|
||||
import {
|
||||
addColumnAfter,
|
||||
addColumnBefore,
|
||||
@@ -87,7 +87,9 @@ export async function visual(root, markdown, editable, texts, changed, edge) {
|
||||
.config((ctx) => {
|
||||
ctx.set(rootCtx, root)
|
||||
ctx.set(defaultValueCtx, markdown)
|
||||
ctx.update(remarkStringifyOptionsCtx, (prev) => ({ ...prev, ...style(markdown) }))
|
||||
const { tableCellPadding, tablePipeAlign, ...marks } = style(markdown)
|
||||
ctx.update(remarkStringifyOptionsCtx, (prev) => ({ ...prev, ...marks }))
|
||||
ctx.set(remarkGFMPlugin.options.key, { tableCellPadding, tablePipeAlign })
|
||||
ctx.update(editorViewOptionsCtx, (prev) => ({ ...prev, editable: () => editable }))
|
||||
})
|
||||
.use(commonmark)
|
||||
|
||||
Reference in new issue
Block a user