Desktop: Fixes #13745: Prevent cut events from being merged with other actions in the undo history (#13791)

This commit is contained in:
Henry Heino
2025-11-29 09:57:46 +00:00
committed by GitHub
parent dba3a3f68f
commit 31cb404854
7 changed files with 136 additions and 31 deletions
@@ -16,6 +16,7 @@ import jumpToHash from './editorCommands/jumpToHash';
import { resetImageResourceEffect } from './extensions/rendering/renderBlockImages';
import Logger from '@joplin/utils/Logger';
import { searchChangeSourceEffect } from './extensions/searchExtension';
import cutOrCopyText, { ClipboardAction } from './editorCommands/cutOrCopyText';
const logger = Logger.create('CodeMirrorControl');
@@ -260,6 +261,14 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
this._callbacks.onRemove();
}
public cutText(writeClipboard: (text: string)=> void) {
return cutOrCopyText(writeClipboard, ClipboardAction.Cut)(this.editor);
}
public copyText(writeClipboard: (text: string)=> void) {
return cutOrCopyText(writeClipboard, ClipboardAction.Copy)(this.editor);
}
//
// CodeMirror-specific methods
//
@@ -0,0 +1,47 @@
import { isolateHistory } from '@codemirror/commands';
import { Command } from '@codemirror/view';
export enum ClipboardAction {
Cut = 'cut',
Copy = 'copy',
}
type OnWriteClipboard = (text: string)=> void;
const cutOrCopyText = (onWriteClipboard: OnWriteClipboard, action: ClipboardAction): Command => (view) => {
const state = view.state;
const selections = state.selection.ranges.map(range => (
state.sliceDoc(range.from, range.to)
));
const nonEmptySelections = selections.filter(s => !!s);
const cutTransactions = [];
if (nonEmptySelections.length > 0) {
onWriteClipboard(nonEmptySelections.join('\n'));
cutTransactions.push(state.replaceSelection(''));
} else {
const selectedLine = state.doc.lineAt(state.selection.main.anchor);
onWriteClipboard(`${selectedLine.text}\n`);
cutTransactions.push({
changes: [{
from: selectedLine.from,
to: Math.min(selectedLine.to + 1, state.doc.length),
insert: '',
}],
});
}
cutTransactions.push({
annotations: [isolateHistory.of('full')],
userEvent: 'delete.cut',
});
if (action === ClipboardAction.Cut) {
view.dispatch(...cutTransactions);
}
return true;
};
export default cutOrCopyText;