Created
October 7, 2018 20:28
-
-
Save bradleyayers/1d2da8d375517ab82bf27471276cd6ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tslint:disable:no-invalid-this | |
import memoizeOne from "memoize-one"; | |
import { EditorState, Plugin, Transaction, PluginSpec } from "prosemirror-state"; | |
import { Schema } from "prosemirror-model"; | |
declare module "prosemirror-state" { | |
// tslint:disable-next-line:no-any | |
interface PluginSpec<S extends Schema = any> { | |
replaceTransaction?: | |
| ((tr: Transaction, oldState: EditorState, newState: EditorState) => undefined | null | false | Transaction) | |
| null; | |
} | |
} | |
/** | |
* Installs a monkey patch for `replaceTransaction`. This avoids forking | |
* prosemirror-state (which would be tricky for distribution), and serves as | |
* reference implementation for https://github.com/ProseMirror/rfcs/pull/10. | |
*/ | |
export const installReplaceTransactionMonkeyPatch = memoizeOne(() => { | |
// tslint:disable-next-line:no-any | |
interface PluginWithReplaceTransaction<S extends Schema = any> extends Plugin<S> { | |
spec: Pick<PluginSpec<S>, "replaceTransaction">; | |
} | |
interface EditorStateWithPrivate extends EditorState { | |
filterTransaction(tr: Transaction, ignore?: number): boolean; | |
applyInner(tr: Transaction): EditorState; | |
config: { | |
plugins: PluginWithReplaceTransaction[]; | |
}; | |
} | |
function replaceTransaction( | |
state: EditorState, | |
tr: Transaction, | |
ignorePlugin = -1 | |
): false | { tr: Transaction; state: EditorState } { | |
if (!(state as EditorStateWithPrivate).filterTransaction(tr, ignorePlugin)) { | |
return false; | |
} | |
let newState = (state as EditorStateWithPrivate).applyInner(tr); | |
const { plugins } = (state as EditorStateWithPrivate).config; | |
for (let i = 0; i < plugins.length; i++) { | |
if (i !== ignorePlugin) { | |
const plugin = plugins[i]; | |
if (plugin.spec.replaceTransaction != null) { | |
const replaceResult = plugin.spec.replaceTransaction.call(plugin, tr, state, newState); | |
if (replaceResult === false) { | |
// Equivalent effect of `filterTransaction` | |
return false; | |
} else if (replaceResult != null) { | |
tr = replaceResult; | |
newState = (state as EditorStateWithPrivate).applyInner(tr); | |
} | |
} | |
} | |
} | |
return { tr, state: newState }; | |
} | |
EditorState.prototype.applyTransaction = function(tr) { | |
const replaceResult = replaceTransaction(this, tr); | |
if (replaceResult === false) { | |
return { state: this, transactions: [] }; | |
} | |
// tslint:disable-next-line:no-any | |
const { plugins } = (this as any).config; | |
const trs = [replaceResult.tr]; | |
let newState = replaceResult.state; | |
let seen = null; | |
// This loop repeatedly gives plugins a chance to respond to | |
// transactions as new transactions are added, making sure to only | |
// pass the transactions the plugin did not see before. | |
for (;;) { | |
let haveNew = false; | |
for (let i = 0; i < plugins.length; i++) { | |
const plugin = plugins[i]; | |
if (plugin.spec.appendTransaction) { | |
const n = seen !== null ? seen[i].n : 0; | |
const oldState = seen !== null ? seen[i].state : this; | |
const trAppended = | |
n < trs.length && plugin.spec.appendTransaction.call(plugin, n > 0 ? trs.slice(n) : trs, oldState, newState); | |
if (trAppended) { | |
const replaceResult = replaceTransaction(newState, trAppended, i); | |
if (replaceResult !== false) { | |
replaceResult.tr.setMeta("appendedTransaction", tr); | |
if (seen === null) { | |
seen = []; | |
for (let j = 0; j < plugins.length; j++) { | |
seen.push(j < i ? { state: newState, n: trs.length } : { state: this, n: 0 }); | |
} | |
} | |
trs.push(replaceResult.tr); | |
newState = replaceResult.state; | |
haveNew = true; | |
} | |
} | |
if (seen !== null) seen[i] = { state: newState, n: trs.length }; | |
} | |
} | |
if (!haveNew) return { state: newState, transactions: trs }; | |
} | |
}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment