@adecore/editor 0.39.0 → 0.40.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/controller.d.ts +1 -0
- package/dist/controller.js +17 -28
- package/dist/engine.js +2 -9
- package/dist/find.js +12 -13
- package/dist/layout.js +7 -2
- package/dist/paint.d.ts +2 -0
- package/dist/paint.js +23 -20
- package/dist/view.d.ts +3 -2
- package/dist/view.js +46 -59
- package/package.json +6 -6
- package/src/attribution.ts +14 -0
- package/src/code-block.ts +25 -0
- package/src/column-selection.ts +10 -0
- package/src/controller.ts +189 -28
- package/src/engine.ts +87 -9
- package/src/fake.ts +79 -0
- package/src/find-search.ts +20 -0
- package/src/find.ts +135 -14
- package/src/geometry-stops.ts +10 -0
- package/src/grammar-preparation.ts +23 -0
- package/src/index.ts +1 -0
- package/src/input.ts +13 -0
- package/src/keymap-table.ts +9 -0
- package/src/keymap.ts +25 -0
- package/src/layout-changes.ts +7 -0
- package/src/layout.ts +219 -3
- package/src/lens.ts +3 -0
- package/src/listeners.ts +1 -0
- package/src/metrics.ts +14 -0
- package/src/modifier-gesture.ts +17 -0
- package/src/native-input.ts +27 -0
- package/src/offsets.ts +10 -0
- package/src/outline.ts +38 -0
- package/src/overview.ts +13 -0
- package/src/paint.ts +140 -20
- package/src/php-html.ts +1 -0
- package/src/pointer.ts +50 -0
- package/src/scroll-animation.ts +10 -0
- package/src/scroll.ts +7 -0
- package/src/search-client.ts +32 -0
- package/src/search-worker.ts +38 -0
- package/src/semantic.ts +12 -0
- package/src/shiki-document.ts +19 -0
- package/src/shiki-worker.ts +68 -0
- package/src/shiki.ts +37 -0
- package/src/testing.ts +17 -0
- package/src/theme-scopes.ts +14 -0
- package/src/tokens.ts +31 -0
- package/src/touch.ts +16 -0
- package/src/tracked-range.ts +5 -0
- package/src/uniform-geometry.ts +10 -0
- package/src/view.ts +481 -75
- package/src/worker-client.ts +54 -0
package/src/find.ts
CHANGED
|
@@ -105,33 +105,42 @@ export class FindController {
|
|
|
105
105
|
matchesBetween(from: number, to: number): readonly FindMark[] {
|
|
106
106
|
let low = 0;
|
|
107
107
|
let high = this.marks.length;
|
|
108
|
+
|
|
108
109
|
while (low < high) {
|
|
109
110
|
const middle = (low + high) >>> 1;
|
|
111
|
+
|
|
110
112
|
if (this.marks[middle]!.to < from) {
|
|
111
113
|
low = middle + 1;
|
|
112
114
|
} else {
|
|
113
115
|
high = middle;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
118
|
+
|
|
116
119
|
const first = low;
|
|
117
120
|
high = this.marks.length;
|
|
121
|
+
|
|
118
122
|
while (low < high) {
|
|
119
123
|
const middle = (low + high) >>> 1;
|
|
124
|
+
|
|
120
125
|
if (this.marks[middle]!.from <= to) {
|
|
121
126
|
low = middle + 1;
|
|
122
127
|
} else {
|
|
123
128
|
high = middle;
|
|
124
129
|
}
|
|
125
130
|
}
|
|
131
|
+
|
|
126
132
|
return this.marks.slice(first, low);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
get currentMatch(): FindMatch | null {
|
|
130
136
|
if (this.search !== undefined && this.query?.regex) {
|
|
131
137
|
const mark = this.currentMark;
|
|
138
|
+
|
|
132
139
|
return this.freshRegex && this.retained?.from === mark?.from && this.retained?.to === mark?.to ? this.retained : null;
|
|
133
140
|
}
|
|
141
|
+
|
|
134
142
|
const matches = this.cachedMatches;
|
|
143
|
+
|
|
135
144
|
if (this.outside !== null && matches !== null) {
|
|
136
145
|
return {
|
|
137
146
|
...this.outside,
|
|
@@ -141,6 +150,7 @@ export class FindController {
|
|
|
141
150
|
revision: this.model.getRevision()
|
|
142
151
|
};
|
|
143
152
|
}
|
|
153
|
+
|
|
144
154
|
return this.index === null || matches === null ? null : (matches[this.index] ?? null);
|
|
145
155
|
}
|
|
146
156
|
|
|
@@ -155,6 +165,7 @@ export class FindController {
|
|
|
155
165
|
private cacheKey(): string {
|
|
156
166
|
const query = this.query;
|
|
157
167
|
const scope = this.scope;
|
|
168
|
+
|
|
158
169
|
return JSON.stringify([
|
|
159
170
|
query?.text,
|
|
160
171
|
query?.caseSensitive === true,
|
|
@@ -171,15 +182,18 @@ export class FindController {
|
|
|
171
182
|
const oldKey = this.cacheKey();
|
|
172
183
|
const wasInSelection = this.query?.inSelection === true;
|
|
173
184
|
this.query = query === null || query.text === '' ? null : { ...query };
|
|
185
|
+
|
|
174
186
|
if (this.query?.inSelection !== true) {
|
|
175
187
|
this.bounds = null;
|
|
176
188
|
} else if (!wasInSelection) {
|
|
177
189
|
const selection = this.model.getPrimary();
|
|
190
|
+
|
|
178
191
|
this.bounds =
|
|
179
192
|
selection.anchor === selection.head
|
|
180
193
|
? null
|
|
181
194
|
: { from: Math.min(selection.anchor, selection.head), to: Math.max(selection.anchor, selection.head) };
|
|
182
195
|
}
|
|
196
|
+
|
|
183
197
|
if (this.cacheKey() !== oldKey) {
|
|
184
198
|
this.pendingAnchor = undefined;
|
|
185
199
|
this.pendingStep = undefined;
|
|
@@ -187,6 +201,7 @@ export class FindController {
|
|
|
187
201
|
this.replaceFailed = undefined;
|
|
188
202
|
this.replaced = undefined;
|
|
189
203
|
}
|
|
204
|
+
|
|
190
205
|
this.refresh();
|
|
191
206
|
}
|
|
192
207
|
|
|
@@ -195,6 +210,7 @@ export class FindController {
|
|
|
195
210
|
if (this.pendingAnchor !== undefined) {
|
|
196
211
|
this.pendingAnchor = mapOffset(this.pendingAnchor, changes);
|
|
197
212
|
}
|
|
213
|
+
|
|
198
214
|
if (this.bounds !== null) {
|
|
199
215
|
this.bounds = { from: mapOffset(this.bounds.from, changes), to: mapOffset(this.bounds.to, changes) };
|
|
200
216
|
}
|
|
@@ -209,26 +225,35 @@ export class FindController {
|
|
|
209
225
|
this.revision = this.model.getRevision();
|
|
210
226
|
this.replaced = undefined;
|
|
211
227
|
}
|
|
228
|
+
|
|
212
229
|
const previous = this.currentMark;
|
|
213
230
|
const query = this.query;
|
|
231
|
+
|
|
214
232
|
if (this.search !== undefined && query?.regex) {
|
|
215
233
|
this.refreshRegex(previous);
|
|
234
|
+
|
|
216
235
|
return;
|
|
217
236
|
}
|
|
237
|
+
|
|
218
238
|
this.resetRegex(query === null || this.search?.busy === true);
|
|
219
239
|
|
|
220
240
|
if (query === null) {
|
|
221
241
|
this.marks = [];
|
|
222
242
|
this.cache = undefined;
|
|
223
243
|
this.index = null;
|
|
244
|
+
|
|
224
245
|
return;
|
|
225
246
|
}
|
|
247
|
+
|
|
226
248
|
const revision = this.model.getRevision();
|
|
227
249
|
const key = this.cacheKey();
|
|
250
|
+
|
|
228
251
|
if (this.cache?.revision !== revision || this.cache.key !== key) {
|
|
229
252
|
let found: FindMatch[] = [];
|
|
253
|
+
|
|
230
254
|
try {
|
|
231
255
|
const inSelection = query.inSelection === true;
|
|
256
|
+
|
|
232
257
|
if (!inSelection || this.bounds !== null) {
|
|
233
258
|
found = this.model.find(query.text, {
|
|
234
259
|
caseSensitive: query.caseSensitive,
|
|
@@ -241,8 +266,10 @@ export class FindController {
|
|
|
241
266
|
} catch {
|
|
242
267
|
// A pattern that does not parse finds nothing; the find bar says it is invalid.
|
|
243
268
|
}
|
|
269
|
+
|
|
244
270
|
this.cache = { revision, key, matches: found.slice(0, FIND_LIMIT), complete: found.length <= FIND_LIMIT };
|
|
245
271
|
}
|
|
272
|
+
|
|
246
273
|
const found = this.cache.matches;
|
|
247
274
|
this.marks = found;
|
|
248
275
|
const selection = this.model.getPrimary();
|
|
@@ -253,23 +280,32 @@ export class FindController {
|
|
|
253
280
|
|
|
254
281
|
step(direction: 1 | -1): void {
|
|
255
282
|
this.replaced = undefined;
|
|
283
|
+
|
|
256
284
|
if (this.seeking) {
|
|
257
285
|
this.pendingStep = direction;
|
|
286
|
+
|
|
258
287
|
return;
|
|
259
288
|
}
|
|
289
|
+
|
|
260
290
|
if (this.search !== undefined && this.query?.regex && !this.freshRegex) {
|
|
261
291
|
this.pendingStep = direction;
|
|
292
|
+
|
|
262
293
|
if (!this.searching) {
|
|
263
294
|
this.startRegex(true);
|
|
264
295
|
}
|
|
296
|
+
|
|
265
297
|
return;
|
|
266
298
|
}
|
|
299
|
+
|
|
267
300
|
const count = this.marks.length;
|
|
301
|
+
|
|
268
302
|
if (count === 0) {
|
|
269
303
|
return;
|
|
270
304
|
}
|
|
305
|
+
|
|
271
306
|
const incomplete = this.regexCache?.complete === false || this.cache?.complete === false;
|
|
272
307
|
const current = this.currentMark;
|
|
308
|
+
|
|
273
309
|
if (
|
|
274
310
|
this.search !== undefined &&
|
|
275
311
|
incomplete &&
|
|
@@ -277,15 +313,20 @@ export class FindController {
|
|
|
277
313
|
(this.outside !== null || (direction === 1 ? this.index === count - 1 : this.index === 0))
|
|
278
314
|
) {
|
|
279
315
|
let offset = direction === -1 ? current.from : current.to;
|
|
316
|
+
|
|
280
317
|
if (direction === 1 && current.from === current.to) {
|
|
281
318
|
offset += (this.model.getText().codePointAt(offset) ?? 0) > 0xffff ? 2 : 1;
|
|
282
319
|
}
|
|
320
|
+
|
|
283
321
|
if (offset > this.model.getLength()) {
|
|
284
322
|
offset = 0;
|
|
285
323
|
}
|
|
324
|
+
|
|
286
325
|
this.seek(offset, direction);
|
|
326
|
+
|
|
287
327
|
return;
|
|
288
328
|
}
|
|
329
|
+
|
|
289
330
|
this.outside = null;
|
|
290
331
|
this.index = this.index === null ? (direction === 1 ? 0 : count - 1) : (this.index + direction + count) % count;
|
|
291
332
|
this.captureFailed = undefined;
|
|
@@ -312,6 +353,7 @@ export class FindController {
|
|
|
312
353
|
this.searching = false;
|
|
313
354
|
this.failed = undefined;
|
|
314
355
|
this.captureFailed = undefined;
|
|
356
|
+
|
|
315
357
|
if (release) {
|
|
316
358
|
this.search?.release();
|
|
317
359
|
}
|
|
@@ -321,6 +363,7 @@ export class FindController {
|
|
|
321
363
|
if (this.freshRegex) {
|
|
322
364
|
return;
|
|
323
365
|
}
|
|
366
|
+
|
|
324
367
|
const failure = this.failed;
|
|
325
368
|
const retryBlocked = failure === 'timeout' || failure === 'unavailable';
|
|
326
369
|
const sameQuery = this.regexCache?.key === this.cacheKey();
|
|
@@ -328,43 +371,54 @@ export class FindController {
|
|
|
328
371
|
this.cache = undefined;
|
|
329
372
|
this.marks = [];
|
|
330
373
|
this.index = null;
|
|
374
|
+
|
|
331
375
|
if (retryBlocked && sameQuery) {
|
|
332
376
|
this.failed = failure;
|
|
333
377
|
this.regexCache = { revision: this.model.getRevision(), key: this.cacheKey(), complete: false };
|
|
378
|
+
|
|
334
379
|
return;
|
|
335
380
|
}
|
|
381
|
+
|
|
336
382
|
this.pendingAnchor ??= previous?.from;
|
|
337
383
|
this.startRegex(false);
|
|
338
384
|
}
|
|
339
385
|
|
|
340
386
|
private startRegex(explicit: boolean): void {
|
|
341
387
|
const query = this.query;
|
|
388
|
+
|
|
342
389
|
if (query === null || this.search === undefined || this.disposed) {
|
|
343
390
|
return;
|
|
344
391
|
}
|
|
392
|
+
|
|
345
393
|
if (query.inSelection && this.bounds === null) {
|
|
346
394
|
return;
|
|
347
395
|
}
|
|
396
|
+
|
|
348
397
|
if (!explicit && this.model.getLength() > LIVE_REGEX_LIMIT) {
|
|
349
398
|
this.pendingStep = undefined;
|
|
350
399
|
this.pendingAnchor = undefined;
|
|
400
|
+
|
|
351
401
|
return;
|
|
352
402
|
}
|
|
403
|
+
|
|
353
404
|
const epoch = this.epoch;
|
|
354
405
|
const key = this.cacheKey();
|
|
355
406
|
const revision = this.model.getRevision();
|
|
356
407
|
this.failed = undefined;
|
|
357
408
|
this.searching = true;
|
|
358
409
|
this.cancelSearch?.();
|
|
410
|
+
|
|
359
411
|
this.cancelSearch = (this.options?.schedule ?? scheduleFind)(
|
|
360
412
|
() => {
|
|
361
413
|
this.cancelSearch = undefined;
|
|
362
414
|
const snapshot = this.snapshot();
|
|
415
|
+
|
|
363
416
|
void this.search!.request(
|
|
364
417
|
(client) => {
|
|
365
418
|
if (epoch !== this.epoch || this.disposed) {
|
|
366
419
|
throw new EditorSearchError('superseded', 'The find query changed.');
|
|
367
420
|
}
|
|
421
|
+
|
|
368
422
|
return client.search(snapshot, query, { ...this.scope, maxResults: FIND_LIMIT, ...(explicit ? { deadline: 10000 } : {}) });
|
|
369
423
|
},
|
|
370
424
|
explicit ? 'command' : 'live',
|
|
@@ -374,11 +428,14 @@ export class FindController {
|
|
|
374
428
|
if (epoch !== this.epoch || revision !== this.model.getRevision() || this.disposed) {
|
|
375
429
|
return;
|
|
376
430
|
}
|
|
431
|
+
|
|
377
432
|
this.regexCache = { revision, key, complete: !result.limited };
|
|
378
433
|
const marks: FindMark[] = [];
|
|
434
|
+
|
|
379
435
|
for (let index = 0; index < result.ranges.length; index += 2) {
|
|
380
436
|
marks.push({ from: result.ranges[index]!, to: result.ranges[index + 1]! });
|
|
381
437
|
}
|
|
438
|
+
|
|
382
439
|
this.marks = marks;
|
|
383
440
|
const selection = this.model.getPrimary();
|
|
384
441
|
const anchor = this.pendingAnchor ?? Math.min(selection.anchor, selection.head);
|
|
@@ -394,22 +451,19 @@ export class FindController {
|
|
|
394
451
|
if (epoch !== this.epoch || this.disposed) {
|
|
395
452
|
return;
|
|
396
453
|
}
|
|
454
|
+
|
|
397
455
|
this.searching = false;
|
|
456
|
+
|
|
398
457
|
if (error instanceof EditorSearchError && error.kind === 'superseded') {
|
|
399
458
|
this.options?.changed();
|
|
400
459
|
this.startRegex(this.pendingStep !== undefined);
|
|
460
|
+
|
|
401
461
|
return;
|
|
402
462
|
}
|
|
463
|
+
|
|
403
464
|
this.pendingAnchor = undefined;
|
|
404
465
|
this.pendingStep = undefined;
|
|
405
|
-
this.failed =
|
|
406
|
-
error instanceof EditorSearchError
|
|
407
|
-
? error.kind === 'syntax'
|
|
408
|
-
? 'invalid'
|
|
409
|
-
: error.kind === 'timeout' || error.kind === 'unavailable'
|
|
410
|
-
? error.kind
|
|
411
|
-
: 'error'
|
|
412
|
-
: 'error';
|
|
466
|
+
this.failed = searchFailure(error);
|
|
413
467
|
this.regexCache = { revision, key, complete: false };
|
|
414
468
|
this.options?.changed();
|
|
415
469
|
});
|
|
@@ -420,9 +474,11 @@ export class FindController {
|
|
|
420
474
|
|
|
421
475
|
private snapshot(): EditorSearchSnapshot {
|
|
422
476
|
const revision = this.model.getRevision();
|
|
477
|
+
|
|
423
478
|
if (this.snapshotMemo?.revision !== revision) {
|
|
424
479
|
this.snapshotMemo = { revision, text: this.model.getText() };
|
|
425
480
|
}
|
|
481
|
+
|
|
426
482
|
return this.snapshotMemo;
|
|
427
483
|
}
|
|
428
484
|
|
|
@@ -434,6 +490,7 @@ export class FindController {
|
|
|
434
490
|
private preparePreview(): void {
|
|
435
491
|
this.cancelMatch?.();
|
|
436
492
|
this.cancelMatch = undefined;
|
|
493
|
+
|
|
437
494
|
if (
|
|
438
495
|
this.preview &&
|
|
439
496
|
this.search !== undefined &&
|
|
@@ -453,44 +510,55 @@ export class FindController {
|
|
|
453
510
|
if (this.currentMatch !== null || this.search === undefined || !this.query?.regex || !this.freshRegex) {
|
|
454
511
|
return this.currentMatch;
|
|
455
512
|
}
|
|
513
|
+
|
|
456
514
|
const mark = this.currentMark;
|
|
515
|
+
|
|
457
516
|
if (mark === null) {
|
|
458
517
|
return null;
|
|
459
518
|
}
|
|
519
|
+
|
|
460
520
|
const query = this.query;
|
|
461
521
|
const epoch = this.epoch;
|
|
462
522
|
const snapshot = this.snapshot();
|
|
523
|
+
|
|
463
524
|
if (command) {
|
|
464
525
|
this.captureFailed = undefined;
|
|
465
526
|
}
|
|
527
|
+
|
|
466
528
|
const current = (): boolean =>
|
|
467
529
|
!this.disposed &&
|
|
468
530
|
epoch === this.epoch &&
|
|
469
531
|
this.model.getRevision() === snapshot.revision &&
|
|
470
532
|
this.currentMark?.from === mark.from &&
|
|
471
533
|
this.currentMark?.to === mark.to;
|
|
534
|
+
|
|
472
535
|
try {
|
|
473
536
|
const found = await this.search.request(
|
|
474
537
|
(client) => {
|
|
475
538
|
if (!current()) {
|
|
476
539
|
throw new EditorSearchError('superseded', 'The current find match changed.');
|
|
477
540
|
}
|
|
541
|
+
|
|
478
542
|
return client.match(snapshot, query, { ...mark, deadline: 10000 });
|
|
479
543
|
},
|
|
480
544
|
command ? 'command' : 'preview'
|
|
481
545
|
);
|
|
546
|
+
|
|
482
547
|
if (!current()) {
|
|
483
548
|
return null;
|
|
484
549
|
}
|
|
550
|
+
|
|
485
551
|
this.retained = found;
|
|
486
552
|
this.captureFailed = undefined;
|
|
487
553
|
this.options?.changed();
|
|
554
|
+
|
|
488
555
|
return found;
|
|
489
556
|
} catch (error) {
|
|
490
557
|
if (current() && !(error instanceof EditorSearchError && error.kind === 'superseded')) {
|
|
491
558
|
this.captureFailed = error instanceof EditorSearchError && (error.kind === 'timeout' || error.kind === 'unavailable') ? error.kind : 'error';
|
|
492
559
|
this.options?.changed();
|
|
493
560
|
}
|
|
561
|
+
|
|
494
562
|
return null;
|
|
495
563
|
}
|
|
496
564
|
}
|
|
@@ -498,9 +566,12 @@ export class FindController {
|
|
|
498
566
|
private chooseCurrent(anchor: number, direction: 1 | -1, limited: boolean): void {
|
|
499
567
|
if (this.marks.length === 0) {
|
|
500
568
|
this.index = null;
|
|
569
|
+
|
|
501
570
|
return;
|
|
502
571
|
}
|
|
572
|
+
|
|
503
573
|
const next = direction === 1 ? this.marks.findIndex((mark) => mark.from >= anchor) : this.marks.findLastIndex((mark) => mark.from < anchor);
|
|
574
|
+
|
|
504
575
|
if (limited && this.search !== undefined && (next === -1 || (direction === -1 && next === this.marks.length - 1))) {
|
|
505
576
|
this.index = null;
|
|
506
577
|
this.seek(anchor, direction);
|
|
@@ -511,34 +582,43 @@ export class FindController {
|
|
|
511
582
|
|
|
512
583
|
private seek(offset: number, direction: 1 | -1): void {
|
|
513
584
|
const query = this.query;
|
|
585
|
+
|
|
514
586
|
if (this.search === undefined || query === null) {
|
|
515
587
|
return;
|
|
516
588
|
}
|
|
589
|
+
|
|
517
590
|
const epoch = this.epoch;
|
|
518
591
|
const snapshot = this.snapshot();
|
|
519
592
|
const scope = this.scope;
|
|
593
|
+
|
|
520
594
|
const valid = (): boolean => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision();
|
|
595
|
+
|
|
521
596
|
this.seeking = true;
|
|
522
597
|
this.failed = undefined;
|
|
523
598
|
this.retained = null;
|
|
524
599
|
this.captureFailed = undefined;
|
|
600
|
+
|
|
525
601
|
void this.search
|
|
526
602
|
.request((client) => client.next(snapshot, query, offset, { ...scope, backwards: direction === -1, wrap: true, deadline: 10000 }), 'command', valid)
|
|
527
603
|
.then((range) => {
|
|
528
604
|
if (!valid()) {
|
|
529
605
|
return;
|
|
530
606
|
}
|
|
607
|
+
|
|
531
608
|
this.seeking = false;
|
|
532
609
|
this.outside = null;
|
|
533
610
|
this.index = range === null ? null : this.marks.findIndex((mark) => mark.from === range.from && mark.to === range.to);
|
|
611
|
+
|
|
534
612
|
if (this.index === -1) {
|
|
535
613
|
this.index = null;
|
|
536
614
|
this.outside = range;
|
|
537
615
|
}
|
|
616
|
+
|
|
538
617
|
this.options?.changed(true);
|
|
539
618
|
this.preparePreview();
|
|
540
619
|
const pending = this.pendingStep;
|
|
541
620
|
this.pendingStep = undefined;
|
|
621
|
+
|
|
542
622
|
if (pending !== undefined) {
|
|
543
623
|
this.step(pending);
|
|
544
624
|
}
|
|
@@ -556,25 +636,32 @@ export class FindController {
|
|
|
556
636
|
if (this.search === undefined) {
|
|
557
637
|
return null;
|
|
558
638
|
}
|
|
639
|
+
|
|
559
640
|
const snapshot = this.snapshot();
|
|
560
641
|
const epoch = this.epoch;
|
|
642
|
+
|
|
561
643
|
const valid = (): boolean => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision();
|
|
644
|
+
|
|
562
645
|
this.cursorRequests++;
|
|
646
|
+
|
|
563
647
|
try {
|
|
564
648
|
const range = await this.search.request(
|
|
565
649
|
(client) => client.next(snapshot, query, offset, { backwards: direction === -1, wrap: true, deadline: 10000 }),
|
|
566
650
|
'command',
|
|
567
651
|
valid
|
|
568
652
|
);
|
|
653
|
+
|
|
569
654
|
return valid() ? range : null;
|
|
570
655
|
} catch (error) {
|
|
571
656
|
if (valid()) {
|
|
572
657
|
this.failed = searchFailure(error);
|
|
573
658
|
this.options?.changed();
|
|
574
659
|
}
|
|
660
|
+
|
|
575
661
|
return null;
|
|
576
662
|
} finally {
|
|
577
663
|
this.cursorRequests--;
|
|
664
|
+
|
|
578
665
|
if (this.query === null && this.cursorRequests === 0) {
|
|
579
666
|
this.search.release();
|
|
580
667
|
}
|
|
@@ -594,13 +681,16 @@ export class FindController {
|
|
|
594
681
|
}
|
|
595
682
|
|
|
596
683
|
async replaceOne(replacement: string, preserveCase: boolean, canApply: () => boolean): Promise<boolean> {
|
|
597
|
-
const query = this.query
|
|
598
|
-
|
|
684
|
+
const query = this.query;
|
|
685
|
+
const mark = this.currentMark;
|
|
686
|
+
|
|
599
687
|
if (query === null || mark === null || this.search === undefined || !canApply()) {
|
|
600
688
|
return false;
|
|
601
689
|
}
|
|
690
|
+
|
|
602
691
|
const snapshot = this.snapshot();
|
|
603
692
|
const epoch = this.epoch;
|
|
693
|
+
|
|
604
694
|
const valid = (): boolean =>
|
|
605
695
|
!this.disposed &&
|
|
606
696
|
epoch === this.epoch &&
|
|
@@ -608,10 +698,12 @@ export class FindController {
|
|
|
608
698
|
canApply() &&
|
|
609
699
|
this.currentMark?.from === mark.from &&
|
|
610
700
|
this.currentMark?.to === mark.to;
|
|
701
|
+
|
|
611
702
|
this.replacing++;
|
|
612
703
|
this.replaceEpoch = epoch;
|
|
613
704
|
this.replaceFailed = undefined;
|
|
614
705
|
this.options?.changed();
|
|
706
|
+
|
|
615
707
|
try {
|
|
616
708
|
const page = await this.search.request(
|
|
617
709
|
(client) =>
|
|
@@ -626,26 +718,32 @@ export class FindController {
|
|
|
626
718
|
'command',
|
|
627
719
|
valid
|
|
628
720
|
);
|
|
721
|
+
|
|
629
722
|
if (!valid() || page.ranges.length !== 3 || page.ranges[0] !== mark.from || page.ranges[1] !== mark.to || page.next !== null) {
|
|
630
723
|
return false;
|
|
631
724
|
}
|
|
725
|
+
|
|
632
726
|
this.pendingAnchor = mark.to;
|
|
727
|
+
|
|
633
728
|
const changed = this.model.applyEdits([{ from: mark.from, to: mark.to, text: page.text }], {
|
|
634
729
|
source: 'command',
|
|
635
730
|
expectedRevision: snapshot.revision
|
|
636
731
|
});
|
|
732
|
+
|
|
637
733
|
if (!changed) {
|
|
638
734
|
this.pendingAnchor = undefined;
|
|
639
735
|
}
|
|
736
|
+
|
|
640
737
|
return changed;
|
|
641
738
|
} catch (error) {
|
|
642
739
|
if (valid()) {
|
|
643
|
-
|
|
644
|
-
this.reportReplaceFailed(error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure);
|
|
740
|
+
this.reportReplaceFailed(replaceFailure(error));
|
|
645
741
|
}
|
|
742
|
+
|
|
646
743
|
return false;
|
|
647
744
|
} finally {
|
|
648
745
|
this.replacing--;
|
|
746
|
+
|
|
649
747
|
if (!this.disposed) {
|
|
650
748
|
this.options?.changed();
|
|
651
749
|
}
|
|
@@ -654,18 +752,23 @@ export class FindController {
|
|
|
654
752
|
|
|
655
753
|
async replaceAll(replacement: string, preserveCase: boolean, canApply: () => boolean): Promise<number> {
|
|
656
754
|
const query = this.query;
|
|
755
|
+
|
|
657
756
|
if (query === null || this.search === undefined || this.state.noSelection || !canApply()) {
|
|
658
757
|
return 0;
|
|
659
758
|
}
|
|
759
|
+
|
|
660
760
|
const snapshot = this.snapshot();
|
|
661
761
|
const epoch = this.epoch;
|
|
662
762
|
const scope = this.scope;
|
|
763
|
+
|
|
663
764
|
const valid = (): boolean => !this.disposed && epoch === this.epoch && snapshot.revision === this.model.getRevision() && canApply();
|
|
765
|
+
|
|
664
766
|
this.replacing++;
|
|
665
767
|
this.replaceEpoch = epoch;
|
|
666
768
|
this.replaceFailed = undefined;
|
|
667
769
|
this.replaced = undefined;
|
|
668
770
|
this.options?.changed();
|
|
771
|
+
|
|
669
772
|
try {
|
|
670
773
|
const page = await this.search.request(
|
|
671
774
|
(client) =>
|
|
@@ -679,32 +782,40 @@ export class FindController {
|
|
|
679
782
|
'command',
|
|
680
783
|
valid
|
|
681
784
|
);
|
|
785
|
+
|
|
682
786
|
if (!valid()) {
|
|
683
787
|
return 0;
|
|
684
788
|
}
|
|
789
|
+
|
|
685
790
|
if (page.next !== null) {
|
|
686
791
|
this.replaceFailed = 'limit';
|
|
792
|
+
|
|
687
793
|
return 0;
|
|
688
794
|
}
|
|
795
|
+
|
|
689
796
|
const edits: TextEdit[] = [];
|
|
690
797
|
let position = 0;
|
|
798
|
+
|
|
691
799
|
for (let index = 0; index < page.ranges.length; index += 3) {
|
|
692
800
|
const length = page.ranges[index + 2]!;
|
|
693
801
|
edits.push({ from: page.ranges[index]!, to: page.ranges[index + 1]!, text: page.text.slice(position, position + length) });
|
|
694
802
|
position += length;
|
|
695
803
|
}
|
|
804
|
+
|
|
696
805
|
const changed = this.model.applyEdits(edits, { source: 'command', expectedRevision: snapshot.revision });
|
|
697
806
|
const count = changed ? edits.length : 0;
|
|
698
807
|
this.reportReplaced(count);
|
|
808
|
+
|
|
699
809
|
return count;
|
|
700
810
|
} catch (error) {
|
|
701
811
|
if (!this.disposed && epoch === this.epoch) {
|
|
702
|
-
|
|
703
|
-
this.replaceFailed = error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure;
|
|
812
|
+
this.replaceFailed = replaceFailure(error);
|
|
704
813
|
}
|
|
814
|
+
|
|
705
815
|
return 0;
|
|
706
816
|
} finally {
|
|
707
817
|
this.replacing--;
|
|
818
|
+
|
|
708
819
|
if (!this.disposed) {
|
|
709
820
|
this.options?.changed();
|
|
710
821
|
}
|
|
@@ -727,3 +838,13 @@ function searchFailure(error: unknown): NonNullable<EditorFindState['failed']> {
|
|
|
727
838
|
: 'error'
|
|
728
839
|
: 'error';
|
|
729
840
|
}
|
|
841
|
+
|
|
842
|
+
function replaceFailure(error: unknown): NonNullable<EditorFindState['replaceFailed']> {
|
|
843
|
+
if (error instanceof EditorSearchError && error.kind === 'range') {
|
|
844
|
+
return 'limit';
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const failure = searchFailure(error);
|
|
848
|
+
|
|
849
|
+
return failure === 'invalid' ? 'error' : failure;
|
|
850
|
+
}
|
package/src/geometry-stops.ts
CHANGED
|
@@ -12,16 +12,20 @@ export function rowOfStop(geometry: LineGeometry, stop: number): number {
|
|
|
12
12
|
if (geometry.uniform === undefined) {
|
|
13
13
|
return geometry.rowOf[stop]!;
|
|
14
14
|
}
|
|
15
|
+
|
|
15
16
|
let first = 0;
|
|
16
17
|
let last = geometry.rowStarts.length;
|
|
18
|
+
|
|
17
19
|
while (first < last) {
|
|
18
20
|
const middle = (first + last) >>> 1;
|
|
21
|
+
|
|
19
22
|
if (geometry.rowStarts[middle]! <= stop) {
|
|
20
23
|
first = middle + 1;
|
|
21
24
|
} else {
|
|
22
25
|
last = middle;
|
|
23
26
|
}
|
|
24
27
|
}
|
|
28
|
+
|
|
25
29
|
return Math.max(0, first - 1);
|
|
26
30
|
}
|
|
27
31
|
|
|
@@ -29,7 +33,9 @@ export function xOfStop(geometry: LineGeometry, stop: number, affinity: Affinity
|
|
|
29
33
|
if (geometry.uniform === undefined) {
|
|
30
34
|
return geometry[affinity][stop]!;
|
|
31
35
|
}
|
|
36
|
+
|
|
32
37
|
const row = rowOfStop(geometry, stop);
|
|
38
|
+
|
|
33
39
|
return geometry.uniform[row === 0 ? 'first' : 'continuation'][stop - geometry.rowStarts[row]!]!;
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -37,15 +43,19 @@ export function stopAtOffset(geometry: LineGeometry, offset: number): number {
|
|
|
37
43
|
if (geometry.uniform !== undefined) {
|
|
38
44
|
return Math.min(geometry.text.length, Math.max(0, Math.ceil(offset)));
|
|
39
45
|
}
|
|
46
|
+
|
|
40
47
|
let first = 0;
|
|
41
48
|
let last = geometry.offsets.length;
|
|
49
|
+
|
|
42
50
|
while (first < last) {
|
|
43
51
|
const middle = (first + last) >>> 1;
|
|
52
|
+
|
|
44
53
|
if (geometry.offsets[middle]! < offset) {
|
|
45
54
|
first = middle + 1;
|
|
46
55
|
} else {
|
|
47
56
|
last = middle;
|
|
48
57
|
}
|
|
49
58
|
}
|
|
59
|
+
|
|
50
60
|
return Math.min(geometry.offsets.length - 1, first);
|
|
51
61
|
}
|