From 04722de871d81c05a806bda06b8ee65e786f485f Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 2 Jan 2025 13:56:30 -0800 Subject: [PATCH] Mobile,Desktop: Add additional editor CSS classes --- .../CodeMirror/markdown/decoratorExtension.ts | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/editor/CodeMirror/markdown/decoratorExtension.ts b/packages/editor/CodeMirror/markdown/decoratorExtension.ts index 7f46ed2ade0..f4ea2f275ad 100644 --- a/packages/editor/CodeMirror/markdown/decoratorExtension.ts +++ b/packages/editor/CodeMirror/markdown/decoratorExtension.ts @@ -104,6 +104,45 @@ const taskMarkerDecoration = Decoration.mark({ attributes: { class: 'cm-taskMarker' }, }); +const strikethroughDecoration = Decoration.mark({ + attributes: { class: 'cm-strike' }, +}); + +// A decoration that includes information about its parent nodes +class NestingDecoration { + private decorationCache: Map = new Map(); + + public constructor( + // Parent nodes to include information about + private readonly parentNodes: string[], + // Node to add the decoration to + public readonly childNode: string, + ) { + } + + public toDecoration(parentNodeCounts: Map): Decoration { + let parentCounter = 0; + for (const nodeName of this.parentNodes) { + const nodeCount = parentNodeCounts.get(nodeName); + parentCounter += nodeCount ?? 0; + } + + const cachedDecoration = this.decorationCache.get(parentCounter); + if (cachedDecoration) { + return cachedDecoration; + } + + const decoration = Decoration.mark({ + attributes: { class: `cm-node${this.childNode}-level-${parentCounter}` }, + }); + this.decorationCache.set(parentCounter, decoration); + return decoration; + } +} + +const quoteMarkDecoration = new NestingDecoration(['Blockquote'], 'QuoteMark'); +const listMarkDecoration = new NestingDecoration(['OrderedList', 'BulletList'], 'ListMark'); + const nodeNameToLineDecoration: Record = { 'FencedCode': codeBlockDecoration, 'CodeBlock': codeBlockDecoration, @@ -128,7 +167,7 @@ const nodeNameToLineDecoration: Record = { 'TableRow': tableBodyDecoration, }; -const nodeNameToMarkDecoration: Record = { +const nodeNameToMarkDecoration: Record = { 'InlineCode': inlineCodeDecoration, 'URL': urlDecoration, 'InlineMath': inlineMathDecoration, @@ -136,6 +175,9 @@ const nodeNameToMarkDecoration: Record = { 'TagName': htmlTagNameDecoration, 'HorizontalRule': horizontalRuleDecoration, 'TaskMarker': taskMarkerDecoration, + 'Strikethrough': strikethroughDecoration, + 'QuoteMark': quoteMarkDecoration, + 'ListMark': listMarkDecoration, }; const multilineNodes = { @@ -180,12 +222,16 @@ const computeDecorations = (view: EditorView) => { }; for (const { from, to } of view.visibleRanges) { + // Maps from node names to the number of times a node is the parent of the current. + const parentNodeCounts = new Map(); ensureSyntaxTree( view.state, to, )?.iterate({ from, to, enter: node => { + parentNodeCounts.set(node.name, (parentNodeCounts.get(node.name) ?? 0) + 1); + let blockDecorated = false; // Compute the visible region of the node. @@ -199,7 +245,11 @@ const computeDecorations = (view: EditorView) => { } if (nodeNameToMarkDecoration.hasOwnProperty(node.name)) { - const decoration = nodeNameToMarkDecoration[node.name]; + let decoration = nodeNameToMarkDecoration[node.name]; + if (decoration instanceof NestingDecoration) { + decoration = decoration.toDecoration(parentNodeCounts); + } + addDecorationToRange(viewFrom, viewTo, decoration); } @@ -215,6 +265,12 @@ const computeDecorations = (view: EditorView) => { } } }, + leave: node => { + const prevNestingLevel = parentNodeCounts.get(node.name); + if (prevNestingLevel) { + parentNodeCounts.set(node.name, prevNestingLevel - 1); + } + }, }); }