@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.
Files changed (53) hide show
  1. package/dist/controller.d.ts +1 -0
  2. package/dist/controller.js +17 -28
  3. package/dist/engine.js +2 -9
  4. package/dist/find.js +12 -13
  5. package/dist/layout.js +7 -2
  6. package/dist/paint.d.ts +2 -0
  7. package/dist/paint.js +23 -20
  8. package/dist/view.d.ts +3 -2
  9. package/dist/view.js +46 -59
  10. package/package.json +6 -6
  11. package/src/attribution.ts +14 -0
  12. package/src/code-block.ts +25 -0
  13. package/src/column-selection.ts +10 -0
  14. package/src/controller.ts +189 -28
  15. package/src/engine.ts +87 -9
  16. package/src/fake.ts +79 -0
  17. package/src/find-search.ts +20 -0
  18. package/src/find.ts +135 -14
  19. package/src/geometry-stops.ts +10 -0
  20. package/src/grammar-preparation.ts +23 -0
  21. package/src/index.ts +1 -0
  22. package/src/input.ts +13 -0
  23. package/src/keymap-table.ts +9 -0
  24. package/src/keymap.ts +25 -0
  25. package/src/layout-changes.ts +7 -0
  26. package/src/layout.ts +219 -3
  27. package/src/lens.ts +3 -0
  28. package/src/listeners.ts +1 -0
  29. package/src/metrics.ts +14 -0
  30. package/src/modifier-gesture.ts +17 -0
  31. package/src/native-input.ts +27 -0
  32. package/src/offsets.ts +10 -0
  33. package/src/outline.ts +38 -0
  34. package/src/overview.ts +13 -0
  35. package/src/paint.ts +140 -20
  36. package/src/php-html.ts +1 -0
  37. package/src/pointer.ts +50 -0
  38. package/src/scroll-animation.ts +10 -0
  39. package/src/scroll.ts +7 -0
  40. package/src/search-client.ts +32 -0
  41. package/src/search-worker.ts +38 -0
  42. package/src/semantic.ts +12 -0
  43. package/src/shiki-document.ts +19 -0
  44. package/src/shiki-worker.ts +68 -0
  45. package/src/shiki.ts +37 -0
  46. package/src/testing.ts +17 -0
  47. package/src/theme-scopes.ts +14 -0
  48. package/src/tokens.ts +31 -0
  49. package/src/touch.ts +16 -0
  50. package/src/tracked-range.ts +5 -0
  51. package/src/uniform-geometry.ts +10 -0
  52. package/src/view.ts +481 -75
  53. package/src/worker-client.ts +54 -0
package/src/fake.ts CHANGED
@@ -77,11 +77,14 @@ const LINE_BREAK = /\r\n|\r|\n/g;
77
77
  function linesOf(text: string): { start: number; end: number }[] {
78
78
  const lines: { start: number; end: number }[] = [];
79
79
  let start = 0;
80
+
80
81
  for (const match of text.matchAll(LINE_BREAK)) {
81
82
  lines.push({ start, end: match.index });
82
83
  start = match.index + match[0].length;
83
84
  }
85
+
84
86
  lines.push({ start, end: text.length });
87
+
85
88
  return lines;
86
89
  }
87
90
 
@@ -98,6 +101,7 @@ function clampOffset(text: string, value: number): number {
98
101
  const offset = Math.max(0, Math.min(text.length, Number.isFinite(value) ? Math.trunc(value) : 0));
99
102
  const splitsBreak = text[offset - 1] === '\r' && text[offset] === '\n';
100
103
  const splitsPair = isHighSurrogate(text.charCodeAt(offset - 1)) && isLowSurrogate(text.charCodeAt(offset));
104
+
101
105
  return splitsBreak || splitsPair ? offset - 1 : offset;
102
106
  }
103
107
 
@@ -105,9 +109,11 @@ function positionIn(text: string, offset: number): EditorPosition {
105
109
  const at = clampOffset(text, offset);
106
110
  const lines = linesOf(text);
107
111
  let line = lines.length - 1;
112
+
108
113
  while (line > 0 && lines[line]!.start > at) {
109
114
  line--;
110
115
  }
116
+
111
117
  return { line, character: Math.min(at, lines[line]!.end) - lines[line]!.start };
112
118
  }
113
119
 
@@ -115,28 +121,36 @@ function offsetIn(text: string, position: EditorPosition): number {
115
121
  const lines = linesOf(text);
116
122
  const line = lines[Math.max(0, Math.min(Number.isFinite(position.line) ? Math.trunc(position.line) : 0, lines.length - 1))]!;
117
123
  const character = Math.max(0, Math.min(Number.isFinite(position.character) ? Math.trunc(position.character) : 0, line.end - line.start));
124
+
118
125
  return clampOffset(text, line.start + character);
119
126
  }
120
127
 
121
128
  /* The line of a one-based line number, clamped to the text. */
122
129
  function lineNumbered(text: string, line: number): { start: number; end: number } {
123
130
  const lines = linesOf(text);
131
+
124
132
  return lines[Math.min(Math.max(1, Math.trunc(line) || 1), lines.length) - 1]!;
125
133
  }
126
134
 
127
135
  /* Clamps every selection and merges those that overlap or touch, each merge taking the place and direction of the last given of them, as the real document does. */
128
136
  function normalized(text: string, selections: readonly OffsetSelection[]): OffsetSelection[] {
129
137
  const input = selections.length > 0 ? selections : [{ anchor: 0, head: 0 }];
138
+
130
139
  const clamped = input.map((raw) => {
131
140
  const anchor = clampOffset(text, raw.anchor);
141
+
132
142
  return { anchor, head: raw.head === raw.anchor ? anchor : clampOffset(text, raw.head) };
133
143
  });
144
+
134
145
  const order = clamped
135
146
  .map((selection, index) => ({ from: Math.min(selection.anchor, selection.head), to: Math.max(selection.anchor, selection.head), index }))
136
147
  .sort((left, right) => left.from - right.from);
148
+
137
149
  const groups: { from: number; to: number; last: number }[] = [];
150
+
138
151
  for (const { from, to, index } of order) {
139
152
  const group = groups.at(-1);
153
+
140
154
  if (group !== undefined && from <= group.to) {
141
155
  group.to = Math.max(group.to, to);
142
156
  group.last = Math.max(group.last, index);
@@ -144,10 +158,12 @@ function normalized(text: string, selections: readonly OffsetSelection[]): Offse
144
158
  groups.push({ from, to, last: index });
145
159
  }
146
160
  }
161
+
147
162
  return groups
148
163
  .sort((left, right) => left.last - right.last)
149
164
  .map((group) => {
150
165
  const selection = clamped[group.last]!;
166
+
151
167
  return selection.anchor > selection.head ? { anchor: group.to, head: group.from } : { anchor: group.from, head: group.to };
152
168
  });
153
169
  }
@@ -156,28 +172,36 @@ function normalized(text: string, selections: readonly OffsetSelection[]): Offse
156
172
  function orderedEdits(edits: readonly Edit[]): Edit[] | null {
157
173
  const sorted = [...edits].sort((left, right) => left.from - right.from || left.to - right.to);
158
174
  const unique: Edit[] = [];
175
+
159
176
  for (const edit of sorted) {
160
177
  const previous = unique.at(-1);
178
+
161
179
  if (previous !== undefined && edit.from === previous.from && edit.to === previous.to && edit.text === previous.text) {
162
180
  continue;
163
181
  }
182
+
164
183
  if (previous !== undefined && (edit.from < previous.to || (edit.from === previous.from && edit.to === previous.to))) {
165
184
  return null;
166
185
  }
186
+
167
187
  unique.push(edit);
168
188
  }
189
+
169
190
  return unique;
170
191
  }
171
192
 
172
193
  /* Where an offset lands after ordered edits: one inside a replaced stretch, or at an insertion, ends up behind the new text. */
173
194
  function mapOffset(offset: number, edits: readonly Edit[]): number {
174
195
  let delta = 0;
196
+
175
197
  for (const edit of edits) {
176
198
  if (edit.to > offset) {
177
199
  return edit.from <= offset ? edit.from + delta + edit.text.length : offset + delta;
178
200
  }
201
+
179
202
  delta += edit.text.length - (edit.to - edit.from);
180
203
  }
204
+
181
205
  return offset + delta;
182
206
  }
183
207
 
@@ -233,6 +257,7 @@ export class FakeEditor implements Editor {
233
257
  this.revealedLine = options.line ?? null;
234
258
  this.column = options.column ?? null;
235
259
  this.scrollTop = options.scrollTop ?? null;
260
+
236
261
  if (options.line !== undefined) {
237
262
  const line = lineNumbered(this.text, options.line);
238
263
  const offset = Math.min(line.end, line.start + Math.max(0, (options.column ?? 1) - 1));
@@ -245,14 +270,18 @@ export class FakeEditor implements Editor {
245
270
  type(text: string): void {
246
271
  this.assertLive();
247
272
  const span = changedSpan(this.text, text);
273
+
248
274
  if (this.readOnly || span === null) {
249
275
  return;
250
276
  }
277
+
251
278
  const edit = { from: span.start, to: span.end, text: span.text };
252
279
  const end = span.start + span.text.length;
280
+
253
281
  const others = this.selections
254
282
  .slice(0, -1)
255
283
  .map((selection) => ({ anchor: mapOffset(selection.anchor, [edit]), head: mapOffset(selection.head, [edit]) }));
284
+
256
285
  this.edit([edit], 'input', false, () => [...others, { anchor: end, head: end }]);
257
286
  }
258
287
 
@@ -292,41 +321,53 @@ export class FakeEditor implements Editor {
292
321
  place?: (text: string) => readonly OffsetSelection[]
293
322
  ): boolean {
294
323
  const before = this.state();
324
+
295
325
  if (!this.rewrite(edits, source, place)) {
296
326
  return false;
297
327
  }
328
+
298
329
  const last = this.undoStack.at(-1);
330
+
299
331
  if (mergeUndo && last !== undefined && last.after.text === before.text) {
300
332
  last.after = this.state();
301
333
  } else {
302
334
  this.undoStack.push({ before, after: this.state() });
303
335
  }
336
+
304
337
  this.redoStack = [];
338
+
305
339
  if (source !== 'external') {
306
340
  emit(this.changes);
307
341
  }
342
+
308
343
  return true;
309
344
  }
310
345
 
311
346
  private rewrite(edits: readonly Edit[], source: EditorTextChange['source'], place?: (text: string) => readonly OffsetSelection[]): boolean {
312
347
  const before = this.text;
313
348
  let text = before;
349
+
314
350
  for (let index = edits.length - 1; index >= 0; index--) {
315
351
  const edit = edits[index]!;
316
352
  text = text.slice(0, edit.from) + edit.text + text.slice(edit.to);
317
353
  }
354
+
318
355
  if (text === before) {
319
356
  return false;
320
357
  }
358
+
321
359
  const changed = edits.filter((edit) => before.slice(edit.from, edit.to) !== edit.text);
322
360
  this.text = text;
361
+
323
362
  this.selections = normalized(
324
363
  text,
325
364
  place?.(text) ?? this.selections.map((selection) => ({ anchor: mapOffset(selection.anchor, edits), head: mapOffset(selection.head, edits) }))
326
365
  );
366
+
327
367
  this.followTracked(changed);
328
368
  this.ghost = null;
329
369
  this.announceCaret();
370
+
330
371
  // Last to first, so each change's range is also where it stands once the ones after it are applied, as the real editor reports them.
331
372
  const change: EditorTextChange = {
332
373
  source,
@@ -334,7 +375,9 @@ export class FakeEditor implements Editor {
334
375
  .reverse()
335
376
  .map((edit) => ({ range: { start: positionIn(before, edit.from), end: positionIn(before, edit.to) }, text: edit.text }))
336
377
  };
378
+
337
379
  emit(this.textChanges, change);
380
+
338
381
  return true;
339
382
  }
340
383
 
@@ -346,24 +389,30 @@ export class FakeEditor implements Editor {
346
389
  private restore(direction: 'undo' | 'redo'): boolean {
347
390
  const [from, to] = direction === 'undo' ? [this.undoStack, this.redoStack] : [this.redoStack, this.undoStack];
348
391
  const entry = this.readOnly ? undefined : from.pop();
392
+
349
393
  if (entry === undefined) {
350
394
  return false;
351
395
  }
396
+
352
397
  to.push(entry);
353
398
  const state = direction === 'undo' ? entry.before : entry.after;
354
399
  const span = changedSpan(this.text, state.text);
400
+
355
401
  if (span === null) {
356
402
  this.select(state.selections);
357
403
  } else {
358
404
  this.rewrite([{ from: span.start, to: span.end, text: span.text }], 'command', () => state.selections);
359
405
  }
406
+
360
407
  emit(this.changes);
408
+
361
409
  return true;
362
410
  }
363
411
 
364
412
  /* Tells the caret listeners where the primary caret is, when it moved since they last heard. */
365
413
  private announceCaret(): void {
366
414
  const head = this.selections.at(-1)!.head;
415
+
367
416
  if (head !== this.caretOffset) {
368
417
  this.caretOffset = head;
369
418
  emit(this.carets, positionIn(this.text, head));
@@ -374,8 +423,10 @@ export class FakeEditor implements Editor {
374
423
 
375
424
  private followTracked(edits: readonly Edit[]): void {
376
425
  const changes = edits.map((edit) => ({ from: edit.from, to: edit.to, insertedLength: edit.text.length }));
426
+
377
427
  for (const entry of this.tracked) {
378
428
  const mapped = mapTrackedRange(entry.from, entry.to, changes);
429
+
379
430
  if (mapped === null) {
380
431
  this.tracked.delete(entry);
381
432
  entry.lost = true;
@@ -388,6 +439,7 @@ export class FakeEditor implements Editor {
388
439
  trackRange(range: EditorRange): EditorTrackedRange {
389
440
  const entry = { from: this.offsetAt(range.start), to: this.offsetAt(range.end), lost: false };
390
441
  this.tracked.add(entry);
442
+
391
443
  return {
392
444
  get: () => (entry.lost ? null : { start: this.positionAt(entry.from), end: this.positionAt(entry.to) }),
393
445
  dispose: () => {
@@ -458,12 +510,14 @@ export class FakeEditor implements Editor {
458
510
  replace(replacement: string, options: EditorReplaceOptions = {}): boolean {
459
511
  this.replacements.push(replacement);
460
512
  this.replaceOptions.push(options);
513
+
461
514
  return !this.readOnly && this.findState.count > 0;
462
515
  }
463
516
 
464
517
  replaceAll(replacement: string, options: EditorReplaceOptions = {}): number {
465
518
  this.replacementsOfAll.push(replacement);
466
519
  this.replaceOptions.push(options);
520
+
467
521
  return this.readOnly ? 0 : this.findState.count;
468
522
  }
469
523
 
@@ -478,16 +532,19 @@ export class FakeEditor implements Editor {
478
532
  selectFindMatches(): number {
479
533
  this.selectedMatches = this.findState.count;
480
534
  this.find(null);
535
+
481
536
  return this.selectedMatches;
482
537
  }
483
538
 
484
539
  findFromCursor(query: EditorFindQuery, direction: 1 | -1): boolean {
485
540
  this.lastFindFromCursor = { query, direction };
541
+
486
542
  return true;
487
543
  }
488
544
 
489
545
  findStep(direction: 1 | -1): void {
490
546
  const { count, current } = this.findState;
547
+
491
548
  if (count > 0) {
492
549
  this.announceFind({ count, current: ((current ?? 0) + direction + count) % count });
493
550
  }
@@ -597,10 +654,12 @@ export class FakeEditor implements Editor {
597
654
  /* The pointer on the bar of a mark, or leaving it. The rect is where `rectAt` puts the mark's first line unless given. */
598
655
  hoverAttribution(id: string | null, rect?: EditorRect): void {
599
656
  const mark = id === null ? undefined : this.attributionMarks.find((candidate) => candidate.id === id);
657
+
600
658
  const hover =
601
659
  mark === undefined
602
660
  ? null
603
661
  : { id: mark.id, rect: rect ?? this.rectAt({ line: mark.startLine - 1, character: 0 }) ?? { left: 0, top: 0, right: 0, bottom: 0 } };
662
+
604
663
  emit(this.attributionHovers, hover);
605
664
  }
606
665
 
@@ -619,15 +678,19 @@ export class FakeEditor implements Editor {
619
678
  if (widgets.length === 0) {
620
679
  this.widgetsByOwner.delete(owner);
621
680
  this.drawnWidgets.delete(owner);
681
+
622
682
  return;
623
683
  }
684
+
624
685
  this.widgetsByOwner.set(owner, widgets);
625
686
  const before = this.drawnWidgets.get(owner) ?? new Set<string>();
687
+
626
688
  for (const widget of widgets) {
627
689
  if (!before.has(widget.id)) {
628
690
  widget.render(this.element.ownerDocument?.createElement('div') ?? ({} as HTMLElement));
629
691
  }
630
692
  }
693
+
631
694
  this.drawnWidgets.set(owner, new Set(widgets.map((widget) => widget.id)));
632
695
  }
633
696
 
@@ -804,6 +867,7 @@ export class FakeEditor implements Editor {
804
867
  /* A press on a hint as the editor sees it first; true when a handler took it. */
805
868
  clickInlay(click: Partial<EditorInlayClick> & Pick<EditorInlayClick, 'id'>): boolean {
806
869
  const full: EditorInlayClick = { part: null, mod: false, alt: false, shift: false, clicks: 1, touch: false, ...click };
870
+
807
871
  return [...this.inlayHandlers].some((handler) => handler(full));
808
872
  }
809
873
 
@@ -864,6 +928,7 @@ export class FakeEditor implements Editor {
864
928
  /* The primary selection, or null when it is only a caret; setting it tells no listener. */
865
929
  get selection(): EditorRange | null {
866
930
  const primary = this.selections.at(-1)!;
931
+
867
932
  return primary.anchor === primary.head ? null : rangeIn(this.text, primary);
868
933
  }
869
934
 
@@ -920,6 +985,7 @@ export class FakeEditor implements Editor {
920
985
  if (ranges.length === 0) {
921
986
  return;
922
987
  }
988
+
923
989
  this.lastReveal = reveal;
924
990
  this.select(ranges.map((range) => ({ anchor: this.offsetAt(range.start), head: this.offsetAt(range.end) })));
925
991
  }
@@ -970,6 +1036,7 @@ export class FakeEditor implements Editor {
970
1036
  rectAt(position: EditorPosition): EditorRect | null {
971
1037
  const left = this.origin.left + position.character * this.charSize.width;
972
1038
  const top = this.origin.top + position.line * this.charSize.height;
1039
+
973
1040
  return { left, top, right: left + this.charSize.width, bottom: top + this.charSize.height };
974
1041
  }
975
1042
 
@@ -989,6 +1056,7 @@ export class FakeEditor implements Editor {
989
1056
  if (range !== undefined) {
990
1057
  this.visibleRange = range;
991
1058
  }
1059
+
992
1060
  emit(this.views);
993
1061
  }
994
1062
 
@@ -1027,18 +1095,23 @@ export class FakeEditor implements Editor {
1027
1095
  /* A key as the editor sees it first; true when a handler took it. */
1028
1096
  press(event: Pick<KeyboardEvent, 'key'> & Partial<KeyboardEvent>): boolean {
1029
1097
  const full = { ctrlKey: false, metaKey: false, altKey: false, shiftKey: false, preventDefault: () => undefined, ...event } as KeyboardEvent;
1098
+
1030
1099
  return [...this.keys].some((handler) => handler(full));
1031
1100
  }
1032
1101
 
1033
1102
  applyEdits(edits: readonly EditorContentChange[], options: EditorApplyEditsOptions = {}): boolean {
1034
1103
  const ordered = orderedEdits(edits.map((edit) => ({ from: this.offsetAt(edit.range.start), to: this.offsetAt(edit.range.end), text: edit.text })));
1104
+
1035
1105
  if (this.readOnly || ordered === null || ordered.length === 0) {
1036
1106
  return false;
1037
1107
  }
1108
+
1038
1109
  const { mapSelections } = options;
1110
+
1039
1111
  const mapped =
1040
1112
  mapSelections &&
1041
1113
  this.selections.map((selection) => ({ backward: selection.head < selection.anchor, range: mapSelections(rangeIn(this.text, selection)) }));
1114
+
1042
1115
  // A backward selection stays backward, as the real editor places it.
1043
1116
  const place =
1044
1117
  mapped &&
@@ -1046,8 +1119,10 @@ export class FakeEditor implements Editor {
1046
1119
  mapped.map(({ backward, range }) => {
1047
1120
  const from = offsetIn(text, range.start);
1048
1121
  const to = offsetIn(text, range.end);
1122
+
1049
1123
  return backward ? { anchor: to, head: from } : { anchor: from, head: to };
1050
1124
  }));
1125
+
1051
1126
  return this.edit(ordered, 'command', options.mergeUndo === true, place);
1052
1127
  }
1053
1128
 
@@ -1073,6 +1148,7 @@ export class FakeEditor implements Editor {
1073
1148
 
1074
1149
  runCommand(command: EditorRunCommand): boolean {
1075
1150
  this.commands.push(command);
1151
+
1076
1152
  return command === 'undo' || command === 'redo' ? this.restore(command) : false;
1077
1153
  }
1078
1154
 
@@ -1118,15 +1194,18 @@ export class FakeEditorEngine implements EditorEngine {
1118
1194
  mount(element: HTMLElement, options: EditorOptions): FakeEditor {
1119
1195
  const editor = new FakeEditor(element, options);
1120
1196
  this.editors.push(editor);
1197
+
1121
1198
  return editor;
1122
1199
  }
1123
1200
 
1124
1201
  /* The editor mounted last, which is the one a test of a single surface drives. */
1125
1202
  get last(): FakeEditor {
1126
1203
  const editor = this.editors.at(-1);
1204
+
1127
1205
  if (!editor) {
1128
1206
  throw new Error('No editor was mounted');
1129
1207
  }
1208
+
1130
1209
  return editor;
1131
1210
  }
1132
1211
  }
@@ -19,6 +19,7 @@ interface SearchJob {
19
19
 
20
20
  export function scheduleFind(callback: () => void, delay: number): () => void {
21
21
  const timer = setTimeout(callback, delay);
22
+
22
23
  return () => clearTimeout(timer);
23
24
  }
24
25
 
@@ -53,17 +54,22 @@ export class FindSearchQueue {
53
54
  if (this.disposed) {
54
55
  return Promise.reject(new EditorSearchError('disposed', 'The find session was disposed.'));
55
56
  }
57
+
56
58
  return new Promise((resolve, reject) => {
57
59
  this.pending = this.pending.filter((job) => {
58
60
  if (job.kind !== 'command' && (job.kind === kind || (kind !== 'preview' && job.kind === 'preview'))) {
59
61
  job.cancelled = true;
60
62
  job.reject(superseded());
63
+
61
64
  return false;
62
65
  }
66
+
63
67
  return true;
64
68
  });
69
+
65
70
  this.pending.push({ kind, valid, run, resolve: (value) => resolve(value as Result), reject, cancelled: false });
66
71
  this.pending.sort((left, right) => priority(left.kind) - priority(right.kind));
72
+
67
73
  if (this.active === undefined) {
68
74
  this.pump();
69
75
  } else if (
@@ -74,6 +80,7 @@ export class FindSearchQueue {
74
80
  this.cancelTimer = (this.options.schedule ?? scheduleFind)(
75
81
  () => {
76
82
  this.cancelTimer = undefined;
83
+
77
84
  if (this.pending.length > 0 && this.active !== undefined && (this.active.kind !== 'command' || this.active.valid?.() === false)) {
78
85
  this.active.cancelled = true;
79
86
  this.active.reject(superseded());
@@ -94,25 +101,35 @@ export class FindSearchQueue {
94
101
  if (this.disposed || this.active !== undefined) {
95
102
  return;
96
103
  }
104
+
97
105
  const job = this.pending.shift();
106
+
98
107
  if (job === undefined) {
99
108
  return;
100
109
  }
110
+
101
111
  this.active = job;
102
112
  this.started = this.now();
103
113
  const epoch = this.epoch;
114
+
104
115
  const alive = (): boolean => !this.disposed && epoch === this.epoch && !job.cancelled;
116
+
105
117
  const isCurrent = (): boolean => alive() && job.valid?.() !== false;
118
+
106
119
  void Promise.resolve()
107
120
  .then(() => {
108
121
  if (!isCurrent()) {
109
122
  throw superseded();
110
123
  }
124
+
111
125
  this.client ??= this.options.createClient();
126
+
112
127
  if (this.client === null) {
113
128
  throw new EditorSearchError('unavailable', 'Regex search needs a search worker.');
114
129
  }
130
+
115
131
  const client = this.client;
132
+
116
133
  const guarded: EditorSearchClient = {
117
134
  search: (...args) => (isCurrent() ? client.search(...args) : Promise.reject(superseded())),
118
135
  match: (...args) => (isCurrent() ? client.match(...args) : Promise.reject(superseded())),
@@ -122,6 +139,7 @@ export class FindSearchQueue {
122
139
  release: () => client.release(),
123
140
  dispose: () => client.dispose()
124
141
  };
142
+
125
143
  return job.run(guarded, isCurrent);
126
144
  })
127
145
  .then((value) => {
@@ -142,10 +160,12 @@ export class FindSearchQueue {
142
160
  release(): void {
143
161
  this.epoch++;
144
162
  this.active?.reject(superseded());
163
+
145
164
  for (const job of this.pending) {
146
165
  job.cancelled = true;
147
166
  job.reject(superseded());
148
167
  }
168
+
149
169
  this.pending = [];
150
170
  this.cancelTimer?.();
151
171
  this.cancelTimer = undefined;