diff --git a/package.json b/package.json index 2f1c070..3100413 100644 --- a/package.json +++ b/package.json @@ -102,13 +102,18 @@ "clone-deep": "^4.0.1", "electron-context-menu": "^2.4.0", "electron-squirrel-startup": "^1.0.0", + "hast-util-from-string": "^1.0.4", "hast-util-to-html": "^7.1.2", + "hast-util-to-mdast": "^7.1.3", "immer": "^8.0.1", "lit-element": "^2.4.0", "lit-html": "^1.3.0", + "mdast-builder": "^1.1.1", "mdast-util-compact": "^3.0.0", "mdast-util-from-markdown": "^0.8.4", + "mdast-util-gfm": "^0.1.1", "mdast-util-to-markdown": "^0.6.2", + "rehype-parse": "^7.0.1", "remark-highlight.js": "^6.0.0", "remark-html": "^13.0.1", "remark-parse": "^9.0.0", diff --git a/src/ast/backspace-paragraph.ts b/src/ast/backspace-paragraph.ts index 559a22c..87ecb61 100644 --- a/src/ast/backspace-paragraph.ts +++ b/src/ast/backspace-paragraph.ts @@ -1,11 +1,11 @@ import {visit, EXIT} from "unist-utils-core" import * as md from "mdast" import compact from "mdast-util-compact" +import {DataCaret} from "../caret" export default function backspaceParagraph( root: md.Parent, - target: md.Paragraph, - id?: string + target: md.Paragraph ): void { visit(root, target, (node, index, parents) => { let parent = parents[parents.length - 1] @@ -19,13 +19,27 @@ export default function backspaceParagraph( if (Array.isArray(previous.children)) { let length = previous.children.length + let lastChildOfPrevious = previous.children[length - 1] + while (lastChildOfPrevious.children) { + let len = lastChildOfPrevious.children.length - 1 + lastChildOfPrevious = lastChildOfPrevious.children[len] + } + previous.children.splice(length, 0, ...node.children) - parent.children[index - 1] = compact(previous) - // TODO come back and use merge - if (id) { - let child = previous.children[length - 1] - child.id = id + parent.children[index - 1] = previous + let n = node.children[0] + + while (n.children) { + n = n.children[0] } + + let caret: DataCaret = { + caretStart: 0, + caretEnd: 0, + } + + n.data = {caret} + // TODO come back and use merge } else { throw new TypeError("line before somehow has no children?") } diff --git a/src/ast/convert-to-html.ts b/src/ast/convert-to-html.ts deleted file mode 100644 index 75cf103..0000000 --- a/src/ast/convert-to-html.ts +++ /dev/null @@ -1,287 +0,0 @@ -import type * as md from "mdast" -import {html, TemplateResult} from "lit-html" -import {spread} from "@open-wc/lit-helpers" -import * as is from "./is" -import {DataCaret} from "../caret" - -type Handler = (node: md.Content) => any - -interface Handlers { - [type: string]: Handler -} - -export function spreadable(node: md.Content | md.Root) { - let {data, position, children, ...spreadable} = node - if (is.leaf(node.type)) { - spreadable.leaf = true - } - if (is.block(node.type)) { - spreadable.block = true - } - if (is.inline(node.type)) { - spreadable.inline = true - } - if (is.container(node.type)) { - spreadable.container = true - } - if (is.empty(node.type)) { - spreadable.empty = true - } - - if (data && data.caret) { - let caret: DataCaret = data.caret as DataCaret - spreadable.caret = caret.type - spreadable.caretOffset = caret.offset - } - return spreadable -} - -let handlers: Handlers = { - blockquote(node) { - node = node as md.Blockquote - return html`` - }, - - break(node) { - node = node as md.Break - return html`` - // return html`
` - }, - - code(node) { - node = node as md.Code - return html`` - }, - - delete(node) { - node = node as md.Delete - return html`` - }, - - emphasis(node) { - node = node as md.Emphasis - return html`` - }, - - footnoteReference(node) { - node = node as md.FootnoteReference - return html`` - }, - - footnote(node) { - node = node as md.Footnote - return html`` - }, - - heading(node) { - node = node as md.Heading - let h - switch (node.depth) { - case 1: - h = html`

` - break - case 2: - h = html`

` - break - case 3: - h = html`

` - break - case 4: - h = html`

` - break - case 5: - h = html`
` - break - case 6: - h = html`
` - break - } - return html`` - }, - - html(node) { - node = node as md.HTML - return html`` - }, - - imageReference(node) { - node = node as md.ImageReference - return html`` - }, - - image(node) { - node = node as md.Image - return html`` - }, - - inlineCode(node) { - node = node as md.InlineCode - return html`` - }, - - linkReference(node) { - node = node as md.LinkReference - return html`` - }, - - link(node) { - node = node as md.Link - return html`` - }, - - listItem(node) { - node = node as md.ListItem - return html`` - }, - - list(node) { - node = node as md.List - return html`` - }, - - paragraph(node) { - node = node as md.Paragraph - return html`` - }, - - // root(node) { - //node = node as md.Root - /* return html` - - ` */ - //return children(node) - // }, - - strong(node) { - node = node as md.Strong - return html`` - }, - - table(node) { - node = node as md.Table - return html`` - }, - - text(node) { - // You probably don't want this. - // remember: if you enable this, every text (including those inside an ) - // becomes a mayo- element - // node = node as md.Text - // if (parent.type == "emphasis") { - // return node.value - // } else { - // return html`${node.value}` - return node.value - }, - - thematicBreak(node) { - node = node as md.ThematicBreak - return html`` - }, - - toml(node) { - node = node as md.FrontmatterContent - return html`` - }, - - yaml(node) { - node = node as md.FrontmatterContent - return html`` - }, - - definition(node) { - node = node as md.Definition - return html`` - }, - - footnoteDefinition(node) { - node = node as md.FootnoteDefinition - return html`` - }, -} - -export default function convertToHtml( - node: md.Content -): TemplateResult | string { - let handler = handlers[node.type] - if (handler) { - return handler(node) - } else { - throw new Error(node.type) - } -} diff --git a/src/ast/insert-paragraph.ts b/src/ast/insert-paragraph.ts index a42cd2b..40ea18f 100644 --- a/src/ast/insert-paragraph.ts +++ b/src/ast/insert-paragraph.ts @@ -5,12 +5,12 @@ import u from "unist-builder" import * as is from "./is" import {remove} from "./utils" import split from "./split" +import {DataCaret} from "../caret" export default function insertParagraph( root: md.Parent, target: md.Text | md.InlineCode, - offset: number, - id?: string + offset: number ): void { if (!target || (target.type != "text" && target.type != "inlineCode")) { throw new Error( @@ -18,24 +18,33 @@ export default function insertParagraph( ) } let [left, right] = split(root, target, offset) + let t = selectAll("text, inlineCode", right) if (t.length == 1) { let [node] = t if (node.value.length == 0) { node.type = "text" - node.value = " " + node.value = "" } } + let caret: DataCaret = { + caretStart: 0, + caretEnd: 0, + } + + t[0].data = { + caret, + } visit(root, target, (node, index, parents) => { node.value = left.value - let firstp = parents[parents.length - 1] - let prest = firstp.children.slice(index + 1) + let directParent = parents[parents.length - 1] + let directParentNodesAfter = directParent.children.slice(index + 1) let rest: unist.Node[] = [] if (target.type == "text" || target.type == "inlineCode") { parents.forEach((parent, index) => { - if (is.inline(parent.type)) { + if (is.inline(parent)) { let idx = parents[index - 1].children.indexOf(parent) parents[index - 1].children.splice(idx, 1, left) if (!rest.length) { @@ -46,21 +55,21 @@ export default function insertParagraph( }) } parents.forEach((parent, index) => { - if (is.block(parent.type) && is.leaf(parent.type)) { + if (is.block(parent) && is.leaf(parent)) { let idx = parents[index - 1].children.indexOf(parent) - let opts: {id?: string} = {} - if (id) { - opts.id = id - } - prest.forEach((n: unist.Node) => remove(parents[index - 1], {}, n)) - if (parent != firstp) { - prest = [] + + directParentNodesAfter.forEach((n: unist.Node) => + remove(parents[index - 1], {}, n) + ) + if (parent != directParent) { + directParentNodesAfter = [] } parents[index - 1].children.splice( idx + 1, 0, - u("paragraph", opts, [right, ...rest, ...prest]) + u("paragraph", [right, ...rest, ...directParentNodesAfter]) ) + return } }) return EXIT diff --git a/src/ast/is.ts b/src/ast/is.ts index 51fbe1f..8e7a6ed 100644 --- a/src/ast/is.ts +++ b/src/ast/is.ts @@ -1,3 +1,14 @@ +import * as md from "mdast" + +export type MarkdownLeafBlock = + | md.ThematicBreak + | md.Heading + | md.Code + | md.HTML + | md.LinkReference + | md.Paragraph + | md.Break + let leafBlock = [ "thematicBreak", "heading", @@ -8,6 +19,14 @@ let leafBlock = [ "break", ] +export type MarkdownEmpty = + | md.Break + | md.ThematicBreak + | md.Link + | md.LinkReference + | md.Image + | md.ImageReference + let empties = [ "break", "thematicBreak", @@ -17,40 +36,68 @@ let empties = [ "imageReference", ] -let leafInline = ["inlineCode"] +export type MarkdownLeafInline = md.InlineCode | md.Text + +let leafInline = ["inlineCode", "text"] + +export type MarkdownContainerBlock = md.Blockquote | md.ListItem | md.List let containerBlock = ["blockquote", "listItem", "list"] +export type MarkdownContainerInline = md.Delete | md.Emphasis | md.Strong + let containerInline = ["delete", "emphasis", "strong"] +export type MarkdownInline = MarkdownLeafInline | MarkdownContainerInline + let inlines = [...leafInline, ...containerInline] -let leaves = [...leafBlock] +export type MarkdownLeaf = MarkdownLeafBlock | MarkdownLeafInline + +let leaves = [...leafBlock, ...leafInline] + +export type MarkdownBlock = MarkdownLeafBlock | MarkdownContainerBlock let blocks = [...leafBlock, ...containerBlock] -let containers = [...containerBlock, "root"] +export type MarkdownContainer = + | MarkdownContainerBlock + | MarkdownContainerInline + | md.Root + +let containers = [...containerInline, ...containerBlock, "root"] + +import type * as unist from "unist" +import {Unwrappable} from "./unwrap" + +export function empty(node: unist.Node): node is MarkdownEmpty { + return empties.includes(node.type) +} + +export function leaf(node: unist.Node): node is MarkdownLeaf { + return leaves.includes(node.type) +} -export function empty(type: string): boolean { - return empties.includes(type) +export function inline(node: unist.Node): node is MarkdownInline { + return inlines.includes(node.type) } -export function leaf(type: string): boolean { - return leaves.includes(type) +export function container(node: unist.Node): node is MarkdownContainer { + return containers.includes(node.type) } -export function inline(type: string): boolean { - return inlines.includes(type) +export function block(node: unist.Node): node is MarkdownBlock { + return blocks.includes(node.type) } -export function container(type: string): boolean { - return containers.includes(type) +export function list(node: unist.Node): node is md.List { + return node.type == "list" } -export function block(type: string): boolean { - return blocks.includes(type) +export function textish(node: unist.Node): node is md.Text | md.InlineCode { + return node.type == "text" || node.type == "inlineCode" } -export function list(type: string): boolean { - return type == "list" +export function unwrappable(node: unist.Node): node is Unwrappable { + return ["emphasis", "delete", "strong", "inlineCode"].includes(node.type) } diff --git a/src/ast/split.ts b/src/ast/split.ts index 015d4f6..532cf15 100644 --- a/src/ast/split.ts +++ b/src/ast/split.ts @@ -25,13 +25,13 @@ export default function split( let idx = highParent.children.indexOf(lowParent) let pc = highParent.children.slice(0, idx) let ac = highParent.children.slice(idx + 1) - if (is.inline(lowParent.type)) { + if (is.inline(lowParent)) { leftNode = u(lowParent.type, [...nextpc, leftNode]) rightNode = u(lowParent.type, [rightNode, ...nextac]) } nextpc = pc nextac = ac - if (is.block(lowParent.type)) { + if (is.block(lowParent)) { break } } diff --git a/src/ast/transform/delete-content-backward.ts b/src/ast/transform/delete-content-backward.ts new file mode 100644 index 0000000..64abc9c --- /dev/null +++ b/src/ast/transform/delete-content-backward.ts @@ -0,0 +1,53 @@ +import type {TransformHandler} from "." +import * as md from "mdast" +import visitParents from "unist-util-visit-parents" +import backspaceParagraph from "../backspace-paragraph" +import * as is from "../is" +import unwrap from "../unwrap" + +let deleteContentBackward: TransformHandler = (root, options) => { + let startNode = options.start.node + let endNode = options.end.node + if (is.leaf(startNode) && is.inline(startNode) && is.block(endNode)) { + backspaceParagraph(root, (endNode as unknown) as md.Paragraph) + return + } + + visitParents( + root, + startNode as md.Text, + (node, parents) => { + let directParent = parents[parents.length - 1] + + if (is.textish(endNode)) { + if (options.end.offset == endNode.value.length) { + if (is.unwrappable(endNode)) { + unwrap(root, endNode) + return + } else if (is.unwrappable(directParent)) { + unwrap(root, directParent) + return + } + } + } + if ( + startNode == endNode && + is.textish(startNode) && + is.textish(endNode) + ) { + startNode.value = + startNode.value.slice(0, options.start.offset) + + startNode.value.slice(options.end.offset) + + startNode.data = { + caret: { + caretStart: options.start.offset, + caretEnd: options.start.offset, + }, + } + } + }, + true + ) +} +export default deleteContentBackward diff --git a/src/ast/transform/index.ts b/src/ast/transform/index.ts index 3ea2167..a513472 100644 --- a/src/ast/transform/index.ts +++ b/src/ast/transform/index.ts @@ -1,13 +1,17 @@ import * as md from "mdast" -import {CaretIdInstruction} from "../caret" -import BeforeInputEvent, {InputType} from "../before-input-event" +import {InputType} from "../../events/before-input-event" import insertText from "./insert-text" +import insertParagraph from "./insert-paragraph" import visit from "unist-util-visit" import type * as unist from "unist" -import {produce} from "immer" +import {DataCaret} from "../../caret" +import deleteContentBackward from "./delete-content-backward" +import * as is from "../is" +import visitParents from "unist-util-visit-parents" +import {Data} from "electron/main" export interface TransformOptions { - data: string + detail: string | md.Root | md.Content inputType: InputType start: { path: string @@ -19,198 +23,190 @@ export interface TransformOptions { } } -export let nothing = Symbol("@transform/don't-actually") - -type TransformResult = CaretIdInstruction | typeof nothing - interface TransformHandlers { [key: string]: TransformHandler } +export let nothing = Symbol("@transform/don't-actually") + export interface TransformHandler { - (root: md.Root, options: TransformHandlerOptions): TransformResult + (root: md.Root, options: TransformHandlerOptions): void | typeof nothing +} + +function restoreCaret(options: TransformHandlerOptions): void { + if (!options.start.node.data?.caret) { + let caret: Partial = {} + options.start.node.data = {caret} + } + if (!options.end.node.data?.caret) { + let caret: Partial = {} + options.end.node.data = {caret} + } + let startCaret: Partial = options.start.node.data.caret + startCaret.caretStart = options.start.offset + let endCaret: Partial = options.end.node.data.caret + endCaret.caretEnd = options.end.offset } export let handlers: TransformHandlers = { insertReplacementText(root, options) { - console.error(`unhandled inputType`) - return nothing + if (typeof options.start.node.value == "string") { + options.end.offset = options.start.offset = options.data.length + } + options.start.node.value = options.data + let caret: DataCaret = { + caretStart: options.data.length, + caretEnd: options.data.length, + } + options.start.node.data = { + caret, + } }, insertText, insertLineBreak(root, options) { - console.error(`unhandled inputType`) - return nothing - }, - insertParagraph(root, options) { - console.error(`unhandled inputType`) - return nothing - }, - deleteContentBackward(root, options) { - console.error(`unhandled inputType`) return nothing }, + insertParagraph, + deleteContentBackward, deleteContentForward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteByCut(root, options) { - console.error(`unhandled inputType`) return nothing }, insertOrderedList(root, options) { - console.error(`unhandled inputType`) return nothing }, insertUnorderedList(root, options) { - console.error(`unhandled inputType`) return nothing }, insertHorizontalRule(root, options) { - console.error(`unhandled inputType`) return nothing }, insertFromYank(root, options) { - console.error(`unhandled inputType`) return nothing }, insertFromDrop(root, options) { - console.error(`unhandled inputType`) return nothing }, insertFromPaste(root, options) { - console.error(`unhandled inputType`) return nothing }, insertFromPasteAsQuotation(root, options) { - console.error(`unhandled inputType`) return nothing }, insertTranspose(root, options) { - console.error(`unhandled inputType`) return nothing }, insertCompositionText(root, options) { - console.error(`unhandled inputType`) return nothing }, insertLink(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteWordBackward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteWordForward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteSoftLineBackward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteSoftLineForward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteEntireSoftLine(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteHardLineBackward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteHardLineForward(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteByDrag(root, options) { - console.error(`unhandled inputType`) return nothing }, deleteContent(root, options) { - console.error(`unhandled inputType`) return nothing }, historyUndo(root, options) { - console.error(`unhandled inputType`) return nothing }, historyRedo(root, options) { - console.error(`unhandled inputType`) return nothing }, formatBold(root, options) { - console.error(`unhandled inputType`) return nothing }, formatItalic(root, options) { - console.error(`unhandled inputType`) return nothing }, formatUnderline(root, options) { - console.error(`unhandled inputType`) return nothing }, formatStrikeThrough(root, options) { - console.error(`unhandled inputType`) return nothing }, formatSuperscript(root, options) { - console.error(`unhandled inputType`) return nothing }, formatSubscript(root, options) { - console.error(`unhandled inputType`) return nothing }, formatJustifyFull(root, options) { - console.error(`unhandled inputType`) return nothing }, formatJustifyCenter(root, options) { - console.error(`unhandled inputType`) return nothing }, formatJustifyRight(root, options) { - console.error(`unhandled inputType`) return nothing }, formatJustifyLeft(root, options) { - console.error(`unhandled inputType`) return nothing }, formatIndent(root, options) { - console.error(`unhandled inputType`) + // TODO lists + visitParents(root, options.start.node, (node, parents) => { + for (let p of parents) { + if (p.type == "heading") { + if (p.depth < 6) p.depth += 1 + restoreCaret(options) + } + } + }) return nothing }, formatOutdent(root, options) { - console.error(`unhandled inputType`) + // TODO lists + visitParents(root, options.start.node, (node, parents) => { + for (let p of parents) { + if (p.type == "heading") { + if (p.depth > 1) p.depth -= 1 + restoreCaret(options) + } + } + }) return nothing }, formatRemove(root, options) { - console.error(`unhandled inputType`) return nothing }, formatSetBlockTextDirection(root, options) { - console.error(`unhandled inputType`) return nothing }, formatSetInlineTextDirection(root, options) { - console.error(`unhandled inputType`) return nothing }, formatBackColor(root, options) { - console.error(`unhandled inputType`) return nothing }, formatFontColor(root, options) { - console.error(`unhandled inputType`) return nothing }, formatFontName(root, options) { - console.error(`unhandled inputType`) return nothing }, } @@ -218,15 +214,33 @@ export let handlers: TransformHandlers = { export interface TransformHandlerOptions { data: string start: { - node: md.Content + node: unist.Node offset: number } end: { - node: md.Content + node: unist.Node offset: number } } +function getFromPath(root: md.Root, path: string): md.Text | md.InlineCode { + let parts = path.slice(1).split(".").map(Number) + let node: unist.Node = root + while (parts.length) { + if (!Array.isArray(node.children)) { + throw new TypeError("no chilcren") + } + let idx = parts.shift() + node = node.children[idx] + } + if (node.type != "text" && node.type != "inlineCode") { + throw new Error( + `path must lead to text or inline code, led to: ${node.type}` + ) + } + return node as md.Text | md.InlineCode +} + export default function transform( root: md.Root, options: TransformOptions @@ -246,9 +260,7 @@ export default function transform( }) let startParts = options.start.path.slice(1).split(".").map(Number) - console.log(startParts) let startNode: unist.Node = root - while (startParts.length) { if (!Array.isArray(startNode.children)) { throw new TypeError("no chilcren") @@ -256,6 +268,7 @@ export default function transform( let idx = startParts.shift() startNode = startNode.children[idx] } + let endParts = options.end.path.slice(1).split(".").map(Number) let endNode: unist.Node = root while (endParts.length) { @@ -265,17 +278,19 @@ export default function transform( endNode = endNode.children[endParts.shift()] } - let opts = { - data: options.data, + let handlerOptions = { + data: options.detail, start: { - node: startNode as md.Content, + node: startNode as md.Text | md.InlineCode, offset: options.start.offset, }, end: { - node: endNode as md.Content, + node: endNode as md.Text | md.InlineCode, offset: options.end.offset, }, } - handler(root, opts) + if (handler(root, handlerOptions) == nothing) { + console.error(`unhandled inputType: ${options.inputType}`) + } } diff --git a/src/ast/transform/insert-paragraph.ts b/src/ast/transform/insert-paragraph.ts new file mode 100644 index 0000000..bbf88dd --- /dev/null +++ b/src/ast/transform/insert-paragraph.ts @@ -0,0 +1,21 @@ +import type {TransformHandler} from "." +// TODO move _insertParagraph in here :D +import _insertParagraph from "../insert-paragraph" +import * as md from "mdast" +import visitParents from "unist-util-visit-parents" + +let insertParagraph: TransformHandler = (root, options) => { + let startNode = options.start.node + let endNode = options.end.node + + visitParents(root, startNode as md.Text, (node, parents) => { + parents.reverse() + let list = parents.find(p => p.type == "list") + }) + + if (startNode == endNode && typeof startNode.value == "string") { + startNode = startNode as md.Text + _insertParagraph(root, startNode, options.start.offset) + } +} +export default insertParagraph diff --git a/src/ast/transform/insert-text.ts b/src/ast/transform/insert-text.ts index 89e22f9..64a1ad8 100644 --- a/src/ast/transform/insert-text.ts +++ b/src/ast/transform/insert-text.ts @@ -1,22 +1,82 @@ import type {TransformHandler} from "." -import {visit} from "unist-utils-core" -import {produce} from "immer" +import inviteParentsOverForDinner from "unist-util-parents" +import {find} from "unist-utils-core" +import * as md from "mdast" +import * as m from "mdast-builder" +import {DataCaret} from "../../caret" +import * as is from "../is" let insertText: TransformHandler = (root, options) => { let startNode = options.start.node let endNode = options.end.node - if (startNode == endNode && typeof startNode.value == "string") { + let treeWithParents = inviteParentsOverForDinner(root) + let node = find(treeWithParents, startNode) + let parent = node.parent + let index = parent.children.indexOf(node) + + if (index == 0 && options.start.offset == 0) { + if (options.data == "#") { + if (parent.type == "paragraph") { + parent = parent as md.Heading + ;(parent.node as md.Heading).type = "heading" + ;(parent.node as md.Heading).depth = 1 + return + } else if (parent.type == "heading") { + parent = parent as md.Heading + if (parent.depth < 6) { + ;(parent.node as md.Heading).depth += 1 + return + } + } + } + + if (options.data == "." || options.data == "-") { + if (parent.type == "paragraph") { + let pnode = parent.node as md.List + pnode.type = "list" + pnode.ordered = options.data == "." + pnode.children = [ + m.listItem(m.paragraph(pnode.children)) as md.ListItem, + ] + return + } + } + } + + // TODO this will involve splitting the text node and inserting an insertCode + // if (options.data == "`") { + // make up my own inputTypes? why not + // tranformHandlers.formatCode(options) + // } + // TODO this will involve splitting the text node and inserting an emphasis + // if (options.data == "_") { + // transformHandlers.formatItalic(options) + // } + // TODO this will involve splitting the text node and inserting a strong + // if (options.data == "*") { + // transformHandlers.formatBold(options) + //} + + if (startNode == endNode) { if (!startNode.data) { startNode.data = {} } - startNode.data.caret = { - type: "collapsed", - offset: options.start.offset + 1, + + let caret: DataCaret = { + caretStart: options.start.offset + options.data.length, + caretEnd: options.start.offset + options.data.length, + } + + startNode.data.caret = caret + + if (is.leaf(startNode) && is.inline(startNode)) { + startNode.value = + startNode.value.slice(0, options.start.offset) + + options.data + + startNode.value.slice(options.end.offset) + } else { + throw new Error("start node isn't inline??") } - startNode.value = - startNode.value.slice(0, options.start.offset) + - options.data + - startNode.value.slice(options.end.offset) } } export default insertText diff --git a/src/ast/unwrap.ts b/src/ast/unwrap.ts index fde5655..9d3a27d 100644 --- a/src/ast/unwrap.ts +++ b/src/ast/unwrap.ts @@ -3,43 +3,58 @@ import * as unist from "unist" import * as md from "mdast" import u from "unist-builder" import * as is from "./is" +import compact from "mdast-util-compact" export type Unwrappable = md.Emphasis | md.Strong | md.Delete | md.InlineCode -export default function unwrap( - root: md.Parent, - target: md.InlineCode, - offset: number -): [unist.Node, unist.Node] { - let leftText = target.value.slice(0, offset) - let rightText = target.value.slice(offset) - - let leftNode: unist.Node = u(target.type, leftText) - let rightNode: unist.Node = u(target.type, rightText) - +export default function unwrap(root: md.Parent, target: Unwrappable): void { visit(root, target, (node, index, parents) => { - let firstp = parents[parents.length - 1] - let nextpc = firstp.children.slice(0, index) - let nextac = firstp.children.slice(index + 1) - for (let i = parents.length - 1; i > 0; i--) { - let highParent = parents[i - 1] - let lowParent = parents[i] - let idx = highParent.children.indexOf(lowParent) - let pc = highParent.children.slice(0, idx) - let ac = highParent.children.slice(idx + 1) - if (is.inline(lowParent.type)) { - leftNode = u(lowParent.type, [...nextpc, leftNode]) - rightNode = u(lowParent.type, [rightNode, ...nextac]) - } - nextpc = pc - nextac = ac - if (is.block(lowParent.type)) { - break + if (is.inline(node)) { + let directParent = parents[parents.length - 1] + if (is.container(node)) { + directParent.children.splice(index, 1, ...node.children) + node.children[node.children.length - 1].data = { + caret: { + caretStart: node.children[node.children.length - 1].value.length, + caretEnd: node.children[node.children.length - 1].value.length, + }, + } + } else if (node.type == "inlineCode") { + ;((node as unknown) as md.Text).type = "text" + node.data = { + caret: { + caretStart: node.value.length, + caretEnd: node.value.length, + }, + } } - } - return EXIT + // TODO this flatten-text might be over the top, or needed elsewhere + directParent.children = directParent.children.reduce( + (children, next) => { + if (children.length) { + let previous = children[children.length - 1] + if (next.type == "text" && previous.type == "text") { + if (next.data?.caret) { + previous.data = { + caret: { + caretStart: + previous.value.length + next.data.caret.caretStart, + caretEnd: previous.value.length + next.data.caret.caretEnd, + }, + } + } + previous.value += next.value + return children + } else { + return children.concat(next) + } + } else { + return children.concat(next) + } + }, + [] + ) + } }) - - return [leftNode, rightNode] } diff --git a/src/caret.ts b/src/caret.ts new file mode 100644 index 0000000..68b50c2 --- /dev/null +++ b/src/caret.ts @@ -0,0 +1,4 @@ +export interface DataCaret { + caretStart: number | null + caretEnd: number | null +} diff --git a/src/default-doc.ts b/src/default-doc.ts index dc5176d..5d04b93 100644 --- a/src/default-doc.ts +++ b/src/default-doc.ts @@ -1,7 +1,8 @@ -export default `# hello \`this\` and _that_ (and \`others\`) +import compact from "mdast-util-compact" -this is an _ordinary **\`document\` about** ordinary_ things, there's **nothing _going_ on** -here of _interest to you_, or me, or anybody else. +export default compact(`# hello \`this\` and _that_ (and \`others\`) + +this is an _ordinary **\`document\` about** ordinary_ things, there's **nothing _going_ on** here of _interest to you_, or me, or anybody else. ## a list @@ -32,4 +33,4 @@ auto sum(std::vector nums) { return result; } \`\`\` -` +`) diff --git a/src/dom/get-transform-options.ts b/src/dom/get-transform-options.ts new file mode 100644 index 0000000..207612b --- /dev/null +++ b/src/dom/get-transform-options.ts @@ -0,0 +1,71 @@ +import type {TransformOptions} from "../ast/transform" +import {InputType} from "../events/before-input-event" +import MayoNodeElement from "../elements/mayo-node" +import htmlToHast from "rehype-parse" +import hastToMdast from "hast-util-to-mdast" +import unified from "unified" + +export interface GetTransformOptionsOptions { + inputType: InputType + range: StaticRange + data?: string + dataTransfer?: DataTransfer +} + +export default async function getTransformOptions({ + inputType, + range, + data, + dataTransfer, +}: GetTransformOptionsOptions): Promise { + let start = range.startContainer.parentElement as MayoNodeElement + let end = range.endContainer.parentElement as MayoNodeElement + let detail = data + if (inputType == "insertReplacementText") { + // i'm going to regret this + // instead i should be creating a placeholder element + detail = await new Promise(yay => dataTransfer.items[0].getAsString(yay)) + } + + if (inputType == "insertFromPaste") { + let items = Array.from(dataTransfer.items) + let imageItem = items.find(item => item.type.startsWith("image")) + let htmlItem = items.find(item => item.type == "text/html") + let textItem = items.find(item => item.type == "text/plain") + if (imageItem) { + // TODO we copy the image + } + + if (htmlItem) { + detail = await new Promise(yay => htmlItem.getAsString(yay)).then( + string => { + let hast = unified().use(htmlToHast).parse(string) + return hastToMdast(hast) + } + ) + } else if (textItem) { + detail = await new Promise(yay => textItem.getAsString(yay)) + inputType = "insertText" + } + } + + if (start.tagName.toLowerCase() != "mayo-node") { + start = start.closest("mayo-node") + } + if (end.tagName.toLowerCase() != "mayo-node") { + end = end.closest("mayo-node") + } + + return { + detail, + start: { + path: start.path, + offset: range.startOffset, + }, + end: { + path: end.path, + offset: range.endOffset, + }, + inputType: inputType, + } +} diff --git a/src/elements/index.ts b/src/elements/index.ts deleted file mode 100644 index 9ace88d..0000000 --- a/src/elements/index.ts +++ /dev/null @@ -1,121 +0,0 @@ -import MayoBreakElement from "./markdown/mayo-break" -import MayoCodeElement from "./markdown/mayo-code" -import MayoInlineCodeElement from "./markdown/mayo-inline-code" -import MayoDeleteElement from "./markdown/mayo-delete" -import MayoEmphasisElement from "./markdown/mayo-emphasis" -import MayoHeadingElement from "./markdown/mayo-heading" -import MayoThematicBreakElement from "./markdown/mayo-thematic-break" -import MayoHtmlElement from "./markdown/mayo-html" -import MayoImageElement from "./markdown/mayo-image" -import MayoImageReferenceElement from "./markdown/mayo-image-reference" -import MayoListItemElement from "./markdown/mayo-list-item" -import MayoLinkElement from "./markdown/mayo-link" -import MayoLinkReferenceElement from "./markdown/mayo-link-reference" -import MayoListElement from "./markdown/mayo-list" -import MayoParagraphElement from "./markdown/mayo-paragraph" -import MayoBlockquoteElement from "./markdown/mayo-blockquote" -import MayoStrongElement from "./markdown/mayo-strong" -import MayoTableElement from "./markdown/mayo-table" -import MayoTextElement from "./markdown/mayo-text" -import MayoTomlElement from "./markdown/mayo-toml" -import MayoYamlElement from "./markdown/mayo-yaml" - -import MayoNodeElement from "./mayo-node" - -import MayoKillerElement from "./mayo-killer" - -import MayoAppElement from "./mayo-app" -import MayoSidebarElement from "./mayo-sidebar" -import MayoSidebarFileElement from "./mayo-sidebar-file" -import MayoSidebarTreeElement from "./mayo-sidebar-tree" -import MayoDocumentElement from "./mayo-document" - -export type MayoStaticPhrasingContentElement = - | MayoBreakElement - | MayoEmphasisElement - | MayoHtmlElement - | MayoImageElement - | MayoImageReferenceElement - | MayoInlineCodeElement - | MayoStrongElement - | MayoTextElement - -export type MayoPhrasingContentElement = - | MayoLinkElement - | MayoLinkReferenceElement - | MayoStaticPhrasingContentElement - -export type MayoListContentElement = MayoListItemElement - -export type MayoFlowContentElement = - | MayoBlockquoteElement - | MayoCodeElement - | MayoHeadingElement - | MayoHtmlElement - | MayoListElement - | MayoThematicBreakElement - | MayoParagraphElement - -export type MayoContentElement = - | MayoFlowContentElement - | MayoListContentElement - | MayoPhrasingContentElement - -export type { - MayoDocumentElement as MayoDocumentElement, - MayoKillerElement, - MayoSidebarElement, - MayoSidebarFileElement, - MayoSidebarTreeElement, - MayoBreakElement, - MayoCodeElement, - MayoDeleteElement, - MayoEmphasisElement, - MayoHeadingElement, - MayoInlineCodeElement, - MayoThematicBreakElement, - MayoHtmlElement, - MayoImageElement, - MayoImageReferenceElement, - MayoListItemElement, - MayoLinkElement, - MayoLinkReferenceElement, - MayoListElement, - MayoParagraphElement, - MayoBlockquoteElement, - MayoStrongElement, - MayoTableElement, - MayoTextElement, - MayoTomlElement, - MayoYamlElement, -} - -export default [ - MayoNodeElement, - MayoAppElement, - MayoDocumentElement, - MayoSidebarElement, - MayoSidebarFileElement, - MayoSidebarTreeElement, - MayoBreakElement, - MayoCodeElement, - MayoInlineCodeElement, - MayoDeleteElement, - MayoEmphasisElement, - MayoHeadingElement, - MayoThematicBreakElement, - MayoHtmlElement, - MayoImageElement, - MayoImageReferenceElement, - MayoListItemElement, - MayoLinkElement, - MayoLinkReferenceElement, - MayoListElement, - MayoParagraphElement, - MayoBlockquoteElement, - MayoStrongElement, - MayoTableElement, - MayoTextElement, - MayoTomlElement, - MayoYamlElement, -] diff --git a/src/elements/markdown/mayo-blockquote.ts b/src/elements/markdown/mayo-blockquote.ts deleted file mode 100644 index 070cf7d..0000000 --- a/src/elements/markdown/mayo-blockquote.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as md from "mdast" -import {MayoParentElement} from "./mayo-element" -export default class MayoBlockquoteElement extends MayoParentElement { - connectedCallback() {} -} diff --git a/src/elements/markdown/mayo-break.ts b/src/elements/markdown/mayo-break.ts deleted file mode 100644 index df44eef..0000000 --- a/src/elements/markdown/mayo-break.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" -export default class MayoBreakElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-code.ts b/src/elements/markdown/mayo-code.ts deleted file mode 100644 index 1e6fe3f..0000000 --- a/src/elements/markdown/mayo-code.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as md from "mdast" -import {MayoLiteralElement} from "./mayo-element" - -export default class MayoCodeElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-delete.ts b/src/elements/markdown/mayo-delete.ts deleted file mode 100644 index aa132fa..0000000 --- a/src/elements/markdown/mayo-delete.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as md from "mdast" -import {MayoParentElement} from "./mayo-element" - -export default class MayoDeleteElement extends MayoParentElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-element.ts b/src/elements/markdown/mayo-element.ts deleted file mode 100644 index 23bd16d..0000000 --- a/src/elements/markdown/mayo-element.ts +++ /dev/null @@ -1,213 +0,0 @@ -import type * as md from "mdast" -import type * as unist from "unist" -import split from "../../ast/split" -import {MayoContentElement} from ".." -import {CaretInstruction} from "../../caret" -export abstract class MayoElement< - AstNodeType extends unist.Node -> extends HTMLElement { - node: AstNodeType - get value(): string | undefined { - return this.getAttribute("value") - } - - get type(): string | undefined { - return this.getAttribute("type") - } - - get block(): boolean { - return this.hasAttribute("block") - } - - get leaf(): boolean { - return this.hasAttribute("leaf") - } - - get container(): boolean { - return this.hasAttribute("container") - } - - get inline(): boolean { - return this.hasAttribute("inline") - } - - get interestingChildren(): Node[] { - return Array.from(this.childNodes).filter( - n => "node" in n || n.nodeName == "#text" - ) - } - /** - * Insert text when the range start and end are both in a single element - * @param text the text to insert - * @param range the selected range, which may be of interest - */ - abstract selfInsertText(text: string, range: StaticRange): CaretInstruction - - connectedCallback(): void { - return void void 0 - } -} - -export class MayoEmptyElement< - AstNodeType extends - | md.ImageReference - | md.LinkReference - | md.Link - | md.Break - | md.ThematicBreak - | md.Image -> extends MayoElement { - selfDeleteContentBackward(r: StaticRange): void { - throw new TypeError() - } - selfInsertText(_text: string, _range: StaticRange): CaretInstruction { - throw new Error("empty elements") - } -} - -export class MayoLiteralElement< - AstNodeType extends md.Literal -> extends MayoElement { - insertText(selection: Selection, event: InputEvent) { - let special = event.data!.match(/[~`*_]/) - if (special) { - console.log(this.nodeValue, this.node.value) - } else { - this.node.value = selection.focusNode!.nodeValue || "" - return true - } - } - - selfInsertText(text: string, range: StaticRange): CaretInstruction { - this.node.value = - this.node.value.slice(0, range.startOffset) + - text + - this.node.value.slice(range.endOffset) - return { - type: "text", - element: this.interestingChildren[0] as Text, - startOffset: range.startOffset + 1, - } - } - - selfDeleteContentBackward(range: StaticRange): CaretInstruction { - this.node.value = - this.node.value.slice(0, range.startOffset) + - this.node.value.slice(range.endOffset) - return { - type: "text", - element: this.interestingChildren[0] as Text, - startOffset: range.startOffset, - } - } -} - -export class MayoParentElement< - AstNodeType extends md.Parent -> extends MayoElement { - atBeginningOfBlock(range: StaticRange): boolean { - return ( - range.collapsed && - this.interestingChildren.indexOf(range.startContainer) == 0 && - range.startOffset == 0 - ) - } - - selfInsertText(text: string, range: StaticRange): CaretInstruction { - // TODO if the character is special, and the range is !caret then wrap the selected area - let targetTextNode = range.startContainer - let targetIndex = this.interestingChildren.indexOf(targetTextNode) - if (targetIndex == -1) { - throw new Error(`${targetTextNode} is not a child of ${this}`) - } - - let targetAstNode = this.node.children[targetIndex] - if (targetAstNode.type != "text") { - throw new Error( - `expected the ast node to be of type text, got ${targetAstNode.type}` - ) - } - targetAstNode.value = - targetAstNode.value.slice(0, range.startOffset) + - text + - targetAstNode.value.slice(range.endOffset) - - let caret = { - type: "parent" as const, - parent: this, - index: targetIndex, - startOffset: range.startOffset + 1, - } - - return caret - } - - insertTextAsCommonAncestor( - startElement: MayoContentElement, - endElement: MayoContentElement, - text: string, - range: StaticRange - ): CaretInstruction { - // This is tricky for me right now - // but, what i want to to i think is - // to move the startNode and endNode (which are both text nodes) up to being direct - // children of `this`, removing the other nodes between start and end - // and then merging them if they're the same type... - // i think. - // i think. - // TODO write a - - let startVal = range.startContainer.nodeValue?.slice(0, range.startOffset) - let endVal = range.endContainer.nodeValue?.slice(range.endOffset) - - let startChildIndex = startElement.interestingChildren.indexOf( - range.startContainer - ) - let endChildIndex = endElement.interestingChildren.indexOf( - range.endContainer - ) - let startIndex = this.interestingChildren.indexOf(startElement) - let endIndex = this.interestingChildren.indexOf(endElement) - let snode - if (startChildIndex != -1 && Array.isArray(startElement.node.children)) { - snode = startElement.node.children[startChildIndex] - } else { - snode = startElement.node - } - - let child = startElement.firstChild - if (child.nodeName == "#text") { - let element = child as Text - return {type: "text", element, startOffset: 0} - } - } - - insertParagraph(range: StaticRange) {} - - selfDeleteContentBackward(range: StaticRange): CaretInstruction { - let targetTextNode = range.startContainer - let targetIndex = this.interestingChildren.indexOf(targetTextNode) - if (targetIndex == -1) { - throw new Error(`${targetTextNode} is not a child of ${this}`) - } - - let targetAstNode = this.node.children[targetIndex] - if (targetAstNode.type != "text") { - throw new Error( - `expected the ast node to be of type text, got ${targetAstNode.type}` - ) - } - targetAstNode.value = - targetAstNode.value.slice(0, range.startOffset) + - targetAstNode.value.slice(range.endOffset) - - let caret = { - type: "parent" as const, - parent: this, - index: targetIndex, - startOffset: range.startOffset, - } - - return caret - } -} diff --git a/src/elements/markdown/mayo-emphasis.ts b/src/elements/markdown/mayo-emphasis.ts deleted file mode 100644 index be1eb8e..0000000 --- a/src/elements/markdown/mayo-emphasis.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import type * as md from "mdast" - -export default class MayoEmphasisElement extends MayoParentElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-heading.ts b/src/elements/markdown/mayo-heading.ts deleted file mode 100644 index b11c04d..0000000 --- a/src/elements/markdown/mayo-heading.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {customElement, LitElement} from "lit-element" -import {html} from "lit-html" - -@customElement("mayo-heading") -export default class MayoHeadingElement extends LitElement { - render() { - return html`hello` - } - // get depth(): Depth { - // return this.node.depth - // } - // set depth(val: Depth) { - // this.node.depth = val - // } - // selfInsertText(text: string, range: StaticRange): CaretInstruction { - // if (text == "#" && this.atBeginningOfBlock(range)) { - // if (this.depth < 6) { - // this.depth += 1 - // } - // return { - // type: "parent", - // parent: this, - // index: 0, - // startOffset: 0, - // } - // } - // return super.selfInsertText(text, range) - // } - // selfDeleteContentBackward(range: StaticRange): CaretInstruction { - // if (this.atBeginningOfBlock(range)) { - // let id = shortid() - // this.node.id = id - // if (this.depth > 1) { - // this.depth -= 1 - // } else { - // // @ts-ignore - // this.node.type = "paragraph" - // delete this.node.depth - // } - // return { - // type: "id", - // id, - // index: 0, - // startOffset: 0, - // } - // } - // let caret = super.selfDeleteContentBackward(range) - // return caret - // } -} diff --git a/src/elements/markdown/mayo-html.ts b/src/elements/markdown/mayo-html.ts deleted file mode 100644 index 3827fea..0000000 --- a/src/elements/markdown/mayo-html.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as md from "mdast" -import {MayoLiteralElement} from "./mayo-element" - -export default class MayoHtmlElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - this.innerHTML = this.textContent || "" - } -} diff --git a/src/elements/markdown/mayo-image-reference.ts b/src/elements/markdown/mayo-image-reference.ts deleted file mode 100644 index 493e43e..0000000 --- a/src/elements/markdown/mayo-image-reference.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" -export default class MayoImageReferenceElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-image.ts b/src/elements/markdown/mayo-image.ts deleted file mode 100644 index 275315d..0000000 --- a/src/elements/markdown/mayo-image.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" -export default class MayoImageElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-inline-code.ts b/src/elements/markdown/mayo-inline-code.ts deleted file mode 100644 index 57468ab..0000000 --- a/src/elements/markdown/mayo-inline-code.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {MayoLiteralElement} from "./mayo-element" -import * as md from "mdast" -export default class MayoInlineCodeElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-link-reference.ts b/src/elements/markdown/mayo-link-reference.ts deleted file mode 100644 index 74ae3fa..0000000 --- a/src/elements/markdown/mayo-link-reference.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" -export default class MayoLinkReferenceElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-link.ts b/src/elements/markdown/mayo-link.ts deleted file mode 100644 index 3aef3b5..0000000 --- a/src/elements/markdown/mayo-link.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" -export default class MayoLinkElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-list-item.ts b/src/elements/markdown/mayo-list-item.ts deleted file mode 100644 index 3e44a23..0000000 --- a/src/elements/markdown/mayo-list-item.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import * as md from "mdast" -import shortid from "shortid" -import {CaretInstruction} from "../../caret" - -export default class MayoListItemElement extends MayoParentElement { - selfDeleteContentBackward(range: StaticRange): CaretInstruction { - if (this.atBeginningOfBlock(range)) { - let id = shortid() - this.node.id = id - return { - type: "id", - id, - index: 0, - startOffset: 0, - } - } - let caret = super.selfDeleteContentBackward(range) - return caret - } - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-list.ts b/src/elements/markdown/mayo-list.ts deleted file mode 100644 index 2252cfd..0000000 --- a/src/elements/markdown/mayo-list.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import * as md from "mdast" -import shortid from "shortid" -import {CaretInstruction} from "../../caret" - -export default class MayoListElement extends MayoParentElement { - selfDeleteContentBackward(range: StaticRange): CaretInstruction { - let targetNode = range.startContainer - let targetIndex = this.interestingChildren.indexOf(targetNode) - if (targetIndex == -1) { - throw new Error(`${targetNode} is not a child of ${this}`) - } - - let targetAstNode = this.node.children[targetIndex] - let previousAstNode = this.node.children[targetIndex - 1] - - if (previousAstNode) { - let index = previousAstNode.children.length - let [first] = targetAstNode.children - previousAstNode.children.splice(Infinity, 0, ...targetAstNode.children) - let id = shortid() - first.id = id - - return { - type: "id", - id, - index: 0, - startOffset: 0, - } - } else { - // hello - return - } - - if (this.atBeginningOfBlock(range)) { - let id = shortid() - this.node.id = id - } - let caret = super.selfDeleteContentBackward(range) - return caret - } - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-paragraph.ts b/src/elements/markdown/mayo-paragraph.ts deleted file mode 100644 index da5c3cb..0000000 --- a/src/elements/markdown/mayo-paragraph.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import * as md from "mdast" -import u from "unist-builder" -import {CaretInstruction} from "../../caret" -import shortid from "shortid" - -export default class MayoParagraphElement extends MayoParentElement { - selfInsertText(text: string, range: StaticRange): CaretInstruction { - let atBeginning = this.atBeginningOfBlock(range) - - let id = shortid() - let caret: CaretInstruction = { - type: "id", - id, - index: 0, - startOffset: 0, - } - - if (atBeginning) { - switch (text) { - case "#": { - this.node.depth = 1 - // @ts-ignore - this.node.type = "heading" - this.node.id = id - return caret - } - case ">": { - this.node.children = [ - // @ts-ignore - { - ...this.node, - id, - }, - ] - // @ts-ignore - this.node.type = "blockquote" - return caret - } - case "-": { - this.node.children = [ - // @ts-ignore - u("listItem", [{...this.node, id}]), - ] - // @ts-ignore - this.node.type = "list" - this.node.ordered = false - return caret - } - case ".": { - this.node.children = [ - // @ts-ignore - u("listItem", [{...this.node, id}]), - ] - // @ts-ignore - this.node.type = "list" - this.node.ordered = true - return caret - } - } - } - return super.selfInsertText(text, range) - } - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-strong.ts b/src/elements/markdown/mayo-strong.ts deleted file mode 100644 index 8d98c73..0000000 --- a/src/elements/markdown/mayo-strong.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import * as md from "mdast" - -export default class MayoStrongElement extends MayoParentElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-table.ts b/src/elements/markdown/mayo-table.ts deleted file mode 100644 index 54a392b..0000000 --- a/src/elements/markdown/mayo-table.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {MayoParentElement} from "./mayo-element" -import * as md from "mdast" - -export default class MayoTableElement extends MayoParentElement { - connectedCallback() {} -} diff --git a/src/elements/markdown/mayo-text.ts b/src/elements/markdown/mayo-text.ts deleted file mode 100644 index 8dae180..0000000 --- a/src/elements/markdown/mayo-text.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {MayoLiteralElement} from "./mayo-element" -import * as md from "mdast" -export default class MayoTextElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-thematic-break.ts b/src/elements/markdown/mayo-thematic-break.ts deleted file mode 100644 index 481e1bb..0000000 --- a/src/elements/markdown/mayo-thematic-break.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as md from "mdast" -import {MayoEmptyElement} from "./mayo-element" - -export default class MayoThematicBreakElement extends MayoEmptyElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-toml.ts b/src/elements/markdown/mayo-toml.ts deleted file mode 100644 index 0012490..0000000 --- a/src/elements/markdown/mayo-toml.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {MayoLiteralElement} from "./mayo-element" -import * as md from "mdast" -export default class MayoTomlElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/markdown/mayo-yaml.ts b/src/elements/markdown/mayo-yaml.ts deleted file mode 100644 index cfc7475..0000000 --- a/src/elements/markdown/mayo-yaml.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {MayoLiteralElement} from "./mayo-element" -import * as md from "mdast" - -export default class MayoYamlElement extends MayoLiteralElement { - connectedCallback() { - super.connectedCallback() - } -} diff --git a/src/elements/mayo-app.ts b/src/elements/mayo-app.ts index eb5016f..d18701a 100644 --- a/src/elements/mayo-app.ts +++ b/src/elements/mayo-app.ts @@ -1,8 +1,3 @@ -import type { - MayoDocumentElement, - MayoSidebarElement, - MayoSidebarTreeElement, -} from "." import {promises as fs} from "fs" import path from "path" import { @@ -15,6 +10,13 @@ import { } from "lit-element" import {html, TemplateResult} from "lit-html" import defaultDoc from "../default-doc" +import MayoSidebarElement from "./mayo-sidebar" +import MayoSidebarTreeElement from "./mayo-sidebar-tree" +import MayoDocumentElement from "./mayo-document" + +import "./mayo-document" +import "./mayo-sidebar" + export interface GetFilesEvent extends CustomEvent { detail: { target: MayoSidebarTreeElement @@ -58,6 +60,7 @@ export default class MayoAppElement extends LitElement { mayo-document[dirty] { border-left: 20px solid #ff2a50; } + * { box-sizing: border-box; } @@ -255,6 +258,11 @@ export default class MayoAppElement extends LitElement { border-left: 5px solid #3399ff; padding-left: 5px; } + mayo-link { + color: #3399ff; + text-decoration: underline; + cursor: normal; + } ` } @@ -298,10 +306,10 @@ export default class MayoAppElement extends LitElement { @open-file=${this.openFile} > ` } diff --git a/src/elements/mayo-document.ts b/src/elements/mayo-document.ts index a8629f7..1f90fee 100644 --- a/src/elements/mayo-document.ts +++ b/src/elements/mayo-document.ts @@ -1,65 +1,56 @@ -import parse from "mdast-util-from-markdown" +import fromMarkdown from "mdast-util-from-markdown" import type * as md from "mdast" -import BeforeInputEvent from "../before-input-event" -import {CaretInstruction} from "../caret" -import {html} from "lit-html" +import BeforeInputEvent from "../events/before-input-event" +import {html, TemplateResult} from "lit-html" // TODO learn how to declare types for this // @ts-ignore import compact from "mdast-util-compact" import toMarkdown from "mdast-util-to-markdown" import * as is from "../ast/is" -import {css, CSSResult, customElement, LitElement, property} from "lit-element" -import transform, {nothing} from "../ast/transform" -import getTransformOptions from "../get-transform-options" +import {customElement, LitElement, property} from "lit-element" +import transform from "../ast/transform" +import getTransformOptions, { + GetTransformOptionsOptions, +} from "../dom/get-transform-options" import "./mayo-node" -import MayoNodeElement from "./mayo-node" -import {produce, enableAllPlugins} from "immer" -import {spread} from "@open-wc/lit-helpers" -import {spreadable} from "../ast/convert-to-html" +import * as gfm from "mdast-util-gfm" -enableAllPlugins() +import type {Patch} from "immer" +import { + produceWithPatches, + enableAllPlugins as enableAllOfImmer, + applyPatches, +} from "immer" + +enableAllOfImmer() + +function parse(contents: string): md.Root { + let root = fromMarkdown(contents, {mdastExtensions: [gfm.fromMarkdown]}) + // addParents(root) + return root +} + +enum modifiers { + control = 1, + shift = 2, + super = 4, + option = 8, + hyper = 16, +} @customElement("mayo-document") -export default class MayoDocumentElement extends LitElement { +class MayoDocumentElement extends LitElement { @property() contents: string @property({type: Boolean}) dirty: boolean node: md.Root + modifier: number createRenderRoot(): this { return this } - static get styles(): CSSResult { - return css` - article { - white-space: pre-wrap; - } - - article > * + * { - margin-top: 1em; - } - - article { - width: 100%; - height: 100%; - font-size: 1.6em; - font-family: avenir next, sans-serif; - padding: 1em; - color: #000; - line-height: 1.2; - max-width: 80ex; - margin: auto; - margin-bottom: 2em; - } - - article:focus { - outline: none; - } - ` - } - attributeChangedCallback(name: string, before: string, now: string): void { super.attributeChangedCallback(name, before, now) if (name == "contents") { @@ -67,215 +58,156 @@ export default class MayoDocumentElement extends LitElement { } } - updateSelection(caret: CaretInstruction | null): void { - let selection = document.getSelection() - if (caret) { - let target: Text | null = null - if (caret && caret.type == "id") { - let parent = document.getElementById( - caret.elementId - ) as MayoNodeElement - } else if (caret && caret.type == "parent") { - target = caret.parentElement.interestingChildren[ - caret.textChildIndex - ] as Text - } else if (caret && caret.type == "text") { - target = caret.element - } - if (target) { - selection.removeAllRanges() - let range = document.createRange() - range.setStart(target, caret ? caret.startOffset : 0) - selection.addRange(range) - } - } - } - - async handleInput(event: BeforeInputEvent) { - event.preventDefault() - // TODO multiple ranges? - - let transformOptions = await getTransformOptions(event) - this.node = produce(this.node, draft => transform(draft, transformOptions)) - this.setAttribute("dirty", "true") - await this.requestUpdate() - // await this.updateComplete - // this.placeCaret() - event.preventDefault() - return + // history + history: Patch[][] = [] + // future of the history + futures: Patch[][] = [] - // let startContainerElement = startElement.container - // ? startElement - // : (startElement.closest("[container]")! as MayoParentElement) + historyIndex = 0 - // let startBlockElement = startElement.block - // ? startElement - // : (startElement.closest("[block]")! as MayoParentElement) - - // let endBlockElement = endElement.block - // ? endElement - // : (endElement.closest("[block]")! as MayoParentElement) - - // // the index of the start element in the start block (x) - // let startElementIndex = 0 - // if (startElement.inline) { - // startElementIndex = startBlockElement.interestingChildren.indexOf( - // startElement - // ) - // } else if (startElement.block) { - // startElementIndex = startBlockElement.interestingChildren.indexOf( - // range.startContainer - // ) - // } - - // let endElementIndex = 0 - // if (endElement.inline) { - // endElementIndex = endBlockElement.interestingChildren.indexOf(endElement) - // } else if (endElement.block) { - // endElementIndex = endBlockElement.interestingChildren.indexOf( - // range.endContainer - // ) - // } - // switch (event.inputType) { - // case "insertReplacementText": - // event.dataTransfer.items[0].getAsString(text => { - // startElement.selfInsertText(text, range) - // this.setAttribute("dirty", "true") - // }) - // break - // case "insertText": { - // let caret: CaretInstruction | null = null - // if (range.startContainer == range.endContainer) { - // caret = startElement.selfInsertText(event.data || "", range) - // event.preventDefault() - // } else if (startBlockElement == endBlockElement) { - // caret = (startBlockElement as MayoParentElement).insertTextAsCommonAncestor( - // startElement, - // endElement, - // event.data || "", - // range - // ) - // } else { - // // TODO this is a textInsert across blocks, sounds hard - // } - - // this.setAttribute("dirty", "true") - - // this.updateSelection(caret) - // break - // } - // case "insertLineBreak": { - // let index = startBlockElement.interestingChildren.indexOf(startElement) - // startBlockElement.node.children.splice(index + 1, 0, u("break")) - // this.setAttribute("dirty", "true") + undo(): void { + this.node = applyPatches(this.node, this.history[this.historyIndex]) + this.historyIndex += 1 + this.requestUpdate() + } - // event.preventDefault() - // break - // } - // case "insertParagraph": { - // let id = shortId() - // if ("value" in startElement.node) { - // if (isTextOrInlineCode(startElement)) { - // insertParagraph( - // this.node, - // startElement.node, - // range.startOffset, - // id - // ) - // } - // } else if (startElement == startBlockElement) { - // if (Array.isArray(startElement.node.children)) { - // insertParagraph( - // this.node, - // startElement.node.children[startElementIndex], - // range.startOffset, - // id - // ) - // } - // } else { - // let idx = startElement.interestingChildren.indexOf( - // range.startContainer - // ) - // if (Array.isArray(startElement.node.children)) { - // insertParagraph( - // this.node, - // startElement.node.children[idx], - // range.startOffset, - // id - // ) - // } - // } + redo(): void { + this.node = applyPatches(this.node, this.futures[this.historyIndex]) + this.historyIndex -= 1 + this.requestUpdate() + } - // this.setAttribute("dirty", "true") + async transform(options: GetTransformOptionsOptions): Promise { + let transformOptions = await getTransformOptions(options) + let [node, patches, inversePatches] = produceWithPatches( + this.node, + draft => { + transform(draft, transformOptions) + } + ) - // this.updateSelection({ - // type: "id" as const, - // elementId: id, - // textChildIndex: 0, - // startOffset: 0, - // }) - // break - // } - // case "deleteContentBackward": { - // if (startElement.node.type == "paragraph") { - // startElement = startElement as MayoParagraphElement + this.node = node + // TODO better undo/redoo + this.history.splice(this.historyIndex, 0, inversePatches) + this.futures.splice(this.historyIndex, 0, patches) - // if (startElement.atBeginningOfBlock(range)) { - // backspaceParagraph(this.node, startElement.node) - // } - // } else if (startElement != startBlockElement) { - // if (is.container(startElement.node.type)) { - // // go through the children and move the top-level text nodes out into the parent - // let indexOfTextNode = startElement.interestingChildren.indexOf( - // range.startContainer - // ) - // if (Array.isArray(startElement.node.children)) { - // let textNode = startElement.node.children[indexOfTextNode] - // } - // } else if (typeof startElement.node.value == "string") { - // if (range.startOffset == startElement.node.value.length - 1) { - // startElement.node.type = "text" + this.setAttribute("dirty", "true") + await this.requestUpdate() + } - // // TODO fix caret position - // // this.updateSelection() - // event.preventDefault() - // } - // } - // } + async handleInput(event: BeforeInputEvent): Promise { + let metaBindings = { + s: this.save, + "[": () => { + this.transform({ + inputType: "formatOutdent", + range: event.getTargetRanges()[0], + }) + }, + "]": () => { + this.transform({ + inputType: "formatIndent", + range: event.getTargetRanges()[0], + }) + }, + } - // let caret = startElement.selfDeleteContentBackward(range) + if ( + this.modifier & modifiers.super && + event.inputType == "insertText" && + Object.keys(metaBindings).includes(event.data) + ) { + event.preventDefault() + // @ts-ignore + metaBindings[event.data]() + return + } - // this.setAttribute("dirty", "true") + event.preventDefault() + await this.transform({ + inputType: event.inputType, + range: event.getTargetRanges()[0], + data: event.data, + dataTransfer: event.dataTransfer, + }) - // if (caret) { - // this.updateSelection(caret) - // } - // break - // } - // } + return + } - // event.preventDefault() - //this.updateForTransform(event) + // this is messed up, but + handleKeydown(event: KeyboardEvent): void { + switch (event.key) { + case "Meta": + this.modifier |= modifiers.super + break + case "Alt": + this.modifier |= modifiers.option + break + case "Hyper": + this.modifier |= modifiers.hyper + break + case "Control": + this.modifier |= modifiers.control + break + case "Shift": + this.modifier |= modifiers.shift + } } - updateForTransform(caret: CaretInstruction) { - this.setAttribute("dirty", "true") - this.updateSelection(caret) + handleKeyup(event: KeyboardEvent): void { + switch (event.key) { + case "Meta": + this.modifier ^= modifiers.super + break + case "Alt": + this.modifier ^= modifiers.option + break + case "Hyper": + this.modifier ^= modifiers.hyper + break + case "Control": + this.modifier ^= modifiers.control + break + case "Shift": + this.modifier ^= modifiers.shift + } } - handleKeydown(event: KeyboardEvent) { - if (event.key == "s" && (event.ctrlKey || event.metaKey)) { - this.save() - event.preventDefault() + getCurrentRange(): StaticRange { + let { + startContainer, + startOffset, + endContainer, + endOffset, + } = document.getSelection().getRangeAt(0).cloneRange() + + return { + collapsed: startContainer == endContainer && startOffset == endOffset, + startContainer: startContainer, + startOffset: startOffset, + endContainer: endContainer, + endOffset: endOffset, } } - save() { - this.contents = toMarkdown(compact(this.node)) + save(): void { + this.contents = toMarkdown(compact(this.node), { + bullet: "-", + fence: "`", + emphasis: "_", + strong: "*", + rule: "*", + setext: false, + fences: true, + listItemIndent: "tab", + incrementListMarker: true, + quote: '"', + extensions: [gfm.toMarkdown()], + }) this.dispatchEvent(new CustomEvent("save")) } - render() { + render(): TemplateResult { return html`
` + // eslint-disable-next-line no-mixed-spaces-and-tabs }) : ""}
` @@ -307,10 +239,9 @@ export default class MayoDocumentElement extends LitElement { constructor() { super() this.addEventListener("keydown", this.handleKeydown) + this.addEventListener("keyup", this.handleKeyup) this.addEventListener("beforeinput", this.handleInput) } - - connectedCallback() { - super.connectedCallback() - } } + +export default MayoDocumentElement diff --git a/src/elements/mayo-killer.ts b/src/elements/mayo-killer.ts deleted file mode 100644 index e09eba6..0000000 --- a/src/elements/mayo-killer.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {LitElement, html, css, customElement, property} from "lit-element" - -@customElement("mayo-killer") -export default class MayoKillerElement extends LitElement { - @property() - symbol: string - - render() { - return html`${this.symbol}` - } -} diff --git a/src/elements/mayo-node.ts b/src/elements/mayo-node.ts index 89378fd..46ca8b2 100644 --- a/src/elements/mayo-node.ts +++ b/src/elements/mayo-node.ts @@ -1,11 +1,39 @@ import {customElement, LitElement, property} from "lit-element" -import {html, render, TemplateResult} from "lit-html" +import {html, TemplateResult} from "lit-html" import * as md from "mdast" -import * as unist from "unist" import * as is from "../ast/is" -import {spread} from "@open-wc/lit-helpers" -import {spreadable} from "../ast/convert-to-html" -import {repeat} from "lit-html/directives/repeat" +import {ifDefined} from "lit-html/directives/if-defined" + +interface Info { + leaf?: true + block?: true + inline?: true + container?: true + empty?: true + caretStart?: number + caretEnd?: number +} + +export function info(node: md.Content | md.Root): Info { + let info: Info = {} + if (is.leaf(node)) { + info.leaf = true + } + if (is.block(node)) { + info.block = true + } + if (is.inline(node)) { + info.inline = true + } + if (is.container(node)) { + info.container = true + } + if (is.empty(node)) { + info.empty = true + } + + return info +} @customElement("mayo-node") class MayoNodeElement extends LitElement { @@ -31,10 +59,10 @@ class MayoNodeElement extends LitElement { block: boolean @property() value: string - @property() - caret: string - @property({type: Number}) - caretoffset: number + @property({type: Number, attribute: "caret-start"}) + caretStart?: number + @property({type: Number, attribute: "caret-end"}) + caretEnd?: number @property() path: string @@ -61,22 +89,22 @@ class MayoNodeElement extends LitElement { return html`` } return html`${this.node.children.map((child, index) => { - let caret = child.data?.caret?.type - let offset = child.data?.caret?.offset + let caret = child.data?.caret return html`` })}` } @@ -174,21 +202,26 @@ class MayoNodeElement extends LitElement { return html`${this.node.value}` } else if (this.node.type == "text") { return html`${this.node.value}` + } else { + console.error(`unhandled node type: ${this.node.type}`) } } - updated(props: Map) { - if (this.caret) { - console.log(this.caret, this.caretoffset) + updated(): void { + if (this.caretStart != null || this.caretEnd != null) { + if (!this.textNode) { + return console.error(`no text node in ${this.tagName}`) + } let selection = document.getSelection() let range = document.createRange() selection.removeAllRanges() - if (this.caret == "collapsed" || this.caret == "start") { - range.setStart(this.textNode, this.caretoffset) + + if (this.caretStart != null) { + range.setStart(this.textNode, this.caretStart) } - if (this.caret == "collapsed" || this.caret == "end") { - range.setEnd(this.textNode, this.caretoffset) + if (this.caretEnd != null) { + range.setEnd(this.textNode, this.caretEnd) } selection.addRange(range) diff --git a/src/elements/mayo-sidebar-file.ts b/src/elements/mayo-sidebar-file.ts index 734b179..e872b63 100644 --- a/src/elements/mayo-sidebar-file.ts +++ b/src/elements/mayo-sidebar-file.ts @@ -10,7 +10,7 @@ import {TemplateResult} from "lit-html" import type {OpenFileEvent} from "./mayo-app" @customElement("mayo-sidebar-file") -export default class MayoSidebarFileElement extends LitElement { +class MayoSidebarFileElement extends LitElement { @property() name: string @property() @@ -69,3 +69,5 @@ export default class MayoSidebarFileElement extends LitElement { } } } + +export default MayoSidebarFileElement diff --git a/src/elements/mayo-sidebar-tree.ts b/src/elements/mayo-sidebar-tree.ts index af6ad0c..00df6ef 100644 --- a/src/elements/mayo-sidebar-tree.ts +++ b/src/elements/mayo-sidebar-tree.ts @@ -11,7 +11,7 @@ import type {GetFilesEvent} from "./mayo-app" import path from "path" @customElement("mayo-sidebar-tree") -export default class MayoSidebarTreeElement extends LitElement { +class MayoSidebarTreeElement extends LitElement { @property({type: Boolean}) open: boolean @property({attribute: false}) @@ -108,3 +108,5 @@ export default class MayoSidebarTreeElement extends LitElement { } } } + +export default MayoSidebarTreeElement diff --git a/src/elements/mayo-sidebar.ts b/src/elements/mayo-sidebar.ts index 7ca2ea2..65ea566 100644 --- a/src/elements/mayo-sidebar.ts +++ b/src/elements/mayo-sidebar.ts @@ -8,9 +8,11 @@ import { } from "lit-element" import {TemplateResult} from "lit-html" import {basename} from "path" +import "./mayo-sidebar-file" +import "./mayo-sidebar-tree" @customElement("mayo-sidebar") -export default class MayoSidebarElement extends LitElement { +class MayoSidebarElement extends LitElement { @property() cwd: string @@ -54,3 +56,5 @@ export default class MayoSidebarElement extends LitElement { ` } } + +export default MayoSidebarElement diff --git a/src/before-input-event.ts b/src/events/before-input-event.ts similarity index 100% rename from src/before-input-event.ts rename to src/events/before-input-event.ts diff --git a/src/get-transform-options.ts b/src/get-transform-options.ts deleted file mode 100644 index ea1b3fd..0000000 --- a/src/get-transform-options.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type {TransformOptions} from "./ast/transform" -import BeforeInputEvent from "./before-input-event" -import MayoNodeElement from "./elements/mayo-node" -import type * as md from "mdast" -import {find} from "unist-utils-core" - -export default async function getTransformOptions( - event: BeforeInputEvent -): Promise { - let data = event.data - - if (event.inputType == "insertReplacementText") { - // i'm going to regret this - // instead i should be creating a placeholder element - data = await new Promise(yay => - event.dataTransfer.items[0].getAsString(yay) - ) - } - - let range = event.getTargetRanges()[0] - let start = range.startContainer.parentElement as MayoNodeElement - let end = range.endContainer.parentElement as MayoNodeElement - - if (start.tagName.toLowerCase() != "mayo-node") { - start = start.closest("mayo-node") - } - if (end.tagName.toLowerCase() != "mayo-node") { - end = end.closest("mayo-node") - } - - return { - data, - start: { - path: start.path, - offset: range.startOffset, - }, - end: { - path: end.path, - offset: range.endOffset, - }, - inputType: event.inputType, - } -} diff --git a/src/index.html b/src/index.html index eaf9201..b5a31fe 100644 --- a/src/index.html +++ b/src/index.html @@ -1,16 +1,3 @@ mayonaise - - - - + diff --git a/src/mayo.ts b/src/mayo.ts index 1fc503a..a446e6e 100644 --- a/src/mayo.ts +++ b/src/mayo.ts @@ -1 +1 @@ -import "./elements" +import "./elements/mayo-app" diff --git a/src/styles.css b/src/styles.css index 6cae5a1..911b450 100644 --- a/src/styles.css +++ b/src/styles.css @@ -11,187 +11,3 @@ mayo-app { display: flex; height: 100vh; } - -mayo-sidebar { - height: 100vh; - overflow: hidden; -} - -mayo-document { - flex: 1; - border-left: 20px solid white; - width: 100%; - padding-bottom: 5em; - height: 100vh; - overflow: auto; -} - -mayo-document[dirty] { - border-left: 20px solid #ff2a50; -} - -mayo-document { - overflow: auto; - height: 100%; -} - -mayo-document article > * + * { - margin-top: 1em; -} - -mayo-document article { - width: 100%; - height: 100%; - font-size: 1.4em; - font-family: avenir next, sans-serif; - padding: 1em; - color: #000; - line-height: 1.2; - max-width: 80ex; - margin: auto; - margin-bottom: 2em; -} - -mayo-document article:focus { - outline: none; -} - -mayo-heading { - display: block; - font-weight: bolder; -} - -mayo-heading[depth="1"] { - font-size: 2em; -} - -mayo-heading[depth="2"] { - font-size: 1.6em; -} - -mayo-heading[depth="3"] { - font-size: 1.4em; -} - -mayo-heading[depth="4"] { - font-size: 1.25em; -} - -mayo-heading[depth="5"] { - font-size: 1.15em; -} - -mayo-heading[depth="6"] { - font-size: 1em; -} - -mayo-heading::before { - font-family: ibm plex mono, monospace; - font-style: italic; - display: inline-block; - font-size: 0.8em; - color: #77c; - margin-right: 0.5em; -} - -[depth="1"]::before { - content: "# "; -} - -[depth="2"]::before { - content: "## "; -} - -[depth="3"]::before { - content: "### "; -} - -[depth="4"]::before { - content: "#### "; -} - -[depth="5"]::before { - content: "##### "; -} - -[depth="6"]::before { - content: "###### "; -} - -mayo-break { - display: block; -} - -mayo-paragraph { - display: block; -} - -mayo-paragraph + mayo-paragraph { - margin-top: 1em; -} - -mayo-list { - list-style-type: disc; - padding-left: 1em; - display: block; -} - -mayo-list[ordered="true"] { - list-style-type: decimal; - padding-left: 1em; -} - -mayo-list-item { - display: list-item; -} - -[ordered="false"] > mayo-list-item::-moz-list-bullet { - color: #af2af0; -} - -[ordered="true"] > mayo-list-item::-moz-list-bullet { - font-weight: 500; -} - -mayo-code { - display: block; - white-space: pre; - font-family: IBM Plex Mono, monospace; - background: #ffe9ed; - color: #c36; - padding: 1em; - font-size: 0.9em; - margin-bottom: 1em; - overflow-x: scroll; -} - -mayo-inline-code { - font-family: IBM Plex Mono, monospace; - background: #ffe9ed; -} - -/* mayo-inline-code:hover::before, -mayo-inline-code:hover::after { - content: "`"; -} */ -mayo-emphasis { - font-style: oblique; -} - -/* mayo-emphasis:hover::before, -mayo-emphasis:hover::after { - content: "_"; -} */ -mayo-strong { - font-weight: bolder; -} - -/* mayo-strong:hover::before, -mayo-strong:hover::after { - content: "**"; -} */ -blockquote { - display: flex; - border-left: 5px solid #3399ff; - padding-left: 5px; -} diff --git a/tests/insert-paragraph.test.ts b/tests/insert-paragraph.test.ts new file mode 100644 index 0000000..cb20f54 --- /dev/null +++ b/tests/insert-paragraph.test.ts @@ -0,0 +1,104 @@ +import insertParagraph from "../src/ast/insert-paragraph" +import parse from "./parse" +import toMarkdown from "mdast-util-to-markdown" +import {select, selectAll} from "unist-utils-core" +import * as md from "mdast" + +describe("insertParagraph", () => { + test("inserts a new paragraph where the cursor is", () => { + let tree = parse("hellothere") + let expected = parse(`hello + +there`) + let p = select("paragraph", tree) + let text = select("text", p) + insertParagraph(tree, text, 5) + + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) + + test("inserts a new paragraph where the cursor is (in a code)", () => { + let tree = parse("`hellothere`") + let expected = parse(`\`hello\` + +\`there\``) + let p = select("paragraph", tree) + let text = select("inlineCode", p) + insertParagraph(tree, text, 5) + + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) + + test("inserts a new paragraph where the cursor is (in a complex place)", () => { + let tree = parse( + "this is *story __all about how my life__ got flipped* turned up side down" + ) + let expected = parse( + `this is *story __all ab__* + +*__out how my life__ got flipped* turned up side down` + ) + let p = select("paragraph", tree) + let text = selectAll("text", p)[2] + insertParagraph(tree, text, 6) + + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) + + test("inserts a new paragraph where the cursor is (in a complex place in a code)", () => { + let tree = parse( + "this is *story __all about `how` my life__ got flipped* turned up side down" + ) + let expected = parse( + `this is *story __all about \`h\`__* + +*__\`ow\` my life__ got flipped* turned up side down` + ) + let p = select("paragraph", tree) + let inlineCode = select("inlineCode", p) + insertParagraph(tree, inlineCode, 1) + + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) + + test("inserts a new paragraph, maintaining whole nodes", () => { + let tree = parse("split __mein two__ please") + let expected = parse(`split __me__ + +__in two__ please`) + let p = select("paragraph", tree) + let text = selectAll("text", p)[1] + + insertParagraph(tree, text, 2) + + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) + + it("works in a heading", () => { + let tree = parse("# well, well, well") + let expected = parse(`# well, well + +, well`) + let h1 = select("heading", tree) + let text = select("text", h1) + insertParagraph(tree, text, 10) + expect(tree).toEqual(expected) + }) + + it("works in a heading with special elements", () => { + let tree = parse("# well, _well_, well, well") + let expected = parse(`# we + +ll, _well_, well, well`) + let h1 = select("heading", tree) + let text = selectAll("text", h1)[0] + insertParagraph(tree, text, 2) + expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) + expect(tree).toEqual(expected) + }) +}) diff --git a/tests/transform/split.test.ts b/tests/split.test.ts similarity index 100% rename from tests/transform/split.test.ts rename to tests/split.test.ts diff --git a/tests/transform/insert-paragraph.test.ts b/tests/transform/insert-paragraph.test.ts index d0d4e6d..9303d4f 100644 --- a/tests/transform/insert-paragraph.test.ts +++ b/tests/transform/insert-paragraph.test.ts @@ -1,104 +1,31 @@ -import insertParagraph from "../../src/ast/insert-paragraph" +import insertParagraph from "../../src/ast/transform/insert-paragraph" import parse from "../parse" -import toMarkdown from "mdast-util-to-markdown" import {select, selectAll} from "unist-utils-core" import * as md from "mdast" -describe("insertParagraph", () => { - test("inserts a new paragraph where the cursor is", () => { - let tree = parse("hellothere") +describe(insertParagraph, () => { + it("inserts paragraph in the correct place", () => { + let tree = parse("helloagain") let expected = parse(`hello - -there`) - let p = select("paragraph", tree) - let text = select("text", p) - insertParagraph(tree, text, 5) - - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) - }) - - test("inserts a new paragraph where the cursor is (in a code)", () => { - let tree = parse("`hellothere`") - let expected = parse(`\`hello\` - -\`there\``) - let p = select("paragraph", tree) - let text = select("inlineCode", p) - insertParagraph(tree, text, 5) - - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) - }) - - test("inserts a new paragraph where the cursor is (in a complex place)", () => { - let tree = parse( - "this is *story __all about how my life__ got flipped* turned up side down" - ) - let expected = parse( - `this is *story __all ab__* - -*__out how my life__ got flipped* turned up side down` - ) - let p = select("paragraph", tree) - let text = selectAll("text", p)[2] - insertParagraph(tree, text, 6) - - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) - }) - - test("inserts a new paragraph where the cursor is (in a complex place in a code)", () => { - let tree = parse( - "this is *story __all about `how` my life__ got flipped* turned up side down" - ) - let expected = parse( - `this is *story __all about \`h\`__* - -*__\`ow\` my life__ got flipped* turned up side down` - ) - let p = select("paragraph", tree) - let inlineCode = select("inlineCode", p) - insertParagraph(tree, inlineCode, 1) - - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) - }) - - test("inserts a new paragraph, maintaining whole nodes", () => { - let tree = parse("split __mein two__ please") - let expected = parse(`split __me__ -__in two__ please`) - let p = select("paragraph", tree) - let text = selectAll("text", p)[1] - - insertParagraph(tree, text, 2) - - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) - }) - - it("works in a heading", () => { - let tree = parse("# well, well, well") - let expected = parse(`# well, well - -, well`) - let h1 = select("heading", tree) - let text = select("text", h1) - insertParagraph(tree, text, 10) - expect(tree).toEqual(expected) - }) - - it("works in a heading with special elements", () => { - let tree = parse("# well, _well_, well, well") - let expected = parse(`# we - -ll, _well_, well, well`) - let h1 = select("heading", tree) - let text = selectAll("text", h1)[0] - insertParagraph(tree, text, 2) - expect(toMarkdown(tree)).toEqual(toMarkdown(expected)) - expect(tree).toEqual(expected) +again`) + selectAll("text", expected)[1].data = { + caret: { + type: "collapsed", + offset: 0, + }, + } + insertParagraph(tree, { + data: null, + start: { + node: select("text", tree), + offset: 5, + }, + end: { + node: select("text", tree), + offset: 5, + }, + }) + expect(expected).toEqual(tree) }) }) diff --git a/tests/transform/insert-text.test.ts b/tests/transform/insert-text.test.ts index 429d9f1..eb94106 100644 --- a/tests/transform/insert-text.test.ts +++ b/tests/transform/insert-text.test.ts @@ -1,19 +1,20 @@ -import type {TransformHandler} from "../../src/ast/transform" -import {visit} from "unist-utils-core" import insertText from "../../src/ast/transform/insert-text" import parse from "../parse" -import toMarkdown from "mdast-util-to-markdown" import {select, selectAll} from "unist-utils-core" import * as md from "mdast" -import * as unist from "unist" describe(insertText, () => { it("inserts text in the correct place", () => { let tree = parse("hello again") let expected = parse("hello! again") + select("text", expected).data = { + caret: { + caretStart: 6, + caretEnd: 6, + }, + } insertText(tree, { data: "!", - inputType: "insertText", start: { node: select("text", tree), offset: 5, @@ -29,9 +30,14 @@ describe(insertText, () => { it("inserts text in the correct in an inline code", () => { let tree = parse("hello `code` again") let expected = parse("hello `co-de` again") + select("inlineCode", expected).data = { + caret: { + caretStart: 3, + caretEnd: 3, + }, + } insertText(tree, { data: "-", - inputType: "insertText", start: { node: select("inlineCode", tree), offset: 2, @@ -47,9 +53,15 @@ describe(insertText, () => { it("inserts text in a nested thing", () => { let tree = parse("hello _**hey `wow` nice**_ again") let expected = parse("hello _**hey `wow` niice**_ again") + selectAll("text", expected)[2].data = { + caret: { + caretStart: 3, + caretEnd: 3, + }, + } insertText(tree, { data: "i", - inputType: "insertText", + start: { node: selectAll("text", tree)[2], offset: 2, @@ -65,9 +77,15 @@ describe(insertText, () => { it("inserts text across a range", () => { let tree = parse("hello _**hey nice**_ again") let expected = parse("hello _**hehe**_ again") + selectAll("text", expected)[1].data = { + caret: { + caretStart: 3, + caretEnd: 3, + }, + } insertText(tree, { data: "h", - inputType: "insertText", + start: { node: selectAll("text", tree)[1], offset: 2, @@ -83,9 +101,14 @@ describe(insertText, () => { it("inserts text across a range as a common ancestor", () => { let tree = parse("hello _**hey `code` nice**_ again") let expected = parse("hello _**hehe**_ again") + selectAll("text", expected)[1].data = { + caret: { + caretStart: 3, + caretEnd: 3, + }, + } insertText(tree, { data: "-", - inputType: "insertText", start: { node: selectAll("text", tree)[1], offset: 2, diff --git a/tests/transform/unwrap.test.ts b/tests/unwrap.test.ts similarity index 87% rename from tests/transform/unwrap.test.ts rename to tests/unwrap.test.ts index af3c373..726ed8e 100644 --- a/tests/transform/unwrap.test.ts +++ b/tests/unwrap.test.ts @@ -1,6 +1,6 @@ import unwrap from "../src/ast/unwrap" -import parse from "../parse" +import parse from "./parse" import toMarkdown from "mdast-util-to-markdown" import {select, selectAll} from "unist-utils-core" import * as md from "mdast" diff --git a/tsconfig.json b/tsconfig.json index fa6c2e6..d7b1983 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,7 @@ }, "include": [ "src/**/*", - "tests/transform/insert-paragraph.test.ts", + "tests/insert-paragraph.test.ts", "tests/transform/split.test.ts" ] } diff --git a/yarn.lock b/yarn.lock index 33436f2..1933b7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1557,6 +1557,13 @@ dependencies: "@types/node" "*" +"@types/hast@^2.0.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.1.tgz#b16872f2a6144c7025f296fb9636a667ebb79cd9" + integrity sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q== + dependencies: + "@types/unist" "*" + "@types/html-minifier-terser@^5.0.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" @@ -1643,6 +1650,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + "@types/prettier@^2.0.0": version "2.1.6" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.6.tgz#f4b1efa784e8db479cdb8b14403e2144b1e9ff03" @@ -5244,11 +5256,45 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hast-util-is-element@^1.0.0: +hast-util-embedded@^1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/hast-util-embedded/-/hast-util-embedded-1.0.6.tgz#ea7007323351cc43e19e1d6256b7cde66ad1aa03" + integrity sha512-JQMW+TJe0UAIXZMjCJ4Wf6ayDV9Yv3PBDPsHD4ExBpAspJ6MOcCX+nzVF+UJVv7OqPcg852WEMSHQPoRA+FVSw== + dependencies: + hast-util-is-element "^1.1.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-from-string@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-from-string/-/hast-util-from-string-1.0.4.tgz#5f7f94c1522455e881405b37f953ebd68ab0aa05" + integrity sha512-SFLN+9wPgdXVTV300VUyFNJCIltRO6P9zSgmw3O+B0NMV32MoyBJ9Ca+cjzUu7qWjVHN8+tcLaoHHL9+y8jcMQ== + +hast-util-has-property@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz#9f137565fad6082524b382c1e7d7d33ca5059f36" + integrity sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg== + +hast-util-is-element@^1.0.0, hast-util-is-element@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + hast-util-sanitize@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz#b0b783220af528ba8fe6999f092d138908678520" @@ -5272,11 +5318,49 @@ hast-util-to-html@^7.0.0, hast-util-to-html@^7.1.2: unist-util-is "^4.0.0" xtend "^4.0.0" -hast-util-whitespace@^1.0.0: +hast-util-to-mdast@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/hast-util-to-mdast/-/hast-util-to-mdast-7.1.3.tgz#e4ad9098929355501773aed5e66c8181559eee04" + integrity sha512-3vER9p8B8mCs5b2qzoBiWlC9VnTkFmr8Ufb1eKdcvhVY+nipt52YfMRshk5r9gOE1IZ9/xtlSxebGCv1ig9uKA== + dependencies: + extend "^3.0.0" + hast-util-has-property "^1.0.0" + hast-util-is-element "^1.1.0" + hast-util-to-text "^2.0.0" + mdast-util-phrasing "^2.0.0" + mdast-util-to-string "^1.0.0" + rehype-minify-whitespace "^4.0.3" + repeat-string "^1.6.1" + trim-trailing-lines "^1.1.0" + unist-util-is "^4.0.0" + unist-util-visit "^2.0.0" + xtend "^4.0.1" + +hast-util-to-text@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz#04f2e065642a0edb08341976084aa217624a0f8b" + integrity sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ== + dependencies: + hast-util-is-element "^1.0.0" + repeat-string "^1.0.0" + unist-util-find-after "^3.0.0" + +hast-util-whitespace@^1.0.0, hast-util-whitespace@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -6768,6 +6852,13 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" + integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== + dependencies: + repeat-string "^1.0.0" + matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -6784,6 +6875,13 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" +mdast-builder@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/mdast-builder/-/mdast-builder-1.1.1.tgz#ccaaa5ead8ec9c69883ec87d289770569e4b49b2" + integrity sha512-a3KBk/LmYD6wKsWi8WJrGU/rXR4yuF4Men0JO0z6dSZCm5FrXXWTRDjqK0vGSqa+1M6p9edeuypZAZAzSehTUw== + dependencies: + "@types/unist" "^2.0.3" + mdast-util-compact@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-3.0.0.tgz#a5127d5473472a272686254e9b619fb8681e5d00" @@ -6809,6 +6907,51 @@ mdast-util-from-markdown@^0.8.0, mdast-util-from-markdown@^0.8.4: parse-entities "^2.0.0" unist-util-stringify-position "^2.0.0" +mdast-util-gfm-autolink-literal@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.2.tgz#da69d700d42e0692a815292d47e8696926d70561" + integrity sha512-WFeIrcNNsfBety0gyWuiBIPing9UkVcl/m2iZOyW1uHEH2evjFocet2h77M24ub0WyZ4ucnQn/jWhO5Ozl6j4g== + +mdast-util-gfm-strikethrough@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz#45eea337b7fff0755a291844fbea79996c322890" + integrity sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA== + dependencies: + mdast-util-to-markdown "^0.6.0" + +mdast-util-gfm-table@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz#af05aeadc8e5ee004eeddfb324b2ad8c029b6ecf" + integrity sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ== + dependencies: + markdown-table "^2.0.0" + mdast-util-to-markdown "~0.6.0" + +mdast-util-gfm-task-list-item@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz#70c885e6b9f543ddd7e6b41f9703ee55b084af10" + integrity sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A== + dependencies: + mdast-util-to-markdown "~0.6.0" + +mdast-util-gfm@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-0.1.1.tgz#105095ae3e33bd489852579a205a9060414d35a5" + integrity sha512-oE1W1zSXU2L2LHg91V22HC3Z1fbsOZTBYUQq+kpM29f9297TbRm0C1l3bQ88RREl0WaUQaB49G7trvwy5utUKQ== + dependencies: + mdast-util-gfm-autolink-literal "^0.1.0" + mdast-util-gfm-strikethrough "^0.2.0" + mdast-util-gfm-table "^0.1.0" + mdast-util-gfm-task-list-item "^0.1.0" + mdast-util-to-markdown "^0.6.1" + +mdast-util-phrasing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-2.0.0.tgz#57e61f2be908be9f5fce54fcc2fa593687986267" + integrity sha512-G1rNlW/sViwzbBYD7+k3mKGtoWV2v4GBFky66OYHfktHe7Hg9R+hH4xpeoOtjYiwTvle8C8wlKMpgqPCkaeK8Q== + dependencies: + unist-util-is "^4.0.0" + mdast-util-to-hast@^10.0.0: version "10.1.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.1.1.tgz#4dce367abdc57311a87cf95da54a4d115b9d25da" @@ -6823,7 +6966,7 @@ mdast-util-to-hast@^10.0.0: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" -mdast-util-to-markdown@^0.6.2: +mdast-util-to-markdown@^0.6.0, mdast-util-to-markdown@^0.6.1, mdast-util-to-markdown@^0.6.2, mdast-util-to-markdown@~0.6.0: version "0.6.2" resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.2.tgz#8fe6f42a2683c43c5609dfb40407c095409c85b4" integrity sha512-iRczns6WMvu0hUw02LXsPDJshBIwtUPbvHBWo19IQeU0YqmzlA8Pd30U8V7uiI0VPkxzS7A/NXBXH6u+HS87Zg== @@ -6835,6 +6978,11 @@ mdast-util-to-markdown@^0.6.2: repeat-string "^1.0.0" zwitch "^1.0.0" +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + mdast-util-to-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" @@ -7801,6 +7949,11 @@ parse5@5.1.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -8474,6 +8627,24 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" +rehype-minify-whitespace@^4.0.3: + version "4.0.5" + resolved "https://registry.yarnpkg.com/rehype-minify-whitespace/-/rehype-minify-whitespace-4.0.5.tgz#5b4781786116216f6d5d7ceadf84e2489dd7b3cd" + integrity sha512-QC3Z+bZ5wbv+jGYQewpAAYhXhzuH/TVRx7z08rurBmh9AbG8Nu8oJnvs9LWj43Fd/C7UIhXoQ7Wddgt+ThWK5g== + dependencies: + hast-util-embedded "^1.0.0" + hast-util-is-element "^1.0.0" + hast-util-whitespace "^1.0.4" + unist-util-is "^4.0.0" + +rehype-parse@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57" + integrity sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw== + dependencies: + hast-util-from-parse5 "^6.0.0" + parse5 "^6.0.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -9751,6 +9922,11 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +trim-trailing-lines@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + trough@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" @@ -9966,6 +10142,13 @@ unist-builder@^2.0.0, unist-builder@^2.0.3: resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +unist-util-find-after@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz#5c65fcebf64d4f8f496db46fa8fd0fbf354b43e6" + integrity sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ== + dependencies: + unist-util-is "^4.0.0" + unist-util-find-all-between@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/unist-util-find-all-between/-/unist-util-find-all-between-2.1.0.tgz#18ddb51f7abc4e22c645b4321e82ab6a6decc640" @@ -10250,6 +10433,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -10327,6 +10515,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -10578,7 +10771,7 @@ xmldom@0.1.x: resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== -xtend@^4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==