Skip to content

Commit

Permalink
fix: add way to cancel inputrules and pasterules (#2368)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Kühn <[email protected]>
  • Loading branch information
philippkuehn and Philipp Kühn authored Jan 10, 2022
1 parent 5144a6c commit 669efd0
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 86 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/ExtensionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export class ExtensionManager {
editor,
rules: inputRules,
}),
pasteRulesPlugin({
...pasteRulesPlugin({
editor,
rules: pasteRules,
}),
Expand Down
17 changes: 11 additions & 6 deletions packages/core/src/InputRule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { EditorState, Plugin, TextSelection } from 'prosemirror-state'
import {
EditorState,
Plugin,
TextSelection,
Transaction,
} from 'prosemirror-state'
import { Editor } from './Editor'
import { CommandManager } from './CommandManager'
import { createChainableState } from './helpers/createChainableState'
Expand Down Expand Up @@ -33,7 +38,7 @@ export class InputRule {
commands: SingleCommands,
chain: () => ChainedCommands,
can: () => CanCommands,
}) => void
}) => Transaction | null

constructor(config: {
find: InputRuleFinder,
Expand All @@ -44,7 +49,7 @@ export class InputRule {
commands: SingleCommands,
chain: () => ChainedCommands,
can: () => CanCommands,
}) => void,
}) => Transaction | null,
}) {
this.find = config.find
this.handler = config.handler
Expand Down Expand Up @@ -87,7 +92,7 @@ function run(config: {
text: string,
rules: InputRule[],
plugin: Plugin,
}): any {
}): boolean {
const {
editor,
from,
Expand Down Expand Up @@ -148,7 +153,7 @@ function run(config: {
state,
})

rule.handler({
const handler = rule.handler({
state,
range,
match,
Expand All @@ -158,7 +163,7 @@ function run(config: {
})

// stop if there are no changes
if (!tr.steps.length) {
if (!handler || !tr.steps.length) {
return
}

Expand Down
148 changes: 75 additions & 73 deletions packages/core/src/PasteRule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EditorState, Plugin } from 'prosemirror-state'
import { EditorState, Plugin, Transaction } from 'prosemirror-state'
import { Editor } from './Editor'
import { CommandManager } from './CommandManager'
import { createChainableState } from './helpers/createChainableState'
Expand Down Expand Up @@ -34,7 +34,7 @@ export class PasteRule {
commands: SingleCommands,
chain: () => ChainedCommands,
can: () => CanCommands,
}) => void
}) => Transaction | null

constructor(config: {
find: PasteRuleFinder,
Expand All @@ -45,7 +45,7 @@ export class PasteRule {
commands: SingleCommands,
chain: () => ChainedCommands,
can: () => CanCommands,
}) => void,
}) => Transaction | null,
}) {
this.find = config.find
this.handler = config.handler
Expand Down Expand Up @@ -88,22 +88,23 @@ function run(config: {
state: EditorState,
from: number,
to: number,
rules: PasteRule[],
plugin: Plugin,
}): any {
rule: PasteRule,
}): boolean {
const {
editor,
state,
from,
to,
rules,
rule,
} = config

const { commands, chain, can } = new CommandManager({
editor,
state,
})

const handlers: (Transaction | null)[] = []

state.doc.nodesBetween(from, to, (node, pos) => {
if (!node.isTextblock || node.type.spec.code) {
return
Expand All @@ -118,98 +119,99 @@ function run(config: {
'\ufffc',
)

rules.forEach(rule => {
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)

matches.forEach(match => {
if (match.index === undefined) {
return
}
matches.forEach(match => {
if (match.index === undefined) {
return
}

const start = resolvedFrom + match.index + 1
const end = start + match[0].length
const range = {
from: state.tr.mapping.map(start),
to: state.tr.mapping.map(end),
}
const start = resolvedFrom + match.index + 1
const end = start + match[0].length
const range = {
from: state.tr.mapping.map(start),
to: state.tr.mapping.map(end),
}

rule.handler({
state,
range,
match,
commands,
chain,
can,
})
const handler = rule.handler({
state,
range,
match,
commands,
chain,
can,
})

handlers.push(handler)
})
})

const success = handlers.every(handler => handler !== null)

return success
}

/**
* Create an paste rules plugin. When enabled, it will cause pasted
* text that matches any of the given rules to trigger the rule’s
* action.
*/
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin {
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin[] {
const { editor, rules } = props
let isProseMirrorHTML = false

const plugin = new Plugin({
props: {
handlePaste: (view, event) => {
const html = event.clipboardData?.getData('text/html')
const plugins = rules.map(rule => {
return new Plugin({
props: {
handlePaste: (view, event) => {
const html = event.clipboardData?.getData('text/html')

isProseMirrorHTML = !!html?.includes('data-pm-slice')
isProseMirrorHTML = !!html?.includes('data-pm-slice')

return false
return false
},
},
},
appendTransaction: (transactions, oldState, state) => {
const transaction = transactions[0]
appendTransaction: (transactions, oldState, state) => {
const transaction = transactions[0]

// stop if there is not a paste event
if (!transaction.getMeta('paste') || isProseMirrorHTML) {
return
}

// stop if there is no changed range
const { doc, before } = transaction
const from = before.content.findDiffStart(doc.content)
const to = before.content.findDiffEnd(doc.content)
// stop if there is not a paste event
if (!transaction.getMeta('paste') || isProseMirrorHTML) {
return
}

if (!isNumber(from) || !to || from === to.b) {
return
}
// stop if there is no changed range
const from = oldState.doc.content.findDiffStart(state.doc.content)
const to = oldState.doc.content.findDiffEnd(state.doc.content)

// build a chainable state
// so we can use a single transaction for all paste rules
const tr = state.tr
const chainableState = createChainableState({
state,
transaction: tr,
})
if (!isNumber(from) || !to || from === to.b) {
return
}

run({
editor,
state: chainableState,
from: Math.max(from - 1, 0),
to: to.b,
rules,
plugin,
})
// build a chainable state
// so we can use a single transaction for all paste rules
const tr = state.tr
const chainableState = createChainableState({
state,
transaction: tr,
})

// stop if there are no changes
if (!tr.steps.length) {
return
}
const handler = run({
editor,
state: chainableState,
from: Math.max(from - 1, 0),
to: to.b,
rule,
})

return tr
},
// stop if there are no changes
if (!handler || !tr.steps.length) {
return
}

// @ts-ignore
isPasteRules: true,
return tr
},
})
})

return plugin
return plugins
}
4 changes: 3 additions & 1 deletion packages/core/src/inputRules/markInputRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function markInputRule(config: {
const attributes = callOrReturn(config.getAttributes, undefined, match)

if (attributes === false || attributes === null) {
return
return null
}

const { tr } = state
Expand Down Expand Up @@ -64,6 +64,8 @@ export function markInputRule(config: {

tr.removeStoredMark(config.type)
}

return tr
},
})
}
2 changes: 2 additions & 0 deletions packages/core/src/inputRules/nodeInputRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export function nodeInputRule(config: {
} else if (match[0]) {
tr.replaceWith(start, end, config.type.create(attributes))
}

return tr
},
})
}
6 changes: 5 additions & 1 deletion packages/core/src/inputRules/textInputRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export function textInputRule(config: {
}
}

state.tr.insertText(insert, start, end)
const { tr } = state

tr.insertText(insert, start, end)

return tr
},
})
}
7 changes: 5 additions & 2 deletions packages/core/src/inputRules/textblockTypeInputRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ export function textblockTypeInputRule(config: {
return null
}

state.tr
.delete(range.from, range.to)
const { tr } = state

tr.delete(range.from, range.to)
.setBlockType(range.from, range.from, config.type, attributes)

return tr
},
})
}
2 changes: 2 additions & 0 deletions packages/core/src/inputRules/wrappingInputRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export function wrappingInputRule(config: {
) {
tr.join(range.from - 1)
}

return tr
},
})
}
4 changes: 3 additions & 1 deletion packages/core/src/pasteRules/markPasteRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function markPasteRule(config: {
const attributes = callOrReturn(config.getAttributes, undefined, match)

if (attributes === false || attributes === null) {
return
return null
}

const { tr } = state
Expand Down Expand Up @@ -64,6 +64,8 @@ export function markPasteRule(config: {

tr.removeStoredMark(config.type)
}

return tr
},
})
}
6 changes: 5 additions & 1 deletion packages/core/src/pasteRules/textPasteRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export function textPasteRule(config: {
}
}

state.tr.insertText(insert, start, end)
const { tr } = state

tr.insertText(insert, start, end)

return tr
},
})
}

0 comments on commit 669efd0

Please sign in to comment.