From 71ab0e0c9dc2b5a0966e42bc00860b7471d0f8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 6 Feb 2019 15:09:12 +0100 Subject: [PATCH] move highlight plugin to its own file --- packages/tiptap-extensions/src/index.js | 1 + .../src/nodes/CodeBlockHighlight.js | 98 ++----------------- .../src/plugins/Highlight.js | 85 ++++++++++++++++ 3 files changed, 94 insertions(+), 90 deletions(-) create mode 100644 packages/tiptap-extensions/src/plugins/Highlight.js diff --git a/packages/tiptap-extensions/src/index.js b/packages/tiptap-extensions/src/index.js index c147cab33..d0d6a1691 100644 --- a/packages/tiptap-extensions/src/index.js +++ b/packages/tiptap-extensions/src/index.js @@ -27,3 +27,4 @@ export { default as History } from './extensions/History' export { default as Placeholder } from './extensions/Placeholder' export { default as Suggestions } from './plugins/Suggestions' +export { default as Highlight } from './plugins/Highlight' diff --git a/packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js b/packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js index 85ab608cc..f8a2c23c3 100644 --- a/packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js +++ b/packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js @@ -1,65 +1,7 @@ -import { Node, Plugin } from 'tiptap' -import { Decoration, DecorationSet } from 'prosemirror-view' -import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands' -import { findBlockNodes } from 'prosemirror-utils' +import { Node } from 'tiptap' import low from 'lowlight/lib/core' - -function getDecorations(doc) { - const decorations = [] - - const blocks = findBlockNodes(doc) - .filter(item => item.node.type.name === 'code_block') - - const flatten = list => list.reduce( - (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [], - ) - - function parseNodes(nodes, className = []) { - return nodes.map(node => { - - const classes = [ - ...className, - ...node.properties ? node.properties.className : [], - ] - - if (node.children) { - return parseNodes(node.children, classes) - } - - return { - text: node.value, - classes, - } - }) - } - - blocks.forEach(block => { - let startPos = block.pos + 1 - const nodes = low.highlightAuto(block.node.textContent).value - - flatten(parseNodes(nodes)) - .map(node => { - const from = startPos - const to = from + node.text.length - - startPos = to - - return { - ...node, - from, - to, - } - }) - .forEach(node => { - const decoration = Decoration.inline(node.from, node.to, { - class: node.classes.join(' '), - }) - decorations.push(decoration) - }) - }) - - return DecorationSet.create(doc, decorations) -} +import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands' +import HighlightPlugin from '../plugins/Highlight' export default class CodeBlockHighlight extends Node { @@ -74,16 +16,16 @@ export default class CodeBlockHighlight extends Node { } } + get name() { + return 'code_block' + } + get defaultOptions() { return { languages: {}, } } - get name() { - return 'code_block' - } - get schema() { return { content: 'text*', @@ -117,31 +59,7 @@ export default class CodeBlockHighlight extends Node { get plugins() { return [ - new Plugin({ - state: { - init(_, { doc }) { - return getDecorations(doc) - }, - apply(transaction, decorationSet, oldState, state) { - // TODO: find way to cache decorations - // see: https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493 - - const nodeName = state.selection.$head.parent.type.name - const previousNodeName = oldState.selection.$head.parent.type.name - - if (transaction.docChanged && [nodeName, previousNodeName].includes('code_block')) { - return getDecorations(transaction.doc) - } - - return decorationSet.map(transaction.mapping, transaction.doc) - }, - }, - props: { - decorations(state) { - return this.getState(state) - }, - }, - }), + HighlightPlugin({ name: this.name }), ] } diff --git a/packages/tiptap-extensions/src/plugins/Highlight.js b/packages/tiptap-extensions/src/plugins/Highlight.js new file mode 100644 index 000000000..6ed761730 --- /dev/null +++ b/packages/tiptap-extensions/src/plugins/Highlight.js @@ -0,0 +1,85 @@ +import { Plugin, PluginKey } from 'tiptap' +import { Decoration, DecorationSet } from 'prosemirror-view' +import { findBlockNodes } from 'prosemirror-utils' +import low from 'lowlight/lib/core' + +function getDecorations({ doc, name }) { + const decorations = [] + const blocks = findBlockNodes(doc).filter(item => item.node.type.name === name) + const flatten = list => list.reduce( + (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [], + ) + + function parseNodes(nodes, className = []) { + return nodes.map(node => { + + const classes = [ + ...className, + ...node.properties ? node.properties.className : [], + ] + + if (node.children) { + return parseNodes(node.children, classes) + } + + return { + text: node.value, + classes, + } + }) + } + + blocks.forEach(block => { + let startPos = block.pos + 1 + const nodes = low.highlightAuto(block.node.textContent).value + + flatten(parseNodes(nodes)) + .map(node => { + const from = startPos + const to = from + node.text.length + + startPos = to + + return { + ...node, + from, + to, + } + }) + .forEach(node => { + const decoration = Decoration.inline(node.from, node.to, { + class: node.classes.join(' '), + }) + decorations.push(decoration) + }) + }) + + return DecorationSet.create(doc, decorations) +} + +export default function HighlightPlugin({ name }) { + return new Plugin({ + name: new PluginKey('highlight'), + state: { + init: (_, { doc }) => getDecorations({ doc, name }), + apply: (transaction, decorationSet, oldState, state) => { + // TODO: find way to cache decorations + // see: https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493 + + const nodeName = state.selection.$head.parent.type.name + const previousNodeName = oldState.selection.$head.parent.type.name + + if (transaction.docChanged && [nodeName, previousNodeName].includes(name)) { + return getDecorations({ doc: transaction.doc, name }) + } + + return decorationSet.map(transaction.mapping, transaction.doc) + }, + }, + props: { + decorations(state) { + return this.getState(state) + }, + }, + }) +}