Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/keyboard/backspace.ts
1697 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
What happens when you hit the backspace/delete key.
8
9
- deleting (certain?) void elements. See
10
https://github.com/ianstormtaylor/slate/issues/3875
11
for discussion of why we must implement this ourselves.
12
*/
13
14
import { Editor, Path, Point, Range, Text, Transforms } from "slate";
15
import { register } from "./register";
16
import { getNodeAt } from "../slate-util";
17
18
function backspaceKey({ editor }) {
19
if (editor.selection == null || !Range.isCollapsed(editor.selection)) {
20
selectionHack(editor);
21
return false;
22
}
23
24
// In slatejs you can't delete various block elements at the beginning of the
25
// document. This is yet another **BUG IN SLATE**, which we workaround by
26
// inserting an empty node at the beginning of the document. This does not
27
// seem to be reported upstream, and I'm not even bothering since there's
28
// so many bugs like this we have to workaround. Morever, if this bug is
29
// fixed upstream, it breaks our workaround! Sigh.
30
if (isAtStart(editor.selection.focus)) {
31
editor.apply({
32
type: "insert_node",
33
path: [0],
34
node: { type: "paragraph", children: [{ text: "" }] },
35
});
36
Transforms.delete(editor, {
37
reverse: true,
38
});
39
}
40
41
// This seems to work perfectly in all cases, including working around the
42
// void delete bug in Slate:
43
// https://github.com/ianstormtaylor/slate/issues/3875
44
// IMPORTANT: this editor.deleteBackward() is implemented in
45
// format/delete-backward.ts and is quite complicated!
46
editor.deleteBackward();
47
return true;
48
}
49
50
register({ key: "Backspace" }, backspaceKey);
51
52
function deleteKey({ editor }) {
53
if (editor.selection == null) return true;
54
if (!Range.isCollapsed(editor.selection)) {
55
selectionHack(editor);
56
// deleteForward does nothing for non collapsed
57
Transforms.delete(editor);
58
return true;
59
}
60
editor.deleteForward();
61
return true;
62
}
63
64
register({ key: "Delete" }, deleteKey);
65
66
function isAtStart(loc: Point): boolean {
67
for (const n of loc.path) {
68
if (n != 0) return false;
69
}
70
return loc.offset == 0;
71
}
72
73
// This is a hack to workaround this bug:
74
// https://github.com/ianstormtaylor/slate/issues/4121
75
// which is in the core of slate. Call this before
76
// deleting the selection to ensure that the wrong thing
77
// isn't deleted...
78
function selectionHack(editor: Editor): void {
79
const { selection } = editor;
80
if (selection == null || Range.isCollapsed(selection)) return;
81
const edges = Range.edges(selection);
82
const node = getNodeAt(editor, edges[1].path);
83
if (!Text.isText(node)) return;
84
if (node.text.length != edges[1].offset) return;
85
86
// OK, so at this point, we're in exactly the situation
87
// of issue 4121. In particular, the
88
// selection ends at the edge of a text node.
89
// Our hack is to move the cursor to the beginning of
90
// the *next* node, but make the offset 0,
91
// so that when we delete nothing is removed
92
// from there.
93
const path = Path.next(edges[1].path);
94
const nextNode = getNodeAt(editor, path);
95
if (Text.isText(nextNode)) {
96
// NOTE: it doesn't matter if we reverse the range here, since
97
// we're about to delete this selection.
98
const newSelection = {
99
anchor: edges[0],
100
focus: { path, offset: 0 },
101
};
102
Transforms.setSelection(editor, newSelection);
103
}
104
}
105
106