Path: blob/master/src/packages/frontend/editors/slate/markdown-to-slate/parse.ts
1697 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Descendant } from "slate";6import { handlers } from "./register";7import { State, Token } from "./types";8import { parse_markdown } from "./parse-markdown";9import { ensureDocNonempty } from "../padding";10import { createMetaNode } from "../elements/meta/type";11import { createReferencesNode } from "../elements/references/type";12import normalize from "./normalize";13import { len } from "@cocalc/util/misc";1415export function parse(token: Token, state: State, cache?): Descendant[] {16// console.log("parse", JSON.stringify({ token, state }));1718if (token.type == "image") {19// The image token that comes out of markdown-it is very weird, since if you do20// [foo](bar.png)21// then it makes foo be the *child* of bar.png and sets no alt tag. That just22// makes absolutely no sense at all, so we workaround this (bug?!) as follows.23// If this bug gets fixed upstream, then I guess the code below would safely become a no-op.24// I should report this.25if ((token.children?.length ?? 0) > 0) {26if (token.attrs != null && token.children?.[0].content != null) {27// checks above to make typescript happy28token.attrs[1] = ["alt", token.children[0].content];29}30token.children = [];31}32}33for (const handler of handlers) {34const nodes: Descendant[] | undefined = handler({ token, state, cache });35if (nodes != null) {36// console.log("parse got ", JSON.stringify(nodes));37return nodes;38}39}4041throw Error(42`some handler must process every token -- ${JSON.stringify(token)}`43);44}4546export function markdown_to_slate(47markdown: string,48no_meta?: boolean,49cache?50): Descendant[] {51// Parse the markdown:52// const t0 = Date.now();53const { tokens, meta, lines, references } = parse_markdown(markdown, no_meta);54// window.markdown_parse = { tokens, meta, lines, references };5556const doc: Descendant[] = [];57if (meta != null) {58doc.push(createMetaNode(meta));59}60const state: State = { marks: {}, nesting: 0, lines };61for (const token of tokens) {62for (const node of parse(token, state, cache)) {63doc.push(node);64}65}66if (references != null && len(references) > 0) {67doc.push(createReferencesNode(references));68}6970ensureDocNonempty(doc);7172/*73Why normalize? It's critial that the slatejs74tree produced by this code is normalized, as defined here:7576https://docs.slatejs.org/concepts/10-normalizing7778... and also as it is carried out in practice with our normalization plugins79that are in ../normalize.ts.8081The reason is that any time normalization results in a change from the82source markdown document, then every single update to the document83keeps redoing exactly that extra update! This leads to extensive problems.84If you suspect this, enable EXPENSIVE_DEBUG in ./editable-markdown.tsx85and edit a document, watching the console.log.8687I've tried to make it so the parser here is always normalized. However,88there always seem to be really subtle edge cases. Also, in the long run89other people working on this code could add normalizations to90./normalize.ts and mess up this parser ever so slightly. So instead,91we just always normalize. This isn't too expensive, and is worth it92to ensure sanity.93*/94// console.log(95// "time: markdown_to_slate without normalize",96// Date.now() - t0,97// "ms"98// );99const ndoc = normalize(doc);100101// console.log("time: markdown_to_slate", Date.now() - t0, "ms");102// console.log({ markdown_to_slate: JSON.stringify(doc) });103return ndoc;104}105106107