move highlight plugin to its own file

This commit is contained in:
Philipp Kühn 2019-02-06 15:09:12 +01:00
parent 59d37f5697
commit 71ab0e0c9d
3 changed files with 94 additions and 90 deletions

View File

@ -27,3 +27,4 @@ export { default as History } from './extensions/History'
export { default as Placeholder } from './extensions/Placeholder' export { default as Placeholder } from './extensions/Placeholder'
export { default as Suggestions } from './plugins/Suggestions' export { default as Suggestions } from './plugins/Suggestions'
export { default as Highlight } from './plugins/Highlight'

View File

@ -1,65 +1,7 @@
import { Node, Plugin } from 'tiptap' import { Node } from 'tiptap'
import { Decoration, DecorationSet } from 'prosemirror-view'
import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands'
import { findBlockNodes } from 'prosemirror-utils'
import low from 'lowlight/lib/core' import low from 'lowlight/lib/core'
import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands'
function getDecorations(doc) { import HighlightPlugin from '../plugins/Highlight'
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)
}
export default class CodeBlockHighlight extends Node { export default class CodeBlockHighlight extends Node {
@ -74,16 +16,16 @@ export default class CodeBlockHighlight extends Node {
} }
} }
get name() {
return 'code_block'
}
get defaultOptions() { get defaultOptions() {
return { return {
languages: {}, languages: {},
} }
} }
get name() {
return 'code_block'
}
get schema() { get schema() {
return { return {
content: 'text*', content: 'text*',
@ -117,31 +59,7 @@ export default class CodeBlockHighlight extends Node {
get plugins() { get plugins() {
return [ return [
new Plugin({ HighlightPlugin({ name: this.name }),
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)
},
},
}),
] ]
} }

View File

@ -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)
},
},
})
}