@adecore/editor 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attribution.d.ts +9 -8
- package/dist/attribution.js +44 -9
- package/dist/bidi.d.ts +44 -0
- package/dist/bidi.js +26 -0
- package/dist/column-selection.js +2 -1
- package/dist/controller.d.ts +6 -1
- package/dist/controller.js +93 -3
- package/dist/editor.css +81 -1
- package/dist/engine.js +184 -41
- package/dist/fake.d.ts +43 -5
- package/dist/fake.js +117 -13
- package/dist/find-search.d.ts +26 -0
- package/dist/find-search.js +130 -0
- package/dist/find.d.ts +50 -2
- package/dist/find.js +575 -25
- package/dist/geometry-stops.d.ts +6 -0
- package/dist/geometry-stops.js +47 -0
- package/dist/grammar-preparation.d.ts +29 -0
- package/dist/grammar-preparation.js +147 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/keymap-table.d.ts +10 -1
- package/dist/keymap-table.js +37 -1
- package/dist/keymap.d.ts +1 -0
- package/dist/keymap.js +4 -1
- package/dist/layout-changes.d.ts +6 -0
- package/dist/layout-changes.js +27 -0
- package/dist/layout.d.ts +45 -2
- package/dist/layout.js +202 -58
- package/dist/offsets.d.ts +1 -0
- package/dist/offsets.js +23 -0
- package/dist/outline.js +5 -4
- package/dist/paint.d.ts +11 -3
- package/dist/paint.js +180 -34
- package/dist/search-client.d.ts +27 -0
- package/dist/search-client.js +210 -0
- package/dist/search-protocol.d.ts +103 -0
- package/dist/search-protocol.js +3 -0
- package/dist/search-worker.d.ts +11 -0
- package/dist/search-worker.js +137 -0
- package/dist/semantic.d.ts +2 -1
- package/dist/semantic.js +6 -1
- package/dist/shiki-document.d.ts +8 -0
- package/dist/shiki-document.js +93 -0
- package/dist/shiki-worker.d.ts +65 -0
- package/dist/shiki-worker.js +265 -0
- package/dist/shiki.d.ts +1 -0
- package/dist/shiki.js +96 -33
- package/dist/testing.d.ts +1 -0
- package/dist/tokens.d.ts +2 -0
- package/dist/tokens.js +37 -7
- package/dist/touch.d.ts +27 -0
- package/dist/touch.js +100 -0
- package/dist/types.d.ts +84 -2
- package/dist/uniform-geometry.d.ts +7 -0
- package/dist/uniform-geometry.js +49 -0
- package/dist/view.d.ts +68 -17
- package/dist/view.js +424 -97
- package/dist/worker-client.d.ts +37 -0
- package/dist/worker-client.js +195 -0
- package/package.json +17 -2
- package/src/attribution.ts +50 -14
- package/src/bidi.ts +32 -0
- package/src/column-selection.ts +2 -1
- package/src/controller.ts +97 -3
- package/src/editor.css +81 -1
- package/src/engine.ts +216 -42
- package/src/fake.ts +152 -14
- package/src/find-search.ts +165 -0
- package/src/find.ts +622 -21
- package/src/geometry-stops.ts +51 -0
- package/src/grammar-preparation.ts +158 -0
- package/src/index.ts +10 -0
- package/src/keymap-table.ts +44 -2
- package/src/keymap.ts +5 -1
- package/src/layout-changes.ts +33 -0
- package/src/layout.ts +262 -63
- package/src/offsets.ts +24 -0
- package/src/outline.ts +5 -4
- package/src/paint.ts +202 -36
- package/src/search-client.ts +306 -0
- package/src/search-protocol.ts +100 -0
- package/src/search-worker.ts +171 -0
- package/src/semantic.ts +8 -2
- package/src/shiki-document.ts +143 -0
- package/src/shiki-worker.ts +344 -0
- package/src/shiki.ts +103 -33
- package/src/testing.ts +2 -0
- package/src/tokens.ts +36 -7
- package/src/touch.ts +134 -0
- package/src/types.ts +174 -8
- package/src/uniform-geometry.ts +57 -0
- package/src/view.ts +500 -102
- package/src/worker-client.ts +212 -0
package/dist/find.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { mapOffset } from './offsets.js';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { FindSearchQueue, scheduleFind } from './find-search.js';
|
|
3
|
+
import { EditorSearchError } from './search-client.js';
|
|
4
|
+
import { SEARCH_LIMIT, MAX_REPLACEMENT_PAGE, MAX_REPLACEMENT_CHARACTERS } from './search-protocol.js';
|
|
5
|
+
export const FIND_LIMIT = SEARCH_LIMIT;
|
|
6
|
+
const LIVE_REGEX_LIMIT = 8_000_000;
|
|
4
7
|
/*
|
|
5
8
|
* The find of the host's find bar: every match marked, one of them current. It searches the model on
|
|
6
9
|
* its own and never touches the selection until the find ends.
|
|
@@ -8,14 +11,39 @@ export const FIND_LIMIT = 10000;
|
|
|
8
11
|
export class FindController {
|
|
9
12
|
query = null;
|
|
10
13
|
marks = [];
|
|
14
|
+
cache;
|
|
11
15
|
index = null;
|
|
16
|
+
outside = null;
|
|
17
|
+
seeking = false;
|
|
18
|
+
replacing = 0;
|
|
19
|
+
replaceEpoch = -1;
|
|
20
|
+
replaced;
|
|
21
|
+
replaceFailed;
|
|
12
22
|
/* The text a search in the selection is held to, which follows the text through edits; null when nothing was selected. */
|
|
13
23
|
bounds = null;
|
|
14
24
|
/* Where the next refresh looks for the current match from, once: right after what a replace wrote. */
|
|
15
25
|
pendingAnchor;
|
|
16
26
|
model;
|
|
17
|
-
|
|
27
|
+
options;
|
|
28
|
+
search;
|
|
29
|
+
regexCache;
|
|
30
|
+
snapshotMemo;
|
|
31
|
+
retained = null;
|
|
32
|
+
epoch = 0;
|
|
33
|
+
searching = false;
|
|
34
|
+
failed;
|
|
35
|
+
captureFailed;
|
|
36
|
+
pendingStep;
|
|
37
|
+
cancelSearch;
|
|
38
|
+
cancelMatch;
|
|
39
|
+
preview = false;
|
|
40
|
+
disposed = false;
|
|
41
|
+
revision = -1;
|
|
42
|
+
cursorRequests = 0;
|
|
43
|
+
constructor(model, options) {
|
|
18
44
|
this.model = model;
|
|
45
|
+
this.options = options;
|
|
46
|
+
this.search = options === undefined ? undefined : new FindSearchQueue(options);
|
|
19
47
|
}
|
|
20
48
|
get activeQuery() {
|
|
21
49
|
return this.query;
|
|
@@ -30,19 +58,92 @@ export class FindController {
|
|
|
30
58
|
return this.index;
|
|
31
59
|
}
|
|
32
60
|
get state() {
|
|
33
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
count: this.marks.length,
|
|
63
|
+
current: this.index,
|
|
64
|
+
...(this.cache?.complete === false || (this.failed === undefined && this.regexCache?.complete === false) ? { limited: true } : {}),
|
|
65
|
+
...(this.searching || this.seeking || (this.replacing > 0 && this.replaceEpoch === this.epoch) ? { searching: true } : {}),
|
|
66
|
+
...(this.replaced === undefined ? {} : { replaced: this.replaced }),
|
|
67
|
+
...(this.replaceFailed === undefined ? {} : { replaceFailed: this.replaceFailed }),
|
|
68
|
+
...(this.failed === undefined ? {} : { failed: this.failed }),
|
|
69
|
+
...(this.captureFailed === undefined ? {} : { captureFailed: this.captureFailed }),
|
|
70
|
+
...(this.search !== undefined && this.query?.regex && this.model.getLength() > LIVE_REGEX_LIMIT && !this.freshRegex ? { large: true } : {}),
|
|
71
|
+
...(this.query?.inSelection === true && this.bounds === null ? { noSelection: true } : {})
|
|
72
|
+
};
|
|
34
73
|
}
|
|
35
74
|
/* The range a search in the selection is held to. */
|
|
36
75
|
get scope() {
|
|
37
76
|
return this.query?.inSelection === true ? this.bounds : null;
|
|
38
77
|
}
|
|
39
78
|
get currentMark() {
|
|
40
|
-
return this.index === null ? null : (this.marks[this.index] ?? null);
|
|
79
|
+
return this.outside ?? (this.index === null ? null : (this.marks[this.index] ?? null));
|
|
80
|
+
}
|
|
81
|
+
matchesBetween(from, to) {
|
|
82
|
+
let low = 0;
|
|
83
|
+
let high = this.marks.length;
|
|
84
|
+
while (low < high) {
|
|
85
|
+
const middle = (low + high) >>> 1;
|
|
86
|
+
if (this.marks[middle].to < from) {
|
|
87
|
+
low = middle + 1;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
high = middle;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const first = low;
|
|
94
|
+
high = this.marks.length;
|
|
95
|
+
while (low < high) {
|
|
96
|
+
const middle = (low + high) >>> 1;
|
|
97
|
+
if (this.marks[middle].from <= to) {
|
|
98
|
+
low = middle + 1;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
high = middle;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return this.marks.slice(first, low);
|
|
105
|
+
}
|
|
106
|
+
get currentMatch() {
|
|
107
|
+
if (this.search !== undefined && this.query?.regex) {
|
|
108
|
+
const mark = this.currentMark;
|
|
109
|
+
return this.freshRegex && this.retained?.from === mark?.from && this.retained?.to === mark?.to ? this.retained : null;
|
|
110
|
+
}
|
|
111
|
+
const matches = this.cachedMatches;
|
|
112
|
+
if (this.outside !== null && matches !== null) {
|
|
113
|
+
return {
|
|
114
|
+
...this.outside,
|
|
115
|
+
text: this.model.slice(this.outside.from, this.outside.to),
|
|
116
|
+
captures: [],
|
|
117
|
+
regex: false,
|
|
118
|
+
revision: this.model.getRevision()
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return this.index === null || matches === null ? null : (matches[this.index] ?? null);
|
|
122
|
+
}
|
|
123
|
+
get completeMatches() {
|
|
124
|
+
return this.cache?.complete === true ? this.cachedMatches : null;
|
|
125
|
+
}
|
|
126
|
+
get cachedMatches() {
|
|
127
|
+
return this.query !== null && this.cache?.revision === this.model.getRevision() && this.cache.key === this.cacheKey() ? this.cache.matches : null;
|
|
128
|
+
}
|
|
129
|
+
cacheKey() {
|
|
130
|
+
const query = this.query;
|
|
131
|
+
const scope = this.scope;
|
|
132
|
+
return JSON.stringify([
|
|
133
|
+
query?.text,
|
|
134
|
+
query?.caseSensitive === true,
|
|
135
|
+
query?.wholeWord === true,
|
|
136
|
+
query?.regex === true,
|
|
137
|
+
query?.inSelection === true,
|
|
138
|
+
scope?.from,
|
|
139
|
+
scope?.to
|
|
140
|
+
]);
|
|
41
141
|
}
|
|
42
142
|
/* A new query, or null to end the find. */
|
|
43
143
|
set(query) {
|
|
144
|
+
const oldKey = this.cacheKey();
|
|
44
145
|
const wasInSelection = this.query?.inSelection === true;
|
|
45
|
-
this.query = query === null || query.text === '' ? null : query;
|
|
146
|
+
this.query = query === null || query.text === '' ? null : { ...query };
|
|
46
147
|
if (this.query?.inSelection !== true) {
|
|
47
148
|
this.bounds = null;
|
|
48
149
|
}
|
|
@@ -53,10 +154,20 @@ export class FindController {
|
|
|
53
154
|
? null
|
|
54
155
|
: { from: Math.min(selection.anchor, selection.head), to: Math.max(selection.anchor, selection.head) };
|
|
55
156
|
}
|
|
157
|
+
if (this.cacheKey() !== oldKey) {
|
|
158
|
+
this.pendingAnchor = undefined;
|
|
159
|
+
this.pendingStep = undefined;
|
|
160
|
+
this.outside = null;
|
|
161
|
+
this.replaceFailed = undefined;
|
|
162
|
+
this.replaced = undefined;
|
|
163
|
+
}
|
|
56
164
|
this.refresh();
|
|
57
165
|
}
|
|
58
166
|
/* The text of a search in the selection moved with an edit. */
|
|
59
167
|
mapBounds(changes) {
|
|
168
|
+
if (this.pendingAnchor !== undefined) {
|
|
169
|
+
this.pendingAnchor = mapOffset(this.pendingAnchor, changes);
|
|
170
|
+
}
|
|
60
171
|
if (this.bounds !== null) {
|
|
61
172
|
this.bounds = { from: mapOffset(this.bounds.from, changes), to: mapOffset(this.bounds.to, changes) };
|
|
62
173
|
}
|
|
@@ -66,43 +177,482 @@ export class FindController {
|
|
|
66
177
|
* previous one was, or from the cursor, so each letter typed keeps the place instead of starting over.
|
|
67
178
|
*/
|
|
68
179
|
refresh() {
|
|
180
|
+
if (this.revision !== this.model.getRevision()) {
|
|
181
|
+
this.revision = this.model.getRevision();
|
|
182
|
+
this.replaced = undefined;
|
|
183
|
+
}
|
|
69
184
|
const previous = this.currentMark;
|
|
70
185
|
const query = this.query;
|
|
186
|
+
if (this.search !== undefined && query?.regex) {
|
|
187
|
+
this.refreshRegex(previous);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.resetRegex(query === null || this.search?.busy === true);
|
|
71
191
|
if (query === null) {
|
|
72
192
|
this.marks = [];
|
|
193
|
+
this.cache = undefined;
|
|
73
194
|
this.index = null;
|
|
74
195
|
return;
|
|
75
196
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
197
|
+
const revision = this.model.getRevision();
|
|
198
|
+
const key = this.cacheKey();
|
|
199
|
+
if (this.cache?.revision !== revision || this.cache.key !== key) {
|
|
200
|
+
let found = [];
|
|
201
|
+
try {
|
|
202
|
+
const inSelection = query.inSelection === true;
|
|
203
|
+
if (!inSelection || this.bounds !== null) {
|
|
204
|
+
found = this.model.find(query.text, {
|
|
205
|
+
caseSensitive: query.caseSensitive,
|
|
206
|
+
wholeWord: query.wholeWord,
|
|
207
|
+
regex: query.regex,
|
|
208
|
+
maxResults: FIND_LIMIT + 1,
|
|
209
|
+
...(inSelection ? this.bounds : {})
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// A pattern that does not parse finds nothing; the find bar says it is invalid.
|
|
215
|
+
}
|
|
216
|
+
this.cache = { revision, key, matches: found.slice(0, FIND_LIMIT), complete: found.length <= FIND_LIMIT };
|
|
93
217
|
}
|
|
218
|
+
const found = this.cache.matches;
|
|
94
219
|
this.marks = found;
|
|
95
220
|
const selection = this.model.getPrimary();
|
|
96
221
|
const anchor = this.pendingAnchor ?? previous?.from ?? Math.min(selection.anchor, selection.head);
|
|
97
222
|
this.pendingAnchor = undefined;
|
|
98
|
-
|
|
99
|
-
this.index = found.length === 0 ? null : Math.max(0, next);
|
|
223
|
+
this.chooseCurrent(anchor, 1, !this.cache.complete);
|
|
100
224
|
}
|
|
101
225
|
step(direction) {
|
|
226
|
+
this.replaced = undefined;
|
|
227
|
+
if (this.seeking) {
|
|
228
|
+
this.pendingStep = direction;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (this.search !== undefined && this.query?.regex && !this.freshRegex) {
|
|
232
|
+
this.pendingStep = direction;
|
|
233
|
+
if (!this.searching) {
|
|
234
|
+
this.startRegex(true);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
102
238
|
const count = this.marks.length;
|
|
103
239
|
if (count === 0) {
|
|
104
240
|
return;
|
|
105
241
|
}
|
|
242
|
+
const incomplete = this.regexCache?.complete === false || this.cache?.complete === false;
|
|
243
|
+
const current = this.currentMark;
|
|
244
|
+
if (this.search !== undefined &&
|
|
245
|
+
incomplete &&
|
|
246
|
+
current !== null &&
|
|
247
|
+
(this.outside !== null || (direction === 1 ? this.index === count - 1 : this.index === 0))) {
|
|
248
|
+
let offset = direction === -1 ? current.from : current.to;
|
|
249
|
+
if (direction === 1 && current.from === current.to) {
|
|
250
|
+
offset += (this.model.getText().codePointAt(offset) ?? 0) > 0xffff ? 2 : 1;
|
|
251
|
+
}
|
|
252
|
+
if (offset > this.model.getLength()) {
|
|
253
|
+
offset = 0;
|
|
254
|
+
}
|
|
255
|
+
this.seek(offset, direction);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
this.outside = null;
|
|
106
259
|
this.index = this.index === null ? (direction === 1 ? 0 : count - 1) : (this.index + direction + count) % count;
|
|
260
|
+
this.captureFailed = undefined;
|
|
261
|
+
this.preparePreview();
|
|
262
|
+
}
|
|
263
|
+
get freshRegex() {
|
|
264
|
+
return (this.failed === undefined && !this.searching && this.regexCache?.revision === this.model.getRevision() && this.regexCache.key === this.cacheKey());
|
|
265
|
+
}
|
|
266
|
+
resetRegex(release) {
|
|
267
|
+
this.epoch++;
|
|
268
|
+
this.cancelSearch?.();
|
|
269
|
+
this.cancelSearch = undefined;
|
|
270
|
+
this.cancelMatch?.();
|
|
271
|
+
this.cancelMatch = undefined;
|
|
272
|
+
this.regexCache = undefined;
|
|
273
|
+
this.outside = null;
|
|
274
|
+
this.seeking = false;
|
|
275
|
+
this.snapshotMemo = undefined;
|
|
276
|
+
this.retained = null;
|
|
277
|
+
this.searching = false;
|
|
278
|
+
this.failed = undefined;
|
|
279
|
+
this.captureFailed = undefined;
|
|
280
|
+
if (release) {
|
|
281
|
+
this.search?.release();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
refreshRegex(previous) {
|
|
285
|
+
if (this.freshRegex) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const failure = this.failed;
|
|
289
|
+
const retryBlocked = failure === 'timeout' || failure === 'unavailable';
|
|
290
|
+
const sameQuery = this.regexCache?.key === this.cacheKey();
|
|
291
|
+
this.resetRegex(false);
|
|
292
|
+
this.cache = undefined;
|
|
293
|
+
this.marks = [];
|
|
294
|
+
this.index = null;
|
|
295
|
+
if (retryBlocked && sameQuery) {
|
|
296
|
+
this.failed = failure;
|
|
297
|
+
this.regexCache = { revision: this.model.getRevision(), key: this.cacheKey(), complete: false };
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.pendingAnchor ??= previous?.from;
|
|
301
|
+
this.startRegex(false);
|
|
302
|
+
}
|
|
303
|
+
startRegex(explicit) {
|
|
304
|
+
const query = this.query;
|
|
305
|
+
if (query === null || this.search === undefined || this.disposed) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (query.inSelection && this.bounds === null) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!explicit && this.model.getLength() > LIVE_REGEX_LIMIT) {
|
|
312
|
+
this.pendingStep = undefined;
|
|
313
|
+
this.pendingAnchor = undefined;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const epoch = this.epoch;
|
|
317
|
+
const key = this.cacheKey();
|
|
318
|
+
const revision = this.model.getRevision();
|
|
319
|
+
this.failed = undefined;
|
|
320
|
+
this.searching = true;
|
|
321
|
+
this.cancelSearch?.();
|
|
322
|
+
this.cancelSearch = (this.options?.schedule ?? scheduleFind)(() => {
|
|
323
|
+
this.cancelSearch = undefined;
|
|
324
|
+
const snapshot = this.snapshot();
|
|
325
|
+
void this.search.request((client) => {
|
|
326
|
+
if (epoch !== this.epoch || this.disposed) {
|
|
327
|
+
throw new EditorSearchError('superseded', 'The find query changed.');
|
|
328
|
+
}
|
|
329
|
+
return client.search(snapshot, query, { ...this.scope, maxResults: FIND_LIMIT, ...(explicit ? { deadline: 10000 } : {}) });
|
|
330
|
+
}, explicit ? 'command' : 'live', () => epoch === this.epoch && revision === this.model.getRevision() && !this.disposed)
|
|
331
|
+
.then((result) => {
|
|
332
|
+
if (epoch !== this.epoch || revision !== this.model.getRevision() || this.disposed) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
this.regexCache = { revision, key, complete: !result.limited };
|
|
336
|
+
const marks = [];
|
|
337
|
+
for (let index = 0; index < result.ranges.length; index += 2) {
|
|
338
|
+
marks.push({ from: result.ranges[index], to: result.ranges[index + 1] });
|
|
339
|
+
}
|
|
340
|
+
this.marks = marks;
|
|
341
|
+
const selection = this.model.getPrimary();
|
|
342
|
+
const anchor = this.pendingAnchor ?? Math.min(selection.anchor, selection.head);
|
|
343
|
+
this.pendingAnchor = undefined;
|
|
344
|
+
const direction = this.pendingStep;
|
|
345
|
+
this.pendingStep = undefined;
|
|
346
|
+
this.searching = false;
|
|
347
|
+
this.chooseCurrent(anchor, direction ?? 1, result.limited);
|
|
348
|
+
this.options?.changed(true);
|
|
349
|
+
this.preparePreview();
|
|
350
|
+
})
|
|
351
|
+
.catch((error) => {
|
|
352
|
+
if (epoch !== this.epoch || this.disposed) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
this.searching = false;
|
|
356
|
+
if (error instanceof EditorSearchError && error.kind === 'superseded') {
|
|
357
|
+
this.options?.changed();
|
|
358
|
+
this.startRegex(this.pendingStep !== undefined);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.pendingAnchor = undefined;
|
|
362
|
+
this.pendingStep = undefined;
|
|
363
|
+
this.failed =
|
|
364
|
+
error instanceof EditorSearchError
|
|
365
|
+
? error.kind === 'syntax'
|
|
366
|
+
? 'invalid'
|
|
367
|
+
: error.kind === 'timeout' || error.kind === 'unavailable'
|
|
368
|
+
? error.kind
|
|
369
|
+
: 'error'
|
|
370
|
+
: 'error';
|
|
371
|
+
this.regexCache = { revision, key, complete: false };
|
|
372
|
+
this.options?.changed();
|
|
373
|
+
});
|
|
374
|
+
}, explicit || this.model.getLength() <= 1_000_000 ? 0 : 150);
|
|
375
|
+
}
|
|
376
|
+
snapshot() {
|
|
377
|
+
const revision = this.model.getRevision();
|
|
378
|
+
if (this.snapshotMemo?.revision !== revision) {
|
|
379
|
+
this.snapshotMemo = { revision, text: this.model.getText() };
|
|
380
|
+
}
|
|
381
|
+
return this.snapshotMemo;
|
|
107
382
|
}
|
|
383
|
+
setPreview(active) {
|
|
384
|
+
this.preview = active;
|
|
385
|
+
this.preparePreview();
|
|
386
|
+
}
|
|
387
|
+
preparePreview() {
|
|
388
|
+
this.cancelMatch?.();
|
|
389
|
+
this.cancelMatch = undefined;
|
|
390
|
+
if (this.preview &&
|
|
391
|
+
this.search !== undefined &&
|
|
392
|
+
this.query?.regex &&
|
|
393
|
+
this.freshRegex &&
|
|
394
|
+
this.currentMatch === null &&
|
|
395
|
+
this.captureFailed === undefined) {
|
|
396
|
+
this.cancelMatch = (this.options?.schedule ?? scheduleFind)(() => {
|
|
397
|
+
this.cancelMatch = undefined;
|
|
398
|
+
void this.ensureMatch().catch(() => { });
|
|
399
|
+
}, 50);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async ensureMatch(command = false) {
|
|
403
|
+
if (this.currentMatch !== null || this.search === undefined || !this.query?.regex || !this.freshRegex) {
|
|
404
|
+
return this.currentMatch;
|
|
405
|
+
}
|
|
406
|
+
const mark = this.currentMark;
|
|
407
|
+
if (mark === null) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
const query = this.query;
|
|
411
|
+
const epoch = this.epoch;
|
|
412
|
+
const snapshot = this.snapshot();
|
|
413
|
+
if (command) {
|
|
414
|
+
this.captureFailed = undefined;
|
|
415
|
+
}
|
|
416
|
+
const current = () => !this.disposed &&
|
|
417
|
+
epoch === this.epoch &&
|
|
418
|
+
this.model.getRevision() === snapshot.revision &&
|
|
419
|
+
this.currentMark?.from === mark.from &&
|
|
420
|
+
this.currentMark?.to === mark.to;
|
|
421
|
+
try {
|
|
422
|
+
const found = await this.search.request((client) => {
|
|
423
|
+
if (!current()) {
|
|
424
|
+
throw new EditorSearchError('superseded', 'The current find match changed.');
|
|
425
|
+
}
|
|
426
|
+
return client.match(snapshot, query, { ...mark, deadline: 10000 });
|
|
427
|
+
}, command ? 'command' : 'preview');
|
|
428
|
+
if (!current()) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
this.retained = found;
|
|
432
|
+
this.captureFailed = undefined;
|
|
433
|
+
this.options?.changed();
|
|
434
|
+
return found;
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
if (current() && !(error instanceof EditorSearchError && error.kind === 'superseded')) {
|
|
438
|
+
this.captureFailed = error instanceof EditorSearchError && (error.kind === 'timeout' || error.kind === 'unavailable') ? error.kind : 'error';
|
|
439
|
+
this.options?.changed();
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
chooseCurrent(anchor, direction, limited) {
|
|
445
|
+
if (this.marks.length === 0) {
|
|
446
|
+
this.index = null;
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const next = direction === 1 ? this.marks.findIndex((mark) => mark.from >= anchor) : this.marks.findLastIndex((mark) => mark.from < anchor);
|
|
450
|
+
if (limited && this.search !== undefined && (next === -1 || (direction === -1 && next === this.marks.length - 1))) {
|
|
451
|
+
this.index = null;
|
|
452
|
+
this.seek(anchor, direction);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
this.index = next === -1 ? (direction === 1 ? 0 : this.marks.length - 1) : next;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
seek(offset, direction) {
|
|
459
|
+
const query = this.query;
|
|
460
|
+
if (this.search === undefined || query === null) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const epoch = this.epoch;
|
|
464
|
+
const snapshot = this.snapshot();
|
|
465
|
+
const scope = this.scope;
|
|
466
|
+
const valid = () => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision();
|
|
467
|
+
this.seeking = true;
|
|
468
|
+
this.failed = undefined;
|
|
469
|
+
this.retained = null;
|
|
470
|
+
this.captureFailed = undefined;
|
|
471
|
+
void this.search
|
|
472
|
+
.request((client) => client.next(snapshot, query, offset, { ...scope, backwards: direction === -1, wrap: true, deadline: 10000 }), 'command', valid)
|
|
473
|
+
.then((range) => {
|
|
474
|
+
if (!valid()) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
this.seeking = false;
|
|
478
|
+
this.outside = null;
|
|
479
|
+
this.index = range === null ? null : this.marks.findIndex((mark) => mark.from === range.from && mark.to === range.to);
|
|
480
|
+
if (this.index === -1) {
|
|
481
|
+
this.index = null;
|
|
482
|
+
this.outside = range;
|
|
483
|
+
}
|
|
484
|
+
this.options?.changed(true);
|
|
485
|
+
this.preparePreview();
|
|
486
|
+
const pending = this.pendingStep;
|
|
487
|
+
this.pendingStep = undefined;
|
|
488
|
+
if (pending !== undefined) {
|
|
489
|
+
this.step(pending);
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
.catch((error) => {
|
|
493
|
+
if (valid()) {
|
|
494
|
+
this.seeking = false;
|
|
495
|
+
this.failed = searchFailure(error);
|
|
496
|
+
this.options?.changed();
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
async nextFromCursor(query, offset, direction) {
|
|
501
|
+
if (this.search === undefined) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
const snapshot = this.snapshot();
|
|
505
|
+
const epoch = this.epoch;
|
|
506
|
+
const valid = () => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision();
|
|
507
|
+
this.cursorRequests++;
|
|
508
|
+
try {
|
|
509
|
+
const range = await this.search.request((client) => client.next(snapshot, query, offset, { backwards: direction === -1, wrap: true, deadline: 10000 }), 'command', valid);
|
|
510
|
+
return valid() ? range : null;
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
if (valid()) {
|
|
514
|
+
this.failed = searchFailure(error);
|
|
515
|
+
this.options?.changed();
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
finally {
|
|
520
|
+
this.cursorRequests--;
|
|
521
|
+
if (this.query === null && this.cursorRequests === 0) {
|
|
522
|
+
this.search.release();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
reportReplaceFailed(failure) {
|
|
527
|
+
this.replaceFailed = failure;
|
|
528
|
+
this.replaced = undefined;
|
|
529
|
+
this.options?.changed();
|
|
530
|
+
}
|
|
531
|
+
reportReplaced(count) {
|
|
532
|
+
this.replaced = count;
|
|
533
|
+
this.replaceFailed = undefined;
|
|
534
|
+
this.options?.changed();
|
|
535
|
+
}
|
|
536
|
+
async replaceOne(replacement, preserveCase, canApply) {
|
|
537
|
+
const query = this.query, mark = this.currentMark;
|
|
538
|
+
if (query === null || mark === null || this.search === undefined || !canApply()) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
const snapshot = this.snapshot();
|
|
542
|
+
const epoch = this.epoch;
|
|
543
|
+
const valid = () => !this.disposed &&
|
|
544
|
+
epoch === this.epoch &&
|
|
545
|
+
snapshot.revision === this.model.getRevision() &&
|
|
546
|
+
canApply() &&
|
|
547
|
+
this.currentMark?.from === mark.from &&
|
|
548
|
+
this.currentMark?.to === mark.to;
|
|
549
|
+
this.replacing++;
|
|
550
|
+
this.replaceEpoch = epoch;
|
|
551
|
+
this.replaceFailed = undefined;
|
|
552
|
+
this.options?.changed();
|
|
553
|
+
try {
|
|
554
|
+
const page = await this.search.request((client) => client.replacements(snapshot, query, replacement, {
|
|
555
|
+
from: mark.from,
|
|
556
|
+
to: mark.to,
|
|
557
|
+
preserveCase,
|
|
558
|
+
maxEdits: 1,
|
|
559
|
+
maxCharacters: MAX_REPLACEMENT_CHARACTERS,
|
|
560
|
+
deadline: 10000
|
|
561
|
+
}), 'command', valid);
|
|
562
|
+
if (!valid() || page.ranges.length !== 3 || page.ranges[0] !== mark.from || page.ranges[1] !== mark.to || page.next !== null) {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
this.pendingAnchor = mark.to;
|
|
566
|
+
const changed = this.model.applyEdits([{ from: mark.from, to: mark.to, text: page.text }], {
|
|
567
|
+
source: 'command',
|
|
568
|
+
expectedRevision: snapshot.revision
|
|
569
|
+
});
|
|
570
|
+
if (!changed) {
|
|
571
|
+
this.pendingAnchor = undefined;
|
|
572
|
+
}
|
|
573
|
+
return changed;
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
if (valid()) {
|
|
577
|
+
const failure = searchFailure(error);
|
|
578
|
+
this.reportReplaceFailed(error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure);
|
|
579
|
+
}
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
finally {
|
|
583
|
+
this.replacing--;
|
|
584
|
+
if (!this.disposed) {
|
|
585
|
+
this.options?.changed();
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async replaceAll(replacement, preserveCase, canApply) {
|
|
590
|
+
const query = this.query;
|
|
591
|
+
if (query === null || this.search === undefined || this.state.noSelection || !canApply()) {
|
|
592
|
+
return 0;
|
|
593
|
+
}
|
|
594
|
+
const snapshot = this.snapshot();
|
|
595
|
+
const epoch = this.epoch;
|
|
596
|
+
const scope = this.scope;
|
|
597
|
+
const valid = () => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision() && canApply();
|
|
598
|
+
this.replacing++;
|
|
599
|
+
this.replaceEpoch = epoch;
|
|
600
|
+
this.replaceFailed = undefined;
|
|
601
|
+
this.replaced = undefined;
|
|
602
|
+
this.options?.changed();
|
|
603
|
+
try {
|
|
604
|
+
const page = await this.search.request((client) => client.replacements(snapshot, query, replacement, {
|
|
605
|
+
...scope,
|
|
606
|
+
preserveCase,
|
|
607
|
+
maxEdits: MAX_REPLACEMENT_PAGE,
|
|
608
|
+
maxCharacters: MAX_REPLACEMENT_CHARACTERS,
|
|
609
|
+
deadline: 10000
|
|
610
|
+
}), 'command', valid);
|
|
611
|
+
if (!valid()) {
|
|
612
|
+
return 0;
|
|
613
|
+
}
|
|
614
|
+
if (page.next !== null) {
|
|
615
|
+
this.replaceFailed = 'limit';
|
|
616
|
+
return 0;
|
|
617
|
+
}
|
|
618
|
+
const edits = [];
|
|
619
|
+
let position = 0;
|
|
620
|
+
for (let index = 0; index < page.ranges.length; index += 3) {
|
|
621
|
+
const length = page.ranges[index + 2];
|
|
622
|
+
edits.push({ from: page.ranges[index], to: page.ranges[index + 1], text: page.text.slice(position, position + length) });
|
|
623
|
+
position += length;
|
|
624
|
+
}
|
|
625
|
+
const changed = this.model.applyEdits(edits, { source: 'command', expectedRevision: snapshot.revision });
|
|
626
|
+
const count = changed ? edits.length : 0;
|
|
627
|
+
this.reportReplaced(count);
|
|
628
|
+
return count;
|
|
629
|
+
}
|
|
630
|
+
catch (error) {
|
|
631
|
+
if (!this.disposed && epoch === this.epoch) {
|
|
632
|
+
const failure = searchFailure(error);
|
|
633
|
+
this.replaceFailed = error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure;
|
|
634
|
+
}
|
|
635
|
+
return 0;
|
|
636
|
+
}
|
|
637
|
+
finally {
|
|
638
|
+
this.replacing--;
|
|
639
|
+
if (!this.disposed) {
|
|
640
|
+
this.options?.changed();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
dispose() {
|
|
645
|
+
this.disposed = true;
|
|
646
|
+
this.resetRegex(false);
|
|
647
|
+
this.search?.dispose();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function searchFailure(error) {
|
|
651
|
+
return error instanceof EditorSearchError
|
|
652
|
+
? error.kind === 'syntax'
|
|
653
|
+
? 'invalid'
|
|
654
|
+
: error.kind === 'timeout' || error.kind === 'unavailable'
|
|
655
|
+
? error.kind
|
|
656
|
+
: 'error'
|
|
657
|
+
: 'error';
|
|
108
658
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Affinity, LineGeometry } from './layout.ts';
|
|
2
|
+
export declare function stopCount(geometry: LineGeometry): number;
|
|
3
|
+
export declare function offsetOfStop(geometry: LineGeometry, stop: number): number;
|
|
4
|
+
export declare function rowOfStop(geometry: LineGeometry, stop: number): number;
|
|
5
|
+
export declare function xOfStop(geometry: LineGeometry, stop: number, affinity?: Affinity): number;
|
|
6
|
+
export declare function stopAtOffset(geometry: LineGeometry, offset: number): number;
|