Skip to content

Instantly share code, notes, and snippets.

@mgreystone
Created March 14, 2023 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mgreystone/3cfeed5b8b08ef2a1a38228ddb8c6ee3 to your computer and use it in GitHub Desktop.
Save mgreystone/3cfeed5b8b08ef2a1a38228ddb8c6ee3 to your computer and use it in GitHub Desktop.
ot-prosemirror.ts
import { Node, Schema } from 'prosemirror-model'
import { Mappable, Mapping, MapResult, Step } from 'prosemirror-transform'
import type ShareDB from 'sharedb'
type Type = Parameters<typeof ShareDB.types.register>[0]
function mapToSide (source: Mappable, side: 'left' | 'right'): Mappable {
const assoc = side === 'left' ? -1 : 1
return {
map (pos: number): number {
return source.map(pos, assoc)
},
mapResult (pos: number): MapResult {
return source.mapResult(pos, assoc)
}
}
}
export function createProseMirrorType (name: string, uri: string, schema: Schema): Type {
function create (doc?: unknown): unknown {
let node
if (doc instanceof Node) node = doc
else if (doc != null) node = Node.fromJSON(schema, doc)
else node = schema.nodes.doc.createAndFill() ?? schema.nodes.doc.create()
return node.toJSON()
}
function apply (doc: unknown, steps: unknown[]): unknown {
const node = Node.fromJSON(schema, doc)
const next = steps.reduce<Node>((acc, step) => {
const result = deserializeStep(step).apply(acc)
if (result.failed != null || result.doc == null) {
throw new Error(result.failed ?? 'Could not apply ProseMirror Steps')
}
return result.doc
}, node)
return next.toJSON()
}
function transform (a: unknown[], b: unknown[], side: 'left' | 'right'): unknown[] {
const mapping = mapToSide(getMapping(b), side)
return a.map(step => {
const result = deserializeStep(step).map(mapping)
if (result == null) throw new Error('Could not transform ProseMirror Step')
return result.toJSON()
})
}
function compose (a: unknown[], b: unknown[]): unknown[] {
return [...a, ...b]
}
function invertWithDoc (steps: unknown[], doc: unknown): unknown[] {
const result = []
let node = Node.fromJSON(schema, doc)
for (let idx = steps.length - 1; idx >= 0; idx++) {
const inverted = Step.fromJSON(schema, steps[idx]).invert(node)
const stepResult = inverted.apply(node)
if (stepResult.failed != null || stepResult.doc == null) {
throw new Error(stepResult.failed ?? 'Could not invert ProseMirror Steps')
}
node = stepResult.doc
result.unshift(inverted.toJSON())
}
return result
}
function transformCursor (cursor: number, steps: unknown[], isOwnOp: boolean): number {
const mapping = getMapping(steps)
return mapping.map(cursor, isOwnOp ? -1 : 1)
}
function serialize (doc: unknown): unknown {
return doc instanceof Node ? doc.toJSON() : doc
}
function deserialize (val: any): unknown {
return val
}
function deserializeStep (val: any): Step {
return val instanceof Step ? val : Step.fromJSON(schema, val)
}
function getMapping (steps: unknown[]): Mapping {
return new Mapping(steps.map(step => deserializeStep(step).getMap()))
}
return {
apply,
compose,
create,
deserialize,
invertWithDoc,
name,
serialize,
transform,
transformCursor,
uri
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment