Better multi-window updates

This commit is contained in:
Gregory Schier
2023-03-29 21:53:20 -07:00
parent bc40e22008
commit dab2df7e79
16 changed files with 180 additions and 136 deletions

View File

@@ -1,6 +1,7 @@
import { defaultKeymap } from '@codemirror/commands';
import type { Extension } from '@codemirror/state';
import { Compartment, EditorState } from '@codemirror/state';
import { Compartment, EditorState, Transaction } from '@codemirror/state';
import type { ViewUpdate } from '@codemirror/view';
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
import classnames from 'classnames';
import { EditorView } from 'codemirror';
@@ -19,6 +20,7 @@ export { formatSdl } from 'format-graphql';
export interface EditorProps {
id?: string;
forceUpdateKey?: string;
readOnly?: boolean;
type?: 'text' | 'password';
className?: string;
@@ -42,6 +44,7 @@ export function Editor({
type = 'text',
heightMode,
contentType,
forceUpdateKey,
autoFocus,
placeholder,
useTemplating,
@@ -87,6 +90,15 @@ export function Editor({
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
}, [contentType, autocomplete]);
useEffect(() => {
if (cm.current === null) return;
const { view, languageCompartment } = cm.current;
const newDoc = defaultValue;
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: newDoc ?? '' } });
const ext = getLanguageExtension({ contentType, useTemplating, autocomplete });
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
}, [forceUpdateKey]);
// Initialize the editor when ref mounts
useEffect(() => {
if (wrapperRef.current === null || cm.current !== null) return;
@@ -218,13 +230,27 @@ function getExtensions({
// Handle onChange
EditorView.updateListener.of((update) => {
if (onChange && update.docChanged) {
if (onChange && update.docChanged && isViewUpdateFromUserInput(update)) {
onChange.current?.(update.state.doc.toString());
}
}),
];
}
function isViewUpdateFromUserInput(viewUpdate: ViewUpdate) {
// Make sure document has changed, ensuring user events like selections don't count.
if (viewUpdate.docChanged) {
// Check transactions for any that are direct user input, not changes from Y.js or another extension.
for (const transaction of viewUpdate.transactions) {
// Not using Transaction.isUserEvent because that only checks for a specific User event type ( "input", "delete", etc.). Checking the annotation directly allows for any type of user event.
const userEventType = transaction.annotation(Transaction.userEvent);
if (userEventType) return userEventType;
}
}
return false;
}
const syncGutterBg = ({
parent,
className = '',