Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/editors/slate/search/replace-matches.ts
1700 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
import { Editor, Element, Range, Transforms } from "slate";
7
import { ReactEditor } from "../slate-react";
8
import { selectNextMatch } from "./find-matches";
9
import { nextMatch } from "./search-control";
10
import { alert_message } from "@cocalc/frontend/alerts";
11
12
function lowestNode(editor: Editor) {
13
for (const [node] of Editor.nodes(editor, { mode: "lowest" })) {
14
return node;
15
}
16
}
17
18
export function replaceOne(
19
editor: ReactEditor,
20
decorate,
21
replace: string,
22
noScroll: boolean = false
23
): boolean {
24
// collapse selection to the starting edge
25
if (editor.selection) {
26
const edges = Range.edges(editor.selection);
27
Transforms.setSelection(editor, { focus: edges[0], anchor: edges[0] });
28
}
29
// find next match
30
if (selectNextMatch(editor, decorate)) {
31
const node = lowestNode(editor);
32
if (node != null) {
33
if (!(Element.isElement(node) && Editor.isVoid(editor, node))) {
34
Transforms.insertText(editor, replace);
35
// Important -- note that insertText puts the focus **after**
36
// the inserted text. It's important to keep this in mind so that
37
// the result of replace=search string isn't to make something
38
// that we immediately replace again thus blowing up the document!
39
// Make sure to preserve this invariant when implementing this for
40
// voids.
41
} else {
42
// TODO: need to handle void elements differently via a plugin mechanism.
43
alert_message({
44
type: "info",
45
message:
46
"Replacing nodes of this type not yet implemented. Please use source view.",
47
});
48
// At least move to next one no matter what.
49
if (noScroll) {
50
selectNextMatch(editor, decorate);
51
}
52
}
53
}
54
if (!noScroll) {
55
// Now select and focus whatever is next after what we just
56
// replaced, in preparation for doing the *next* replace.
57
nextMatch(editor, decorate);
58
}
59
return true;
60
}
61
return false;
62
}
63
64
export function replaceAll(
65
editor: ReactEditor,
66
decorate,
67
replace: string
68
): void {
69
// Keep replacing until nothing left to replace. However, we also keep
70
// of focus points after doing selection so that if for some crazy reason
71
// this would loop around forever -- e.g., a replace doesn't work properly,
72
// or maybe the goal of the replace is to add a copy of what is being searched
73
// for into the document -- in that case, we immediately bail.
74
const pastSelections = new Set<string>([]);
75
while (replaceOne(editor, decorate, replace, true)) {
76
const cur = JSON.stringify(editor.selection?.focus ?? {});
77
if (pastSelections.has(cur)) return;
78
pastSelections.add(cur);
79
}
80
}
81
82