Skip to content

Commit d73026e

Browse files
authored
Change how slate-history handles selection undo (#4717)
* Change how slate-history handles selection undo * fix test * fix lint * cleanup and simplify * Fix redo by applying undo beforeSelection before applying the redo * remove unused shouldClear function * fix lint * add changeset
1 parent 4e52e50 commit d73026e

File tree

4 files changed

+33
-53
lines changed

4 files changed

+33
-53
lines changed

.changeset/cuddly-kiwis-work.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'slate-history': minor
3+
---
4+
5+
Changes how selections are stored in the history resulting in more consistent results

packages/slate-history/src/history.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import { isPlainObject } from 'is-plain-object'
2-
import { Operation } from 'slate'
2+
import { Operation, Range } from 'slate'
3+
4+
interface Batch {
5+
operations: Operation[]
6+
selectionBefore: Range | null
7+
}
38

49
/**
510
* `History` objects hold all of the operations that are applied to a value, so
611
* they can be undone or redone as necessary.
712
*/
813

914
export interface History {
10-
redos: Operation[][]
11-
undos: Operation[][]
15+
redos: Batch[]
16+
undos: Batch[]
1217
}
1318

1419
// eslint-disable-next-line no-redeclare

packages/slate-history/src/with-history.ts

+19-49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Editor, Operation, Path } from 'slate'
1+
import { Editor, Operation, Path, Range, Transforms } from 'slate'
22

33
import { HistoryEditor } from './history-editor'
44

@@ -24,9 +24,13 @@ export const withHistory = <T extends Editor>(editor: T) => {
2424
if (redos.length > 0) {
2525
const batch = redos[redos.length - 1]
2626

27+
if (batch.selectionBefore) {
28+
Transforms.setSelection(e, batch.selectionBefore)
29+
}
30+
2731
HistoryEditor.withoutSaving(e, () => {
2832
Editor.withoutNormalizing(e, () => {
29-
for (const op of batch) {
33+
for (const op of batch.operations) {
3034
e.apply(op)
3135
}
3236
})
@@ -46,11 +50,14 @@ export const withHistory = <T extends Editor>(editor: T) => {
4650

4751
HistoryEditor.withoutSaving(e, () => {
4852
Editor.withoutNormalizing(e, () => {
49-
const inverseOps = batch.map(Operation.inverse).reverse()
53+
const inverseOps = batch.operations.map(Operation.inverse).reverse()
5054

5155
for (const op of inverseOps) {
5256
e.apply(op)
5357
}
58+
if (batch.selectionBefore) {
59+
Transforms.setSelection(e, batch.selectionBefore)
60+
}
5461
})
5562
})
5663

@@ -63,8 +70,8 @@ export const withHistory = <T extends Editor>(editor: T) => {
6370
const { operations, history } = e
6471
const { undos } = history
6572
const lastBatch = undos[undos.length - 1]
66-
const lastOp = lastBatch && lastBatch[lastBatch.length - 1]
67-
const overwrite = shouldOverwrite(op, lastOp)
73+
const lastOp =
74+
lastBatch && lastBatch.operations[lastBatch.operations.length - 1]
6875
let save = HistoryEditor.isSaving(e)
6976
let merge = HistoryEditor.isMerging(e)
7077

@@ -79,28 +86,25 @@ export const withHistory = <T extends Editor>(editor: T) => {
7986
} else if (operations.length !== 0) {
8087
merge = true
8188
} else {
82-
merge = shouldMerge(op, lastOp) || overwrite
89+
merge = shouldMerge(op, lastOp)
8390
}
8491
}
8592

8693
if (lastBatch && merge) {
87-
if (overwrite) {
88-
lastBatch.pop()
89-
}
90-
91-
lastBatch.push(op)
94+
lastBatch.operations.push(op)
9295
} else {
93-
const batch = [op]
96+
const batch = {
97+
operations: [op],
98+
selectionBefore: e.selection,
99+
}
94100
undos.push(batch)
95101
}
96102

97103
while (undos.length > 100) {
98104
undos.shift()
99105
}
100106

101-
if (shouldClear(op)) {
102-
history.redos = []
103-
}
107+
history.redos = []
104108
}
105109

106110
apply(op)
@@ -114,10 +118,6 @@ export const withHistory = <T extends Editor>(editor: T) => {
114118
*/
115119

116120
const shouldMerge = (op: Operation, prev: Operation | undefined): boolean => {
117-
if (op.type === 'set_selection') {
118-
return true
119-
}
120-
121121
if (
122122
prev &&
123123
op.type === 'insert_text' &&
@@ -146,36 +146,6 @@ const shouldMerge = (op: Operation, prev: Operation | undefined): boolean => {
146146
*/
147147

148148
const shouldSave = (op: Operation, prev: Operation | undefined): boolean => {
149-
if (
150-
op.type === 'set_selection' &&
151-
(op.properties == null || op.newProperties == null)
152-
) {
153-
return false
154-
}
155-
156-
return true
157-
}
158-
159-
/**
160-
* Check whether an operation should overwrite the previous one.
161-
*/
162-
163-
const shouldOverwrite = (
164-
op: Operation,
165-
prev: Operation | undefined
166-
): boolean => {
167-
if (prev && op.type === 'set_selection' && prev.type === 'set_selection') {
168-
return true
169-
}
170-
171-
return false
172-
}
173-
174-
/**
175-
* Check whether an operation should clear the redos stack.
176-
*/
177-
178-
const shouldClear = (op: Operation): boolean => {
179149
if (op.type === 'set_selection') {
180150
return false
181151
}

packages/slate-history/test/undo/cursor/keep_after_focus_and_remove_text_undo.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ export const output = {
4444
],
4545
selection: {
4646
anchor: { path: [0, 0], offset: 5 },
47-
focus: { path: [0, 0], offset: 5 },
47+
focus: { path: [0, 0], offset: 0 },
4848
},
4949
}

0 commit comments

Comments
 (0)