Skip to content

Commit

Permalink
Cache return values of computeDocDeco and viewDecorations
Browse files Browse the repository at this point in the history
  • Loading branch information
smoores-dev committed Oct 1, 2024
1 parent 1b19e7c commit ba08587
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 35 deletions.
36 changes: 18 additions & 18 deletions docs/assets/index-CSvukb3X.js → docs/assets/index-BNcakB_Z.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React-ProseMirror Demo</title>
<script type="module" crossorigin src="/react-prosemirror/assets/index-CSvukb3X.js"></script>
<script type="module" crossorigin src="/react-prosemirror/assets/index-BNcakB_Z.js"></script>
<link rel="stylesheet" crossorigin href="/react-prosemirror/assets/index-DAGU9WLy.css">
</head>
<body>
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
"@vitejs/plugin-react": "^4.3.1",
"@wdio/browser-runner": "^9.0.9",
"@wdio/cli": "^9.0.9",
"@wdio/dot-reporter": "^9.1.0",
"@wdio/mocha-framework": "^9.0.8",
"@wdio/spec-reporter": "^9.1.0",
"@wdio/types": "^9.0.8",
"@yarnpkg/sdks": "^3.0.0-rc.38",
"concurrently": "^7.6.0",
Expand Down
25 changes: 13 additions & 12 deletions src/components/ProseMirror.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { DecorationSet, NodeViewConstructor } from "prosemirror-view";
import {
Decoration,
DecorationSet,
NodeViewConstructor,
} from "prosemirror-view";
import React, {
ForwardRefExoticComponent,
ReactNode,
Expand Down Expand Up @@ -36,6 +40,8 @@ export type Props = Omit<UseEditorOptions, "nodeViews"> & {
};
};

const EMPTY_OUTER_DECOS: Decoration[] = [];

function ProseMirrorInner({
className,
children,
Expand All @@ -50,18 +56,13 @@ function ProseMirrorInner({
nodeViews: customNodeViews,
});

const innerDecos = useMemo(
() =>
editor.view
? viewDecorations(editor.view, editor.cursorWrapper)
: (DecorationSet.empty as unknown as DecorationSet),
[editor.cursorWrapper, editor.view]
);
const innerDecos = editor.view
? viewDecorations(editor.view, editor.cursorWrapper)
: (DecorationSet.empty as unknown as DecorationSet);

const outerDecos = useMemo(
() => (editor.view ? computeDocDeco(editor.view) : []),
[editor.view]
);
const outerDecos = editor.view
? computeDocDeco(editor.view)
: EMPTY_OUTER_DECOS;

const nodeViewContextValue = useMemo(
() => ({
Expand Down
36 changes: 35 additions & 1 deletion src/decorations/computeDocDeco.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import { Decoration, EditorView } from "prosemirror-view";

const DocDecorationsCache = new WeakMap<EditorView, [Decoration]>();

/**
* Produces the outer decorations for the doc node, based
* on the attributes editor prop.
*
* The return value of this function is memoized; if it is to
* return an equivalent value to the last time it was called for
* a given EditorView, it will return exactly that previous value.
*
* This makes it safe to call in a React render function, even
* if its result is used in a dependencies array for a hook.
*/
export function computeDocDeco(view: EditorView) {
const attrs = Object.create(null);
attrs.class = "ProseMirror";
Expand All @@ -22,5 +35,26 @@ export function computeDocDeco(view: EditorView) {
});
if (!attrs.translate) attrs.translate = "no";

return [Decoration.node(0, view.state.doc.content.size, attrs)];
const next: [Decoration] = [
Decoration.node(0, view.state.doc.content.size, attrs),
];

const previous = DocDecorationsCache.get(view);
if (!previous) {
DocDecorationsCache.set(view, next);
return next;
}

if (previous[0].to !== view.state.doc.content.size) {
DocDecorationsCache.set(view, next);
return next;
}

// @ts-expect-error Internal property (Decoration.type)
if (!previous[0].type.eq(next[0].type)) {
DocDecorationsCache.set(view, next);
return next;
}

return previous;
}
37 changes: 36 additions & 1 deletion src/decorations/viewDecorations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,19 @@ function insertAhead(array: Decoration[], i: number, deco: Decoration) {
array.splice(i, 0, deco);
}

const ViewDecorationsCache = new WeakMap<EditorView, DecorationSource>();

/**
* Produces the DecorationSource for the current state, based
* on the decorations editor prop.
*
* The return value of this function is memoized; if it is to
* return an equivalent value to the last time it was called for
* a given EditorView, it will return exactly that previous value.
*
* This makes it safe to call in a React render function, even
* if its result is used in a dependencies array for a hook.
*/
export function viewDecorations(
view: EditorView,
cursorWrapper: Decoration | null
Expand All @@ -196,5 +209,27 @@ export function viewDecorations(
DecorationSet.create(view.state.doc, [cursorWrapper])
);
}
return DecorationGroup.from(found);
const previous = ViewDecorationsCache.get(view);
if (!previous) {
const result = DecorationGroup.from(found);
ViewDecorationsCache.set(view, result);
return result;
}
let numPrevious = 0;
let areSetsEqual = true;
previous.forEachSet((set) => {
const next = found[numPrevious++];
if (next !== set) {
areSetsEqual = false;
}
});
if (numPrevious !== found.length) {
areSetsEqual = false;
}
if (!areSetsEqual) {
const result = DecorationGroup.from(found);
ViewDecorationsCache.set(view, result);
return result;
}
return previous;
}
2 changes: 1 addition & 1 deletion wdio.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const config: WebdriverIO.Config = {
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: https://webdriver.io/docs/dot-reporter
// reporters: ['dot'],
reporters: ["spec"],

// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
Expand Down
114 changes: 113 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1944,7 +1944,9 @@ __metadata:
"@vitejs/plugin-react": ^4.3.1
"@wdio/browser-runner": ^9.0.9
"@wdio/cli": ^9.0.9
"@wdio/dot-reporter": ^9.1.0
"@wdio/mocha-framework": ^9.0.8
"@wdio/spec-reporter": ^9.1.0
"@wdio/types": ^9.0.8
"@yarnpkg/sdks": ^3.0.0-rc.38
classnames: ^2.3.2
Expand Down Expand Up @@ -3251,6 +3253,17 @@ __metadata:
languageName: node
linkType: hard

"@wdio/dot-reporter@npm:^9.1.0":
version: 9.1.0
resolution: "@wdio/dot-reporter@npm:9.1.0"
dependencies:
"@wdio/reporter": 9.1.0
"@wdio/types": 9.1.0
chalk: ^5.0.1
checksum: 7bea6e022b8de1fd4509f4bd34d0460923628bed68928fd383f4bcb7d2005e4476cafa186ca609756035f338ba83ffbc48602f3173dd11c9200e2d0698a1451f
languageName: node
linkType: hard

"@wdio/globals@npm:9.0.9":
version: 9.0.9
resolution: "@wdio/globals@npm:9.0.9"
Expand Down Expand Up @@ -3294,6 +3307,18 @@ __metadata:
languageName: node
linkType: hard

"@wdio/logger@npm:9.1.0":
version: 9.1.0
resolution: "@wdio/logger@npm:9.1.0"
dependencies:
chalk: ^5.1.2
loglevel: ^1.6.0
loglevel-plugin-prefix: ^0.8.4
strip-ansi: ^7.1.0
checksum: 52c3574c644fbc623565e913d0cb943bd45284148f4c93b378b3dc835a8d9e263680310f190ab240e76f60f00d6254e1063cc3a7db38ec7cfcf6dc7797a9e434
languageName: node
linkType: hard

"@wdio/logger@npm:^8.38.0":
version: 8.38.0
resolution: "@wdio/logger@npm:8.38.0"
Expand Down Expand Up @@ -3336,6 +3361,19 @@ __metadata:
languageName: node
linkType: hard

"@wdio/reporter@npm:9.1.0":
version: 9.1.0
resolution: "@wdio/reporter@npm:9.1.0"
dependencies:
"@types/node": ^20.1.0
"@wdio/logger": 9.1.0
"@wdio/types": 9.1.0
diff: ^7.0.0
object-inspect: ^1.12.0
checksum: 6afb1984c8f437e5c88e213fc1c857c29c873599f9375c4e859c86fe97a85cd95050ee363db039a5b55e1598f6ab13a3577db4775698215ba1b748fe8fa236c6
languageName: node
linkType: hard

"@wdio/runner@npm:9.0.9":
version: 9.0.9
resolution: "@wdio/runner@npm:9.0.9"
Expand All @@ -3355,6 +3393,19 @@ __metadata:
languageName: node
linkType: hard

"@wdio/spec-reporter@npm:^9.1.0":
version: 9.1.0
resolution: "@wdio/spec-reporter@npm:9.1.0"
dependencies:
"@wdio/reporter": 9.1.0
"@wdio/types": 9.1.0
chalk: ^5.1.2
easy-table: ^1.2.0
pretty-ms: ^9.0.0
checksum: 9c74032d8ec9259e89e057dc310f71f8af84d50ecada98e29fb8ccea4643c1a1a746c4873d5365475e79749bbe4ccbe443f9d24bf31ae8f22dfdabc1f0cdcc72
languageName: node
linkType: hard

"@wdio/types@npm:9.0.8, @wdio/types@npm:^9.0.8":
version: 9.0.8
resolution: "@wdio/types@npm:9.0.8"
Expand All @@ -3364,6 +3415,15 @@ __metadata:
languageName: node
linkType: hard

"@wdio/types@npm:9.1.0":
version: 9.1.0
resolution: "@wdio/types@npm:9.1.0"
dependencies:
"@types/node": ^20.1.0
checksum: 14632e3b4dd041d5ccacace3e333ddc1854853ddae853dc6a846601fb7da1d31003f3a1b66afda45e943af0b42537f80e30375def810e946318343f15769f3f4
languageName: node
linkType: hard

"@wdio/utils@npm:9.0.8":
version: 9.0.8
resolution: "@wdio/utils@npm:9.0.8"
Expand Down Expand Up @@ -4368,7 +4428,7 @@ __metadata:
languageName: node
linkType: hard

"chalk@npm:^5.1.2, chalk@npm:^5.2.0":
"chalk@npm:^5.0.1, chalk@npm:^5.1.2, chalk@npm:^5.2.0":
version: 5.3.0
resolution: "chalk@npm:5.3.0"
checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80
Expand Down Expand Up @@ -4570,6 +4630,13 @@ __metadata:
languageName: node
linkType: hard

"clone@npm:^1.0.2":
version: 1.0.4
resolution: "clone@npm:1.0.4"
checksum: d06418b7335897209e77bdd430d04f882189582e67bd1f75a04565f3f07f5b3f119a9d670c943b6697d0afb100f03b866b3b8a1f91d4d02d72c4ecf2bb64b5dd
languageName: node
linkType: hard

"co@npm:^4.6.0":
version: 4.6.0
resolution: "co@npm:4.6.0"
Expand Down Expand Up @@ -5046,6 +5113,15 @@ __metadata:
languageName: node
linkType: hard

"defaults@npm:^1.0.3":
version: 1.0.4
resolution: "defaults@npm:1.0.4"
dependencies:
clone: ^1.0.2
checksum: 3a88b7a587fc076b84e60affad8b85245c01f60f38fc1d259e7ac1d89eb9ce6abb19e27215de46b98568dd5bc48471730b327637e6f20b0f1bc85cf00440c80a
languageName: node
linkType: hard

"defer-to-connect@npm:^2.0.0":
version: 2.0.1
resolution: "defer-to-connect@npm:2.0.1"
Expand Down Expand Up @@ -5172,6 +5248,13 @@ __metadata:
languageName: node
linkType: hard

"diff@npm:^7.0.0":
version: 7.0.0
resolution: "diff@npm:7.0.0"
checksum: 5db0d339476b18dfbc8a08a7504fbcc74789eec626c8d20cf2cdd1871f1448962888128f4447c8f50a1e41a80decfe5e8489c375843b8cf1d42b7c2b611da4e1
languageName: node
linkType: hard

"dir-glob@npm:^3.0.1":
version: 3.0.1
resolution: "dir-glob@npm:3.0.1"
Expand Down Expand Up @@ -5267,6 +5350,19 @@ __metadata:
languageName: node
linkType: hard

"easy-table@npm:^1.2.0":
version: 1.2.0
resolution: "easy-table@npm:1.2.0"
dependencies:
ansi-regex: ^5.0.1
wcwidth: ^1.0.1
dependenciesMeta:
wcwidth:
optional: true
checksum: 66961b19751a68d2d30ce9b74ef750c374cc3112bbcac3d1ed5a939e43c035ecf6b1954098df2d5b05f1e853ab2b67de893794390dcbf0abe1f157fddeb52174
languageName: node
linkType: hard

"edge-paths@npm:^3.0.5":
version: 3.0.5
resolution: "edge-paths@npm:3.0.5"
Expand Down Expand Up @@ -10109,6 +10205,13 @@ __metadata:
languageName: node
linkType: hard

"object-inspect@npm:^1.12.0":
version: 1.13.2
resolution: "object-inspect@npm:1.13.2"
checksum: 9f850b3c045db60e0e97746e809ee4090d6ce62195af17dd1e9438ac761394a7d8ec4f7906559aea5424eaf61e35d3e53feded2ccd5f62fcc7d9670d3c8eb353
languageName: node
linkType: hard

"object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0":
version: 1.12.3
resolution: "object-inspect@npm:1.12.3"
Expand Down Expand Up @@ -13136,6 +13239,15 @@ __metadata:
languageName: node
linkType: hard

"wcwidth@npm:^1.0.1":
version: 1.0.1
resolution: "wcwidth@npm:1.0.1"
dependencies:
defaults: ^1.0.3
checksum: 814e9d1ddcc9798f7377ffa448a5a3892232b9275ebb30a41b529607691c0491de47cba426e917a4d08ded3ee7e9ba2f3fe32e62ee3cd9c7d3bafb7754bd553c
languageName: node
linkType: hard

"web-streams-polyfill@npm:^3.0.3":
version: 3.3.3
resolution: "web-streams-polyfill@npm:3.3.3"
Expand Down

0 comments on commit ba08587

Please sign in to comment.