diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 6e5bc59..0000000 --- a/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules/ - -main.js - -.deprecated/ \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 0807290..0000000 --- a/.eslintrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "env": { "node": true }, - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], - "@typescript-eslint/ban-ts-comment": "off", - "no-prototype-builtins": "off", - "@typescript-eslint/no-empty-function": "off" - } - } \ No newline at end of file diff --git a/src/declaration/index.d.ts b/src/declaration/index.d.ts deleted file mode 100644 index d363c67..0000000 --- a/src/declaration/index.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { type WidgetType } from "@codemirror/view"; -import { type TableCell } from "obsidian"; - -declare module "obsidian" { - - interface Workspace { - _: EventListener; - } - - interface TableCell { - row: number; - col: number; - text: string; - contentEl: HTMLElement; - } - - interface EventListener { - [name: string]: EventRef[]; - } - - interface EventRef { - ctx?: T; - } - - interface MarkdownView { - path: string; - } - - interface MarkdownPostProcessorContext { - containerEl: HTMLElement; - el: HTMLElement; - } - -} - -declare module "@codemirror/view" { - - interface BlockWidget extends WidgetType { - toDOM(view: EditorView): HTMLElement; - containerEl: HTMLElement; - text: string; - } - - interface DocViewBlock { - widget?: T; - } - - interface DocView { - children: DocViewBlock[]; - } - - interface EditorView { - docView: DocView; - } - - interface TableWidget extends BlockWidget { - cellChildMap: Map; - } - - interface TableBlock extends DocViewBlock { - widget: TableWidget; - } - -} \ No newline at end of file diff --git a/src/editorPlugins/decorationBuilders/alignerPlugin.ts b/src/editorPlugins/decorationBuilders/alignerPlugin.ts deleted file mode 100644 index 9e2657f..0000000 --- a/src/editorPlugins/decorationBuilders/alignerPlugin.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Decoration, ViewPlugin, type PluginValue, type DecorationSet, type EditorView, type ViewUpdate } from "@codemirror/view"; -import { type Range } from "@codemirror/state"; -import { RegExpCursor } from "@codemirror/search"; -import { editorLivePreviewField } from "obsidian"; -import { checkSelectionOverlap } from "../../utils/codemirror"; -import { isCodeblock } from "../../utils/codemirror"; -import { HiddenWidget } from "../widgets"; -import { alignerDecorators } from "../decorators"; - -class Aligner implements PluginValue { - - decorations: DecorationSet; - isLivePreview: boolean; - - constructor(view: EditorView) { - this.decorations = this.buildDecorations(view); - this.isLivePreview = view.state.field(editorLivePreviewField); - } - - update(update: ViewUpdate) { - - if (update.state.field(editorLivePreviewField) == this.isLivePreview) { - this.decorations = this.buildDecorations(update.view); - } - - this.isLivePreview = update.state.field(editorLivePreviewField); - - if (update.docChanged || update.selectionSet) { - this.decorations = this.buildDecorations(update.view); - - } - } - - buildDecorations(view: EditorView) { - - let newDecorations: Range[] = []; - - alignerDecorators.forEach((deco) => { - - let searchCursor = new RegExpCursor(view.state.doc, deco.spec.query).next(); - if (searchCursor.done) {return newDecorations}; - let marker = deco.spec.marker as Decoration; - - while (!searchCursor.done) { - let [from, to] = [searchCursor.value.from, searchCursor.value.to]; - if (!isCodeblock(view, from, to)) { - let linePosFrom = view.state.doc.lineAt(from).from; - newDecorations.push(deco.range(linePosFrom, linePosFrom)); - newDecorations.push(marker.range(from, to)); - if (!checkSelectionOverlap(view.state.selection, from, to) && this.isLivePreview) { - this.hideMarker(newDecorations, marker, from, to); - } - } - searchCursor.next(); - } - }); - - return Decoration.set(newDecorations, true); - } - - hideMarker(decorations: Range[], marker: Decoration, from: number, to: number) { - let hiddenMarker = Decoration.replace({ - widget: new HiddenWidget(marker) - }); - decorations.push(hiddenMarker.range(from, to)); - } -} - -export const alignerPlugin = ViewPlugin.fromClass(Aligner, { - decorations: (value) => value.decorations -}); \ No newline at end of file diff --git a/src/editorPlugins/decorationBuilders/customHighlightPlugin.ts b/src/editorPlugins/decorationBuilders/customHighlightPlugin.ts deleted file mode 100644 index c85876d..0000000 --- a/src/editorPlugins/decorationBuilders/customHighlightPlugin.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Decoration, ViewPlugin, type PluginValue, type DecorationSet, type EditorView, type ViewUpdate, drawSelection } from "@codemirror/view"; -import { type Range } from "@codemirror/state"; -import { editorLivePreviewField } from "obsidian"; -import { checkSelectionOverlap } from "../../utils/codemirror"; -import { ColorButton, HiddenWidget } from "../widgets"; -import { highlightDecorator } from "../decorators"; -import { customHighlightField } from "../stateFields"; - -class CustomHighlight implements PluginValue { - - decorations: DecorationSet; - isLivePreview: boolean; - - constructor(view: EditorView) { - this.decorations = this.buildDecorations(view); - this.isLivePreview = view.state.field(editorLivePreviewField); - } - - update(update: ViewUpdate) { - - if (update.state.field(editorLivePreviewField) == this.isLivePreview) { - this.decorations = this.buildDecorations(update.view); - } - - this.isLivePreview = update.state.field(editorLivePreviewField); - - if (update.docChanged || update.selectionSet) { - this.decorations = this.buildDecorations(update.view); - } - } - - buildDecorations(view: EditorView) { - - let newDecorations: Range[] = []; - let marker = highlightDecorator.markerDeco as Decoration; - - view.state.field(customHighlightField).hlCollection.forEach(hl => { - - let [outerFrom, innerFrom, innerTo, outerTo, color] = [...hl]; - let markerLength = color ? color.length + 2 : 0; - - newDecorations.push(Decoration.mark({class: `${highlightDecorator.class}-${color ?? "default"}`}).range(outerFrom, outerTo)); - - if (checkSelectionOverlap(view.state.selection, outerFrom, outerTo) || !this.isLivePreview) { - newDecorations.push(Decoration.widget({widget: new ColorButton( - color, innerFrom, innerFrom + markerLength, outerFrom, outerTo, innerFrom, innerTo - ), side: -1}).range(innerFrom)); - } - - if (color && !(checkSelectionOverlap(view.state.selection, innerFrom, innerFrom + markerLength) || !this.isLivePreview)) { - this.hideMarker(newDecorations, marker, innerFrom, innerFrom + markerLength); - } - }); - - return Decoration.set(newDecorations, true); - } - - hideMarker(decorations: Range[], marker: Decoration, from: number, to: number) { - let hiddenMarker = Decoration.replace({ - widget: new HiddenWidget(marker) - }); - decorations.push( - hiddenMarker.range(from, to) - ); - } -} - -export const customHighlightPlugin = ViewPlugin.fromClass(CustomHighlight); \ No newline at end of file diff --git a/src/editorPlugins/decorationBuilders/extendedFormattingPlugin.ts b/src/editorPlugins/decorationBuilders/extendedFormattingPlugin.ts deleted file mode 100644 index 3a5c05e..0000000 --- a/src/editorPlugins/decorationBuilders/extendedFormattingPlugin.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Decoration, type EditorView, type PluginValue, type DecorationSet, type ViewUpdate, ViewPlugin } from "@codemirror/view"; -import { type Range } from "@codemirror/state"; -import { editorLivePreviewField } from "obsidian"; -import { extendedFormattingField } from "../stateFields"; -import { checkSelectionOverlap } from "../../utils/codemirror"; -import { HiddenWidget } from "../widgets"; -import { type DelimPos } from "../../types"; -import { formattingDecorators, hiddenSpoiler } from "../decorators"; - -class ExtendedFormatting implements PluginValue { - - decorations: DecorationSet; - isLivePreview: boolean; - - constructor(view: EditorView) { - this.decorations = this.buildDecorations(view); - this.isLivePreview = view.state.field(editorLivePreviewField); - } - - update(update: ViewUpdate) { - - if (update.state.field(editorLivePreviewField) == this.isLivePreview) { - this.decorations = this.buildDecorations(update.view); - } - - this.isLivePreview = update.state.field(editorLivePreviewField); - - if (update.docChanged || update.selectionSet) { - this.decorations = this.buildDecorations(update.view); - } - } - - buildDecorations(view: EditorView) { - - let newDecorations: Range[] = []; - - view.state.field(extendedFormattingField).delimField.forEach((posCollection, type) => { - - if (posCollection.length === 0) {return}; - - let decorator = formattingDecorators.get(type) as Decoration; - let delimDecorator = decorator?.spec.delimDeco as Decoration; - - - posCollection.forEach(pos => { - - let hasClosingDelim = pos[2] != pos[3]; - - if (type != "spoiler") { - newDecorations.push(decorator.range(pos[1], pos[2])); - } - - newDecorations.push(delimDecorator.range(pos[0], pos[1])); - if (hasClosingDelim) { - newDecorations.push(delimDecorator.range(pos[2], pos[3])); - } - - if ((!view.hasFocus || !checkSelectionOverlap(view.state.selection, pos[0], pos[3])) && this.isLivePreview) { - type == "spoiler" && newDecorations.push(hiddenSpoiler.range(pos[1], pos[2])); - this.hideDelim(newDecorations, delimDecorator, pos, hasClosingDelim); - } else if (type == "spoiler") { - newDecorations.push(decorator.range(pos[1], pos[2])); - } - }); - }); - - return Decoration.set(newDecorations, true); - } - - hideDelim(decorations: Range[], decorator: Decoration, pos: DelimPos, hasClosingDelim: boolean) { - - let hiddenDelim = Decoration.replace({ - widget: new HiddenWidget(decorator) - }); - - decorations.push(hiddenDelim.range(pos[0], pos[1])); - if (hasClosingDelim) { - decorations.push(hiddenDelim.range(pos[2], pos[3])); - } - } -} - -export const extendedFormattingPlugin = ViewPlugin.fromClass(ExtendedFormatting, { - decorations: value => value.decorations -}); \ No newline at end of file diff --git a/src/editorPlugins/decorationBuilders/index.ts b/src/editorPlugins/decorationBuilders/index.ts deleted file mode 100644 index 1ef9fac..0000000 --- a/src/editorPlugins/decorationBuilders/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./extendedFormattingPlugin"; -export * from "./alignerPlugin"; -export * from "./customHighlightPlugin"; \ No newline at end of file diff --git a/src/editorPlugins/decorators/alignerDecorators.ts b/src/editorPlugins/decorators/alignerDecorators.ts deleted file mode 100644 index 4012f5a..0000000 --- a/src/editorPlugins/decorators/alignerDecorators.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Decoration } from "@codemirror/view" - -export const alignerDecorators = [ - Decoration.line({ - class: "cmx-align-left", - query: /(?<=^|^#{1,6} +)!left!/gm, - style: "text-align: left;", - marker: Decoration.mark({class: "cmx-align-left-marker"}) - }), - Decoration.line({ - class: "cmx-align-right", - query: /(?<=^|^#{1,6} +)!right!/gm, - style: "text-align: right;", - marker: Decoration.mark({class: "cmx-align-right-marker"}) - }), - Decoration.line({ - class: "cmx-align-center", - query: /(?<=^|^#{1,6} +)!center!/gm, - style: "text-align: center;", - marker: Decoration.mark({class: "cmx-align-center-marker"}) - }), - Decoration.line({ - class: "cmx-align-justify", - query: /(?<=^|^#{1,6} +)!justify!/gm, - style: "text-align: justify;", - marker: Decoration.mark({class: "cmx-align-justify-marker"}) - }), -] \ No newline at end of file diff --git a/src/editorPlugins/decorators/formattingDecorators.ts b/src/editorPlugins/decorators/formattingDecorators.ts deleted file mode 100644 index 57700fe..0000000 --- a/src/editorPlugins/decorators/formattingDecorators.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Decoration } from "@codemirror/view"; -import { DelimType } from "../../enums"; - -export const formattingDecorators = new Map([ - [DelimType.U, Decoration.mark({class: "cmx-underline", delimDeco: Decoration.mark({class: "cmx-underline cmx-formatting-underline"})})], - [DelimType.Sup, Decoration.mark({class: "cmx-superscript", delimDeco: Decoration.mark({class: "cmx-superscript cmx-formatting-superscript"})})], - [DelimType.Sub, Decoration.mark({class: "cmx-subscript", delimDeco: Decoration.mark({class: "cmx-subscript cmx-formatting-subscript"})})], - [DelimType.Spoiler, Decoration.mark({class: "cmx-spoiler", delimDeco: Decoration.mark({class: "cmx-spoiler cmx-formatting-spoiler"})})] -]); \ No newline at end of file diff --git a/src/editorPlugins/decorators/hiddenSpoiler.ts b/src/editorPlugins/decorators/hiddenSpoiler.ts deleted file mode 100644 index 8df89f9..0000000 --- a/src/editorPlugins/decorators/hiddenSpoiler.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Decoration } from "@codemirror/view"; - -export const hiddenSpoiler = Decoration.mark({ - class: "cmx-spoiler cmx-spoiler-hidden" -}); \ No newline at end of file diff --git a/src/editorPlugins/decorators/highlightDecorator.ts b/src/editorPlugins/decorators/highlightDecorator.ts deleted file mode 100644 index fc3793a..0000000 --- a/src/editorPlugins/decorators/highlightDecorator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Decoration } from "@codemirror/view"; - -export const highlightDecorator = { - class: "cmx-highlight cmx-highlight", - markerDeco: Decoration.mark({class: "cmx-highlight-marker"}) -} \ No newline at end of file diff --git a/src/editorPlugins/decorators/index.ts b/src/editorPlugins/decorators/index.ts deleted file mode 100644 index c419694..0000000 --- a/src/editorPlugins/decorators/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./alignerDecorators"; -export * from "./highlightDecorator"; -export * from "./formattingDecorators"; -export * from "./hiddenSpoiler"; \ No newline at end of file diff --git a/src/editorPlugins/stateFields/customHighlightField.ts b/src/editorPlugins/stateFields/customHighlightField.ts deleted file mode 100644 index f9d3a48..0000000 --- a/src/editorPlugins/stateFields/customHighlightField.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { type Text, StateField, type StateEffect } from "@codemirror/state"; -import { ParseContext, syntaxTree } from "@codemirror/language"; -import { type SyntaxNode, type Tree } from "@lezer/common"; -import { type HighlightProp } from "../../types"; - -function getHighlight(doc: Text, tree: Tree, previous?: {from: number, hlCollection: HighlightProp[]}) { - - let text = doc.toString(); - let hlChecker = /highlight/; - let hlDelim = /(?:(? { - - if (from >= hlProp[1]) { - - if (from < hlProp[3] || from == hlProp[3]) { - lastIndex = hlProp[0]; - collection.splice(index); - } else { - collection.splice(index + 1); - } - return true; - - } else if (index == 0) { - collection.splice(0); - return true; - } - }); - - hlDelim.lastIndex = lastIndex; - } - - for ( - let hlMatch = hlDelim.exec(text), - node: SyntaxNode; - hlMatch && (node = tree.resolveInner(hlMatch.index, 1)); - hlMatch = hlDelim.exec(text) - ) { - - let openingFrom = node.from; - - if (openingFrom > tree.length) { break } - - if (!hlChecker.test(node.name)) { continue } - - let to: number; - - while (true) { - to = node.to; - node = tree.resolveInner(node.to, 1); - if (!hlChecker.test(node.name)) { break } - } - - let lastNode = tree.resolveInner(to, -1); - let innerHlEnd = /formatting-highlight/.test(lastNode.name) ? lastNode.from : to; - - // opening delim start : openingFrom - // end : hlDelim.lastIndex - // closing delim start : innerHlEnd - // end : to - // highlight color : hlMatch[1] - hlCollection.push([openingFrom, hlDelim.lastIndex, innerHlEnd, to, hlMatch[1]]); - hlDelim.lastIndex = to; - } - - return hlCollection; -} - -export const customHighlightField: StateField<{hlCollection: ReturnType, treeLength: number}> = StateField.define({ - - create(state) { - - let tree = syntaxTree(state); - - return { - hlCollection: getHighlight(state.doc, tree), - treeLength: tree.length - }; - }, - - update(value, transaction) { - - let tree = syntaxTree(transaction.state); - - transaction.effects.forEach((value: StateEffect<{context: ParseContext, tree: Tree}>) => { - tree = (value.value?.tree ?? tree); - }); - - if (transaction.docChanged || tree.length != value.treeLength) { - - let from = Math.min(value.treeLength, tree.length); - value.treeLength = tree.length; - - transaction.changes.iterChangedRanges((fromA) => { - from = Math.min(from, fromA); - }, false); - - getHighlight(transaction.state.doc, tree, {from, hlCollection: value.hlCollection}) - } - - return value; - } -}) \ No newline at end of file diff --git a/src/editorPlugins/stateFields/extendedFormattingField.ts b/src/editorPlugins/stateFields/extendedFormattingField.ts deleted file mode 100644 index 40b0161..0000000 --- a/src/editorPlugins/stateFields/extendedFormattingField.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { StateField, type Text, type StateEffect } from '@codemirror/state'; -import { syntaxTree, type ParseContext } from '@codemirror/language'; -import { type Tree } from "@lezer/common" -import { type DelimPos } from "../../types"; -import { isColumnSeparator, isRestrictedPos, isTable } from '../../utils/codemirror'; -import { delimRegExps } from '../../regExps'; -import { DelimType } from '../../enums'; - -/** - * Used to get the position of each delimiter in the editor. - * - */ -function getDelimiterFromEditor(doc: Text, tree: Tree, type: DelimType, openingDelim: RegExp, closingDelim: RegExp, previous?: {from: number, delimLength: number, posCollection: DelimPos[]}) { - - let text = doc.toString(), - doubleNewLineChar = /\n\n+/g, - verticalBarChar = /(? { - - if (from >= delimPos[1]) { - - if (from < delimPos[3] || (from == delimPos[3] && delimPos[2] == delimPos[3])) { - lastIndex = delimPos[0]; - collection.splice(index); - } else if (from - delimPos[3] < delimLength) { - lastIndex = delimPos[3]; - collection.splice(index + 1); - } else { - // type == "spoiler" && (lastIndex = Math.max(closingTo, lastIndex)); - collection.splice(index + 1); - } - return true; - - } else if (index == 0) { - collection.splice(0); - return true; - } - }); - - openingDelim.lastIndex = doubleNewLineChar.lastIndex = lastIndex; - } - - mainLoop: for ( - let openingPos = openingDelim.exec(text)?.indices![0], - endLineOffset = doubleNewLineChar.exec(text)?.index ?? text.length; - openingPos; - openingPos = openingDelim.exec(text)?.indices![0] - ) { - - if (openingPos[1] > tree.length) { break } - - if (isRestrictedPos(tree, openingPos[0])) { continue } - - let delimPos: DelimPos = [0, 0, 0, 0]; - - closingDelim.lastIndex = openingPos[1]; - let endCellOffset = 0, - currentLine = doc.lineAt(openingPos[0]), - isTableRow = isTable(tree, openingPos[0]); - - if (isTableRow) { - - if (type == "spoiler") { - openingDelim.lastIndex = currentLine.to; - continue; - } - - for (verticalBarChar.lastIndex = openingPos[0] - currentLine.from;;) { - let verticalBarPos = verticalBarChar.exec(currentLine.text)?.index; - - if (!verticalBarPos) { - endCellOffset = currentLine.to; - break; - - } else if (isColumnSeparator(tree, verticalBarPos + currentLine.from)) { - endCellOffset = verticalBarPos + currentLine.from; - break; - } - } - } - - if (openingDelim.lastIndex > endLineOffset) { - - doubleNewLineChar.lastIndex = openingDelim.lastIndex; - endLineOffset = doubleNewLineChar.exec(text)?.index || text.length; - } - - [delimPos[0], delimPos[1]] = openingPos; - - while (true) { - - let closingPos = closingDelim.exec(text)?.indices![0]; - - if (!closingPos || closingDelim.lastIndex > endLineOffset || (isTableRow && closingDelim.lastIndex > endCellOffset)) { - delimPos[2] = delimPos[3] = openingDelim.lastIndex = isTableRow ? endCellOffset : endLineOffset; - closingDelim.lastIndex > endLineOffset && (endLineOffset = doubleNewLineChar.exec(text)?.index || text.length); - // if (type == "spoiler") { continue mainLoop } - - } else { - if (isRestrictedPos(tree, closingPos[0])) { continue } - openingDelim.lastIndex = closingPos[1]; - [delimPos[2], delimPos[3]] = closingPos; - } - - break; - } - - delimPosCollection.push(delimPos); - } - - closingDelim.lastIndex &&= 0; - return delimPosCollection; -} - -/** - * @returns - */ -export const extendedFormattingField: StateField<{delimField: Map>, treeLength: number}> = StateField.define({ - - create(state) { - - let delimField = new Map>(); - let tree = syntaxTree(state); - - delimRegExps.forEach((value, type) => { - delimField.set(type, getDelimiterFromEditor(state.doc, tree, type, value.openingDelim, value.closingDelim)); - }); - - return {delimField, treeLength: tree.length}; - - }, - - update(value, transaction) { - - let tree = syntaxTree(transaction.state); - - transaction.effects.forEach((value: StateEffect<{context: ParseContext, tree: Tree}>) => { - tree = (value.value?.tree ?? tree); - }); - - if (transaction.docChanged || tree.length != value.treeLength) { - - let from = Math.min(value.treeLength, tree.length); - value.treeLength = tree.length; - - transaction.changes.iterChangedRanges((fromA) => { - from = Math.min(from, fromA); - }, false); - - value.delimField.forEach((posCollection, type) => { - let {openingDelim, closingDelim, length} = delimRegExps.get(type)!; - getDelimiterFromEditor(transaction.state.doc, tree, type, openingDelim, closingDelim, {from, delimLength: length, posCollection}); - }); - } - - return value; - } -}); \ No newline at end of file diff --git a/src/editorPlugins/stateFields/index.ts b/src/editorPlugins/stateFields/index.ts deleted file mode 100644 index 47fc6de..0000000 --- a/src/editorPlugins/stateFields/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./extendedFormattingField"; -export * from "./customHighlightField"; \ No newline at end of file diff --git a/src/editorPlugins/widgets/ColorButton.ts b/src/editorPlugins/widgets/ColorButton.ts deleted file mode 100644 index 02718a3..0000000 --- a/src/editorPlugins/widgets/ColorButton.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { WidgetType, EditorView } from "@codemirror/view" -import { Menu } from "obsidian"; - -/** - * These code snippets are taken from - * https://github.com/Superschnizel/obisdian-fast-text-color/blob/master/src/widgets/ColorWidget.ts - * with some modifications. -*/ -export class ColorButton extends WidgetType { - - color: string; - markerFrom: number; - markerTo: number; - menu: Menu | null; - outerFrom: number; - outerTo: number; - innerFrom: number; - innerTo: number; - - private readonly colors = ["red", "orange", "yellow", "green", "cyan", "blue", "purple", "pink", "accent", "default"]; - - constructor(color: string = "default", markerFrom: number, markerTo: number, outerFrom: number, outerTo: number, innerFrom: number, innerTo: number) { - super(); - this.color = color; - this.markerFrom = markerFrom; - this.markerTo = markerTo; - this.outerFrom = outerFrom; - this.outerTo = outerTo; - this.innerFrom = innerFrom; - this.innerTo = innerTo; - } - - eq(other: ColorButton) { - return other.color == this.color; - } - - toDOM(view: EditorView): HTMLElement { - let btn = document.createElement("span"); - - btn.setAttribute("aria-hidden", "true"); - btn.className = "cmx-color-btn"; - - btn.onclick = evt => { - view.dispatch({ - selection: { - anchor: this.markerFrom, - head: this.markerTo - } - }); - } - - btn.onmouseover = evt => { - if (this.menu != null) { - return; - } - - this.menu = new Menu(); - - // @ts-ignore - (this.menu.dom as HTMLElement).addClass("es-highlight-colors-modal"); - - this.colors.forEach((color) => { - this.menu!.addItem((item) => { - item - .setTitle(color) - .onClick(evt => { - view.dispatch({ - changes: { - from: this.markerFrom, - to: this.markerTo, - insert: color != "default" ? `{${color}}` : "" - } - }) - }) - .setIcon("palette"); - // @ts-ignore - (item.dom as HTMLElement).addClass(`es-item-${color}`); - }) - }); - - this.menu.addItem(item => { - item - .setTitle("Remove") - .setIcon("ban") - .onClick((evt) => { - view.dispatch({ - changes: [{ - from: this.outerFrom, - to: this.markerTo, - insert: '' - }, { - from: this.innerTo, - to: this.outerTo, - insert: '' - }] - }); - }); - }); - - const rect = btn.getBoundingClientRect(); - this.menu.showAtPosition({ x: rect.left, y: rect.bottom }) - } - - return btn; - } - - ignoreEvent() { - return false; - } -} \ No newline at end of file diff --git a/src/editorPlugins/widgets/HiddenWidget.ts b/src/editorPlugins/widgets/HiddenWidget.ts deleted file mode 100644 index ca20acc..0000000 --- a/src/editorPlugins/widgets/HiddenWidget.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { WidgetType, Decoration, EditorView } from "@codemirror/view" - -export class HiddenWidget extends WidgetType { - - mark: Decoration; - - constructor(formattingMark: Decoration) { - super(); - this.mark = formattingMark; - } - - eq(other: HiddenWidget) { - return other.mark == this.mark; - } - - toDOM(view: EditorView): HTMLElement { - return document.createElement("span"); - } -} \ No newline at end of file diff --git a/src/editorPlugins/widgets/index.ts b/src/editorPlugins/widgets/index.ts deleted file mode 100644 index 5e44034..0000000 --- a/src/editorPlugins/widgets/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./ColorButton"; -export * from "./HiddenWidget"; \ No newline at end of file diff --git a/src/interfaces/BlockquoteChildSection.ts b/src/interfaces/BlockquoteChildSection.ts deleted file mode 100644 index c721e79..0000000 --- a/src/interfaces/BlockquoteChildSection.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface BlockquoteChildSection { - text: string; - level: number; -} \ No newline at end of file diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts deleted file mode 100644 index 59b058f..0000000 --- a/src/interfaces/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./BlockquoteChildSection"; \ No newline at end of file diff --git a/src/postProcessor/AlignerPostProcessor.ts b/src/postProcessor/AlignerPostProcessor.ts deleted file mode 100644 index 5e071af..0000000 --- a/src/postProcessor/AlignerPostProcessor.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { type MarkdownPostProcessor } from "obsidian"; - -export class AlignerPostProcessor { - - private readonly targetedElements = 'p, h1, h2, h3, h4, h5, h6, td, th, .callout-title-inner'; - - constructor() {} - - private format = (el: HTMLElement) => { - - let alignMark = /^!((?:left)|(?:right)|(?:center)|(?:justify))!/d; - let alignMarkExecArr: RegExpExecArray | null; - let firstChildNode = el.firstChild; - - if (!firstChildNode || el.parentElement?.tagName == "BLOCKQUOTE") { return } - - if (el instanceof HTMLHeadingElement && (firstChildNode = el.childNodes[1]) instanceof Text) { - alignMarkExecArr = alignMark.exec(firstChildNode.textContent ?? ""); - - } else { - - if (!(firstChildNode instanceof Text)) { return } - - el.parentElement?.hasClass("callout-content") && (alignMark = /^ *!((?:left)|(?:right)|(?:center)|(?:justify))!/d); - alignMarkExecArr = alignMark.exec(firstChildNode.textContent ?? ""); - } - - if (alignMarkExecArr) { - let [from, to] = alignMarkExecArr.indices![0]; - firstChildNode.replaceData(from, to - from, ""); - el.addClass(`cmx-align-${alignMarkExecArr[1]}`); - } - } - - postProcess: MarkdownPostProcessor = (container) => { - if (container.classList.contains("table-cell-wrapper")) { - this.format(container); - } else { - container.querySelectorAll(this.targetedElements).forEach((el) => { - this.format(el); - }); - } - } -} \ No newline at end of file diff --git a/src/postProcessor/CustomHighlightPostProcessor.ts b/src/postProcessor/CustomHighlightPostProcessor.ts deleted file mode 100644 index a444b63..0000000 --- a/src/postProcessor/CustomHighlightPostProcessor.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { type MarkdownPostProcessor } from "obsidian"; - -export class CustomHighlightPostProcessor { - - constructor() {} - - private format = (el: HTMLElement) => { - - let markElements = el.querySelectorAll("mark"); - let colorMark = /^\{([\w\d\-_]+)\}/d; - - markElements.forEach((mark) => { - - let firstChildNode = mark.firstChild; - - if (!(firstChildNode instanceof Text && firstChildNode.textContent)) { return } - - let colorMarkExecArr = colorMark.exec(firstChildNode.textContent); - - if (colorMarkExecArr) { - - let [from, to] = colorMarkExecArr.indices![0]; - firstChildNode.replaceData(from, to - from, ""); - mark.classList.add(`cmx-highlight-${colorMarkExecArr[1]}`); - } - }); - } - - postProcess: MarkdownPostProcessor = (container, t) => { - this.format(container); - } -} \ No newline at end of file diff --git a/src/postProcessor/ExtendedFormattingPostProcessor.ts b/src/postProcessor/ExtendedFormattingPostProcessor.ts deleted file mode 100644 index b848f10..0000000 --- a/src/postProcessor/ExtendedFormattingPostProcessor.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { type TableBlock } from "@codemirror/view"; -import { type Workspace, type MarkdownPostProcessor, MarkdownView, EventRef } from "obsidian"; -import { postProcessorDelimRegExps } from "../regExps"; -import { iterDelimReplacement, splitCells, getBlockquoteSections } from "../utils/postProcess"; -import { getTextAtLine } from "../utils"; - -export class ExtendedFormattingPostProcessor { - - workspace: Workspace; - - private readonly targetedElements = 'p, li, h1, h2, h3, h4, h5, h6, .callout-title-inner'; - - constructor(workspace: Workspace) { - this.workspace = workspace; - } - - private format(contentEl: HTMLElement, rawText: string, isTableCell: boolean = false) { - - postProcessorDelimRegExps.forEach((delimQuery, tagName) => { - iterDelimReplacement(contentEl, rawText, delimQuery, tagName, isTableCell); - }); - } - - postProcess: MarkdownPostProcessor = (container, ctx) => { - - let sectionInfo = ctx.getSectionInfo(container); - - if (sectionInfo) { - - let rawTextData = getTextAtLine(sectionInfo.text, sectionInfo.lineStart, sectionInfo.lineEnd); - let firstChild = container.firstElementChild; - - if (firstChild instanceof HTMLTableElement) { - - splitCells(rawTextData).forEach((rawText, i) => { - this.format(container.querySelectorAll("td, th")[i], rawText, true); - }); - - } else if (firstChild?.tagName == "BLOCKQUOTE") { - - getBlockquoteSections(rawTextData, false).forEach((section, i) => { - this.format(container.querySelectorAll(this.targetedElements)[i], section.text); - }); - - } else if (firstChild?.hasClass("callout")) { - - getBlockquoteSections(rawTextData, true).forEach((section, i) => { - this.format(container.querySelectorAll(this.targetedElements)[i], section.text); - }); - - } else { - - container.querySelectorAll(this.targetedElements).forEach(contentEl => { - this.format(contentEl, rawTextData); - }); - } - - container.querySelectorAll("spoiler").forEach(el => { - el.addEventListener("click", event => { - let spoiler = event.currentTarget as HTMLElement; - spoiler.hasClass("cmx-revealed") ? spoiler.removeClass("cmx-revealed") : spoiler.addClass("cmx-revealed"); - }) - }) - - } else { - - let cmEditor = this.workspace.activeEditor?.editor?.cm ?? - this.workspace._["quick-preview"] - .find((evtRef): evtRef is EventRef => evtRef.ctx instanceof MarkdownView && evtRef.ctx?.path == ctx.sourcePath) - ?.ctx?.editor.cm!; - - if (container.classList.contains("table-cell-wrapper")) { - - let tableBlock = cmEditor.docView.children.find((block): block is TableBlock => block.widget?.containerEl == ctx.containerEl); - let tableCells = tableBlock?.widget?.cellChildMap.keys()!; - - for (let cell of tableCells) { - if (cell.contentEl == ctx.el) { - this.format(container, cell.text, true); - break; - } - } - - } else if (ctx.containerEl.classList.contains("cm-callout")) { - - let calloutBlock= cmEditor.docView.children.find(el => el.widget?.containerEl == ctx.containerEl); - let calloutText = calloutBlock?.widget?.text!; - - getBlockquoteSections(calloutText, true).forEach((section, i) => { - this.format(container.querySelectorAll(this.targetedElements)[i], section.text); - }); - } - } - } -} \ No newline at end of file diff --git a/src/postProcessor/index.ts b/src/postProcessor/index.ts deleted file mode 100644 index 3f9c984..0000000 --- a/src/postProcessor/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./AlignerPostProcessor"; -export * from "./CustomHighlightPostProcessor"; -export * from "./ExtendedFormattingPostProcessor"; \ No newline at end of file diff --git a/src/regExps/delimRegExps.ts b/src/regExps/delimRegExps.ts deleted file mode 100644 index b15fe3b..0000000 --- a/src/regExps/delimRegExps.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { DelimType } from "../enums"; - -export const delimRegExps = new Map([ - [DelimType.U, { - openingDelim: /(?:(?; + +export type MainFormat = Extract; + +export type TokenGroup = Token[]; + +export type MainFormat2 = Exclude; \ No newline at end of file diff --git a/src/utils/codemirror/checkSelectionOverlap.ts b/src/utils/codemirror/checkSelectionOverlap.ts deleted file mode 100644 index 7109c2d..0000000 --- a/src/utils/codemirror/checkSelectionOverlap.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { EditorSelection } from "@codemirror/state"; - -/** - * These code snippets are taken from - * https://github.com/Mara-Li/obsidian-regex-mark/blob/master/src/cmPlugin.ts - * with some modifications. -*/ -export function checkSelectionOverlap(selection: EditorSelection, from: number, to: number): boolean { - if (!selection) {return false} - for (const range of selection.ranges) { - if (range.to >= from && range.from <= to) { - return true; - } - return false; - } - return false; -} \ No newline at end of file diff --git a/src/utils/codemirror/index.ts b/src/utils/codemirror/index.ts deleted file mode 100644 index 1d2333f..0000000 --- a/src/utils/codemirror/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./checkSelectionOverlap"; -export * from "./isCodeblock"; -export * from "./isColumnSeparator"; -export * from "./isRestrictedPos"; -export * from "./isTable"; \ No newline at end of file diff --git a/src/utils/codemirror/isCodeblock.ts b/src/utils/codemirror/isCodeblock.ts deleted file mode 100644 index 9402bce..0000000 --- a/src/utils/codemirror/isCodeblock.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { syntaxTree } from "@codemirror/language"; -import { EditorView } from "@codemirror/view"; - -export function isCodeblock(view: EditorView, from: number, to: number): boolean { - let isCodeblock: boolean = false; - syntaxTree(view.state).iterate({ - from, - to, - enter: (node) => { - // console.log(node.name); - if (/^inline-code/.test(node.name) || node.name == 'HyperMD-codeblock_HyperMD-codeblock-bg') { - isCodeblock = true; - return false; // short circuit child iteration - } - } - }); - return isCodeblock; -} \ No newline at end of file diff --git a/src/utils/codemirror/isColumnSeparator.ts b/src/utils/codemirror/isColumnSeparator.ts deleted file mode 100644 index b038461..0000000 --- a/src/utils/codemirror/isColumnSeparator.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { type Tree } from "@lezer/common"; - -export function isColumnSeparator(tree: Tree, pos: number): boolean { - return /hmd-table-sep/.test(tree.resolveInner(pos, 1).name); -} \ No newline at end of file diff --git a/src/utils/codemirror/isRestrictedPos.ts b/src/utils/codemirror/isRestrictedPos.ts deleted file mode 100644 index aaa0801..0000000 --- a/src/utils/codemirror/isRestrictedPos.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type Tree } from "@lezer/common"; - -export function isRestrictedPos(tree: Tree, pos: number): boolean { - return /(?:inline-code)|(?:tag)|(?:HyperMD-codeblock)|(?:footref)|(?:hmd-internal-link)|(?:hmd-footnote)|(?:math)|(?:hmd-codeblock)|(?:formatting-strikethrough)/ - .test(tree.resolveInner(pos, 1).name); -} \ No newline at end of file diff --git a/src/utils/codemirror/isTable.ts b/src/utils/codemirror/isTable.ts deleted file mode 100644 index 26dfc7f..0000000 --- a/src/utils/codemirror/isTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { type Tree } from "@lezer/common"; - -export function isTable(tree: Tree, pos: number): boolean { - let isTable: boolean = false; - tree.iterate({ - from: pos, - to: pos, - enter: (node) => { - if (/table/.test(node.name)) { - isTable = true; - return false; // short circuit child iteration - } - } - }); - return isTable; -} \ No newline at end of file diff --git a/src/utils/getTextAtLine.ts b/src/utils/getTextAtLine.ts deleted file mode 100644 index c8ccb77..0000000 --- a/src/utils/getTextAtLine.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Get text on a specific line, based on newline character (\n). - */ -export function getTextAtLine(text: string, lineStart: number, lineEnd: number) { - return text.split("\n").slice(lineStart, lineEnd + 1).join("\n"); -} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index faa3721..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./getTextAtLine"; \ No newline at end of file diff --git a/src/utils/postProcess/getBlockquoteSections.ts b/src/utils/postProcess/getBlockquoteSections.ts deleted file mode 100644 index c977929..0000000 --- a/src/utils/postProcess/getBlockquoteSections.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { BlockquoteChildSection } from "../../interfaces"; - -/** - * Splits the blockquote/callout into {@link BlockquoteChildSection sections}. - * This function also removes codeblocks, mathblocks, and empty row instead - * of keeping them in the sections, as they will not be parsed. Rows that are - * not interspersed by empty row will be considered as one section. - * - * @example - * ```` - * > first section - * > merged to the first - * > - * > $$ - * > math block - * > $$ - * > ``` - * > codeblock - * > ``` - * > - * > second section - * ```` - * - * The blockquote above will be divided into 2 sections - * - * @param text - Must be a markdown text representing a blockquote/callout - * @param isCallout - If true then the first line representing callout title will be a standalone section - */ -export function getBlockquoteSections(text: string, isCallout: boolean) { - - let blockquoteDelim = /^ *>/; - let sections: BlockquoteChildSection[] = []; - - text.split("\n").forEach((text) => { // splits blockquotes into lines based on "\n" character - - /** blockquote level */ - let level = 0; - - while (blockquoteDelim.test(text) && ++level) { // the blockquote level is dependent on the number of delimiters - text = text.replace(blockquoteDelim, ""); // strips out the blockquote/callout delimiters (">") - } - - text = text.trimStart(); - sections.push({ - text: text, - level: level - }); - }); - - // splits the blockquote/callout into sections - loop1: for ( - let i = 0, - /** if true then the following row will be merged with the previous row */ - mergePrevious = false; - i < sections.length; - ) { - - let currentLine = sections[i]; - - if (isCallout && i === 0) { - i++; - continue; - } - - if (/^```/.test(currentLine.text)) { // removes codeblock - - let k = 1; - while (sections[i + k]) { - let {level, text} = sections[i + k++]; - if ( - (text === "" && level < currentLine.level) || - (/^``` *$/.test(text) && level == currentLine.level) - ) { break } - } - - sections.splice(i, k), mergePrevious &&= false; - continue loop1; - - } else if (/^\$\$/.test(currentLine.text)) { // removes mathblock - - let k = 1; - while (sections[i + k]) { - let {level, text} = sections[i + k++]; - if ( - (text === "" && level < currentLine.level && k--) || - (/^\$\$ *$/.test(text) && level == currentLine.level) - ) { break } - } - - if (k !== 1) { - sections.splice(i, k), mergePrevious &&= false; - continue loop1; - } - - } else if (currentLine.text === "") { // removes empty row - sections.splice(i, 1), mergePrevious &&= false; - continue loop1; - } - - // rows that are not interspersed by empty row will be considered as one section. - (mergePrevious && sections[i - 1].level >= currentLine.level) && (sections[i - 1].text += `\n${sections.splice(i, 1)[0].text}`, i--); - i++, mergePrevious ||= true; - } - - return sections; -} \ No newline at end of file diff --git a/src/utils/postProcess/getDelimCounts.ts b/src/utils/postProcess/getDelimCounts.ts deleted file mode 100644 index 185b07e..0000000 --- a/src/utils/postProcess/getDelimCounts.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { getRestrictedRanges } from "."; - -export function getDelimCounts(text: string, openingDelim: RegExp, closingDelim: RegExp, originDelim: string, ignoreEscaper: boolean = false) { - - let restrictedRanges = getRestrictedRanges(text); - let counts: number[] = []; - - let delimChar = originDelim.charAt(0); - ignoreEscaper && (text = text.replaceAll(`\\${delimChar}`, delimChar)); - - openingDelim.lastIndex = 0; - closingDelim.lastIndex = 0; - - for (let openingMatch = openingDelim.exec(text), i = 0; openingMatch; openingMatch = openingDelim.exec(text)) { - - let [all, escaper, target, slippedBackslash, next] = openingMatch.indices!; - - if (restrictedRanges.some(range => range[0] <= all[0] && range[1] >= all[1])) { - continue; - } - - closingDelim.lastIndex = all[1]; - - if (escaper || slippedBackslash || next) { - openingDelim.lastIndex = target[0] + 1; - i++; - continue; - } - - counts.push(i++); - - for( - let closingMatch = closingDelim.exec(text); - closingMatch || ((openingDelim.lastIndex = text.length), false); - closingMatch = closingDelim.exec(text) - ) { - - let [all, escaper, nonEscaper, target, slippedBackslash] = [...closingMatch!.indices!]; - - if (restrictedRanges.some(range => range[0] <= all[0] && range[1] >= all[1])) { - continue; - } - - if (escaper || slippedBackslash) { - closingDelim.lastIndex = target[0] + 1; - i++; - continue; - } - - counts.push(i++); - openingDelim.lastIndex = all[1]; - break; - } - } - - return counts; -} \ No newline at end of file diff --git a/src/utils/postProcess/getRestrictedRange.ts b/src/utils/postProcess/getRestrictedRange.ts deleted file mode 100644 index c36373e..0000000 --- a/src/utils/postProcess/getRestrictedRange.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Pos } from "../../types"; - -export function getRestrictedRanges(text: string) { - - let query = /(\\)|`(?:[^`])+`|\[\[\[?(?!\[)(?:.|\s)+?(?, tagName: DelimType, isTableCell?: boolean) { - - let ignoreEscaper = !!(isTableCell && tagName == "spoiler"); - let {openingDelim, closingDelim, raw, length: delimLength, origin: originDelim} = delimQuery!; - let delimIndexes = getDelimCounts(rawText, raw.openingDelim, raw.closingDelim, originDelim, ignoreEscaper); - let lastDelimCount = delimIndexes[delimIndexes.length - 1]; - let formattedRange: Range[] = []; - - if (delimIndexes.length == 0) { return } - - openingDelim.lastIndex = closingDelim.lastIndex = 0; - let treeWalker = document.createTreeWalker(contentEl, 5, (node: Text | Element) => { - - if (node instanceof Text) { - if (node.textContent?.includes(originDelim)) { - return NodeFilter.FILTER_ACCEPT; - } else { - return NodeFilter.FILTER_REJECT; - } - - } else { - if (node instanceof HTMLBRElement && node.nextSibling instanceof HTMLBRElement) { - return NodeFilter.FILTER_ACCEPT; - } else if (node.tagName == "CODE" || node.hasClass("internal-link") || node.hasClass("math")) { - return NodeFilter.FILTER_REJECT; - } else { - return NodeFilter.FILTER_SKIP; - } - } - }); - - treeWalker.nextNode(); - - for ( - let i = 0, - node = treeWalker.currentNode, - matched: RegExpExecArray | null, - closingTurn = false, - endOfTree = false, - range: Range; - i <= lastDelimCount + 1 || endOfTree; - ) { - - if (node instanceof Text && !closingTurn) { - - matched = openingDelim.exec(node.textContent ?? ""); - - if (matched) { - - if (matched.index + delimLength == node.length && node.nextSibling instanceof HTMLBRElement) { continue } - - if (delimIndexes[0] === i++) { - closingTurn = true; - node.deleteData((closingDelim.lastIndex = matched.index), delimLength); - (range = document.createRange()).setStart(node, matched.index); - delimIndexes.shift(); - } - - } else { - - openingDelim.lastIndex = 0; - - if (treeWalker.nextNode()) { - node = treeWalker.currentNode; - } else { break } - } - - continue; - } - - if (closingTurn) { - - if (node instanceof Text) { - - matched = closingDelim.exec(node.textContent ?? ""); - - if (matched) { - - if (matched.index == 0 && node.previousSibling instanceof HTMLBRElement) { continue } - - if (delimIndexes[0] == i++) { - /\s/.test(node.textContent!.charAt(matched.index - 1)) && matched.index++; - node.deleteData((openingDelim.lastIndex = matched.index), delimLength); - range!.setEnd(node, matched.index); - delimIndexes.shift(); - - } else { - continue; - } - - } else { - - closingDelim.lastIndex = 0; - - if (treeWalker.nextNode()) { - node = treeWalker.currentNode; - continue; - } - - range!.setEndAfter(node); - endOfTree = true; - } - - } else { - range!.setEndBefore(node as HTMLBRElement); - } - - formattedRange.push(range!.cloneRange()); - closingTurn = false; - } - - if (endOfTree) { break } - } - - formattedRange.forEach(range => { - let formattedEl = document.createElement(tagName); - formattedEl.append(range!.extractContents()); - range!.insertNode(formattedEl); - }); -} \ No newline at end of file diff --git a/src/utils/postProcess/splitCells.ts b/src/utils/postProcess/splitCells.ts deleted file mode 100644 index 0363c4e..0000000 --- a/src/utils/postProcess/splitCells.ts +++ /dev/null @@ -1,20 +0,0 @@ -export function splitCells(text: string) { - - let rows = text.split("\n"); - let cells: string[] = []; - - rows.forEach((row, index) => { - - if (index === 1) { return } - - row = row.trim(); - row.startsWith("|") && (row = row.substring(1)); - row.endsWith("|") && (row = row.substring(0, row.length - 1)); - - row.split(/(?:(? { - cells.push(cell); - }); - }); - - return cells; -} \ No newline at end of file