@adecore/editor 0.39.0 → 0.40.1
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/dist/controller.d.ts
CHANGED
package/dist/controller.js
CHANGED
|
@@ -88,6 +88,10 @@ export class InputController {
|
|
|
88
88
|
this.options = options;
|
|
89
89
|
this.gesture = new DoubleModifierGesture(options.apple ? 'Alt' : 'Control');
|
|
90
90
|
const { model, input, viewport } = view;
|
|
91
|
+
const lineAt = (offset) => {
|
|
92
|
+
const row = view.layout.rowForLine(model.positionAt(offset).line);
|
|
93
|
+
return row.kind === 'text' ? { from: model.getLine(row.line).start, to: model.getLine(row.lastLine).next } : { from: offset, to: offset };
|
|
94
|
+
};
|
|
91
95
|
this.native = new NativeInput(input, model, {
|
|
92
96
|
readOnly: () => view.settings.readOnly,
|
|
93
97
|
replace: (text) => this.replaceSelections(text),
|
|
@@ -110,10 +114,7 @@ export class InputController {
|
|
|
110
114
|
contentPoint: (x, y) => view.contentPoint(x, y),
|
|
111
115
|
columnSelections: (from, to) => (sameCell(view.layout, from, to) ? null : columnSelections(view.layout, from, to)),
|
|
112
116
|
visualLine: (offset) => view.layout.visualLine(offset),
|
|
113
|
-
lineAt
|
|
114
|
-
const row = view.layout.rowForLine(model.positionAt(offset).line);
|
|
115
|
-
return row.kind === 'text' ? { from: model.getLine(row.line).start, to: model.getLine(row.lastLine).next } : { from: offset, to: offset };
|
|
116
|
-
},
|
|
117
|
+
lineAt,
|
|
117
118
|
camelHumps: () => view.settings.smartKeys.camelHumps,
|
|
118
119
|
columnMode: () => view.columnMode,
|
|
119
120
|
selectionAt: (x, y) => (view.settings.readOnly ? null : view.selectionAtPoint(x, y)),
|
|
@@ -127,10 +128,7 @@ export class InputController {
|
|
|
127
128
|
model,
|
|
128
129
|
offsetAt: (x, y) => view.offsetAtPoint(x, y),
|
|
129
130
|
camelHumps: () => view.settings.smartKeys.camelHumps,
|
|
130
|
-
lineAt
|
|
131
|
-
const row = view.layout.rowForLine(model.positionAt(offset).line);
|
|
132
|
-
return row.kind === 'text' ? { from: model.getLine(row.line).start, to: model.getLine(row.lastLine).next } : { from: offset, to: offset };
|
|
133
|
-
},
|
|
131
|
+
lineAt,
|
|
134
132
|
focus: () => view.focus()
|
|
135
133
|
});
|
|
136
134
|
this.subscription = model.subscribe(() => {
|
|
@@ -237,15 +235,8 @@ export class InputController {
|
|
|
237
235
|
this.cleanup.length = 0;
|
|
238
236
|
this.keyHandlers.clear();
|
|
239
237
|
}
|
|
240
|
-
/* Tells a person why nothing happened, when the editor is read only and has said why. */
|
|
241
238
|
blockedByReadOnly() {
|
|
242
|
-
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
if (this.view.settings.readOnlyReason) {
|
|
246
|
-
this.view.notify(this.view.settings.readOnlyReason);
|
|
247
|
-
}
|
|
248
|
-
return true;
|
|
239
|
+
return this.view.blockedByReadOnly();
|
|
249
240
|
}
|
|
250
241
|
inputGroup() {
|
|
251
242
|
const now = Date.now();
|
|
@@ -266,23 +257,23 @@ export class InputController {
|
|
|
266
257
|
return;
|
|
267
258
|
}
|
|
268
259
|
const { model } = this.view;
|
|
269
|
-
|
|
270
|
-
this.view.ensureVisible(selection.head);
|
|
271
|
-
}
|
|
260
|
+
this.unfoldCarets();
|
|
272
261
|
const replacement = replacementEdits(model.getSelections(), this.native.normalizeNewlines(text));
|
|
273
262
|
this.applyEdits(replacement.edits, replacement.selections);
|
|
274
263
|
this.view.revealCaret();
|
|
275
264
|
}
|
|
265
|
+
unfoldCarets() {
|
|
266
|
+
for (const selection of this.view.model.getSelections()) {
|
|
267
|
+
this.view.ensureVisible(selection.head);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
276
270
|
paste(text) {
|
|
277
271
|
if (this.blockedByReadOnly()) {
|
|
278
272
|
return;
|
|
279
273
|
}
|
|
280
|
-
|
|
281
|
-
for (const selection of model.getSelections()) {
|
|
282
|
-
this.view.ensureVisible(selection.head);
|
|
283
|
-
}
|
|
274
|
+
this.unfoldCarets();
|
|
284
275
|
this.historyGroup++;
|
|
285
|
-
model.paste(this.native.normalizeNewlines(text), { ...this.commandOptions(), wholeLines: text !== '' && text === lineClip });
|
|
276
|
+
this.view.model.paste(this.native.normalizeNewlines(text), { ...this.commandOptions(), wholeLines: text !== '' && text === lineClip });
|
|
286
277
|
this.historyGroup++;
|
|
287
278
|
this.view.revealCaret();
|
|
288
279
|
}
|
|
@@ -358,7 +349,7 @@ export class InputController {
|
|
|
358
349
|
return { line, character: column };
|
|
359
350
|
});
|
|
360
351
|
const chains = await provider(asked).catch(() => null);
|
|
361
|
-
if (model.getRevision() !== revision ||
|
|
352
|
+
if (model.getRevision() !== revision || selectionSignature(model.getSelections()) !== selectionSignature(selections)) {
|
|
362
353
|
return;
|
|
363
354
|
}
|
|
364
355
|
if (chains === null) {
|
|
@@ -413,9 +404,7 @@ export class InputController {
|
|
|
413
404
|
if (this.blockedByReadOnly()) {
|
|
414
405
|
return;
|
|
415
406
|
}
|
|
416
|
-
|
|
417
|
-
this.view.ensureVisible(selection.head);
|
|
418
|
-
}
|
|
407
|
+
this.unfoldCarets();
|
|
419
408
|
// Every key goes through the model, which decides by the key before it too, as for the arrow of PHP.
|
|
420
409
|
if (text.length === 1) {
|
|
421
410
|
this.view.model.typeText(text, { ...this.commandOptions(), historyGroup: this.inputGroup() });
|
package/dist/engine.js
CHANGED
|
@@ -164,8 +164,7 @@ class SmartEditor {
|
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
this.caretOffset = head;
|
|
167
|
-
|
|
168
|
-
emit(this.carets, position);
|
|
167
|
+
emit(this.carets, this.positionAt(head));
|
|
169
168
|
}
|
|
170
169
|
getCaret() {
|
|
171
170
|
return this.positionAt(this.model.getPrimary().head);
|
|
@@ -483,13 +482,7 @@ class SmartEditor {
|
|
|
483
482
|
}
|
|
484
483
|
}
|
|
485
484
|
blockedByReadOnly() {
|
|
486
|
-
|
|
487
|
-
return false;
|
|
488
|
-
}
|
|
489
|
-
if (this.settings.readOnlyReason) {
|
|
490
|
-
this.view.notify(this.settings.readOnlyReason);
|
|
491
|
-
}
|
|
492
|
-
return true;
|
|
485
|
+
return this.view.blockedByReadOnly();
|
|
493
486
|
}
|
|
494
487
|
onFind(listener) {
|
|
495
488
|
return this.view.onFind(listener);
|
package/dist/find.js
CHANGED
|
@@ -360,14 +360,7 @@ export class FindController {
|
|
|
360
360
|
}
|
|
361
361
|
this.pendingAnchor = undefined;
|
|
362
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';
|
|
363
|
+
this.failed = searchFailure(error);
|
|
371
364
|
this.regexCache = { revision, key, complete: false };
|
|
372
365
|
this.options?.changed();
|
|
373
366
|
});
|
|
@@ -534,7 +527,8 @@ export class FindController {
|
|
|
534
527
|
this.options?.changed();
|
|
535
528
|
}
|
|
536
529
|
async replaceOne(replacement, preserveCase, canApply) {
|
|
537
|
-
const query = this.query
|
|
530
|
+
const query = this.query;
|
|
531
|
+
const mark = this.currentMark;
|
|
538
532
|
if (query === null || mark === null || this.search === undefined || !canApply()) {
|
|
539
533
|
return false;
|
|
540
534
|
}
|
|
@@ -574,8 +568,7 @@ export class FindController {
|
|
|
574
568
|
}
|
|
575
569
|
catch (error) {
|
|
576
570
|
if (valid()) {
|
|
577
|
-
|
|
578
|
-
this.reportReplaceFailed(error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure);
|
|
571
|
+
this.reportReplaceFailed(replaceFailure(error));
|
|
579
572
|
}
|
|
580
573
|
return false;
|
|
581
574
|
}
|
|
@@ -629,8 +622,7 @@ export class FindController {
|
|
|
629
622
|
}
|
|
630
623
|
catch (error) {
|
|
631
624
|
if (!this.disposed && epoch === this.epoch) {
|
|
632
|
-
|
|
633
|
-
this.replaceFailed = error instanceof EditorSearchError && error.kind === 'range' ? 'limit' : failure === 'invalid' ? 'error' : failure;
|
|
625
|
+
this.replaceFailed = replaceFailure(error);
|
|
634
626
|
}
|
|
635
627
|
return 0;
|
|
636
628
|
}
|
|
@@ -656,3 +648,10 @@ function searchFailure(error) {
|
|
|
656
648
|
: 'error'
|
|
657
649
|
: 'error';
|
|
658
650
|
}
|
|
651
|
+
function replaceFailure(error) {
|
|
652
|
+
if (error instanceof EditorSearchError && error.kind === 'range') {
|
|
653
|
+
return 'limit';
|
|
654
|
+
}
|
|
655
|
+
const failure = searchFailure(error);
|
|
656
|
+
return failure === 'invalid' ? 'error' : failure;
|
|
657
|
+
}
|
package/dist/layout.js
CHANGED
|
@@ -417,7 +417,13 @@ export class EditorLayout {
|
|
|
417
417
|
for (const block of this.blocks) {
|
|
418
418
|
const line = this.document.positionAt(block.at).line;
|
|
419
419
|
const target = block.placement === 'above' ? above : below;
|
|
420
|
-
|
|
420
|
+
const list = target.get(line);
|
|
421
|
+
if (list === undefined) {
|
|
422
|
+
target.set(line, [block]);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
list.push(block);
|
|
426
|
+
}
|
|
421
427
|
}
|
|
422
428
|
const folds = new Map();
|
|
423
429
|
for (const fold of this.folds) {
|
|
@@ -703,7 +709,6 @@ export class EditorLayout {
|
|
|
703
709
|
rowForLine(line) {
|
|
704
710
|
return this.rows[this.lineRows[Math.max(0, Math.min(this.lineRows.length - 1, line))]];
|
|
705
711
|
}
|
|
706
|
-
/* Whether a fold hides the line. */
|
|
707
712
|
isHidden(line) {
|
|
708
713
|
const row = this.rowForLine(line);
|
|
709
714
|
return row.kind === 'text' && line > row.line && line <= row.lastLine;
|
package/dist/paint.d.ts
CHANGED
package/dist/paint.js
CHANGED
|
@@ -221,7 +221,7 @@ export class RowPainter {
|
|
|
221
221
|
chip.className = 'se-fold-chip';
|
|
222
222
|
chip.textContent = '…';
|
|
223
223
|
chip.tabIndex = -1;
|
|
224
|
-
const chipLeft = geometry.rowEnds
|
|
224
|
+
const chipLeft = geometry.rowEnds.at(-1) + FOLD_GAP;
|
|
225
225
|
const chipWidth = Math.ceil(this.layout.metrics.charWidth) + FOLD_CHIP_PADDING;
|
|
226
226
|
chip.style.left = `${chipLeft}px`;
|
|
227
227
|
chip.style.top = `${(geometry.subRows - 1) * lineHeight}px`;
|
|
@@ -265,8 +265,7 @@ export class RowPainter {
|
|
|
265
265
|
}
|
|
266
266
|
replacement.render(element);
|
|
267
267
|
this.code.append(element);
|
|
268
|
-
entry =
|
|
269
|
-
this.rows.set(row.key, entry);
|
|
268
|
+
entry = this.hostEntry(row.key, element, revision);
|
|
270
269
|
}
|
|
271
270
|
else if (entry.revision !== revision) {
|
|
272
271
|
entry.element.replaceChildren();
|
|
@@ -277,10 +276,19 @@ export class RowPainter {
|
|
|
277
276
|
entry.element.style.top = `${row.top}px`;
|
|
278
277
|
entry.element.style.left = `${indent}px`;
|
|
279
278
|
entry.element.style.width = `${Math.max(80, paint.viewportWidth - indent)}px`;
|
|
280
|
-
entry.element
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
return this.measure(row.key, entry.element);
|
|
280
|
+
}
|
|
281
|
+
hostEntry(key, element, revision) {
|
|
282
|
+
const entry = { element, text: '', tokens: null, layoutVersion: 0, whitespace: false, folded: false, tail: '', tailTokens: null, revision };
|
|
283
|
+
this.rows.set(key, entry);
|
|
284
|
+
return entry;
|
|
285
|
+
}
|
|
286
|
+
/* Lets the host's content set the height, and returns whether that moved the layout. */
|
|
287
|
+
measure(key, element) {
|
|
288
|
+
element.style.height = '';
|
|
289
|
+
this.observer?.observe(element);
|
|
290
|
+
const height = element.getBoundingClientRect?.().height ?? 0;
|
|
291
|
+
return height > 0 && this.layout.setMeasuredHeight(key, height);
|
|
284
292
|
}
|
|
285
293
|
paintBlock(row, paint) {
|
|
286
294
|
const { widget } = row;
|
|
@@ -296,8 +304,7 @@ export class RowPainter {
|
|
|
296
304
|
if (!widget.lens && !widget.fixed) {
|
|
297
305
|
this.observer?.observe(element);
|
|
298
306
|
}
|
|
299
|
-
entry =
|
|
300
|
-
this.rows.set(row.key, entry);
|
|
307
|
+
entry = this.hostEntry(row.key, element, revision);
|
|
301
308
|
}
|
|
302
309
|
else if (entry.revision !== revision) {
|
|
303
310
|
entry.element.replaceChildren();
|
|
@@ -322,10 +329,7 @@ export class RowPainter {
|
|
|
322
329
|
entry.element.style.height = `${row.height}px`;
|
|
323
330
|
return false;
|
|
324
331
|
}
|
|
325
|
-
entry.element
|
|
326
|
-
this.observer?.observe(entry.element);
|
|
327
|
-
const height = entry.element.getBoundingClientRect?.().height ?? 0;
|
|
328
|
-
return height > 0 && this.layout.setMeasuredHeight(row.key, height);
|
|
332
|
+
return this.measure(row.key, entry.element);
|
|
329
333
|
}
|
|
330
334
|
fillBlock(element, widget) {
|
|
331
335
|
if (widget.render) {
|
|
@@ -407,15 +411,14 @@ export function paintGutter(container, layout, rows, paint) {
|
|
|
407
411
|
}
|
|
408
412
|
item.append(button);
|
|
409
413
|
}
|
|
410
|
-
|
|
411
|
-
if (sign) {
|
|
414
|
+
if (highlight?.sign) {
|
|
412
415
|
const mark = document.createElement('span');
|
|
413
416
|
mark.className = 'se-line-sign';
|
|
414
|
-
mark.textContent = sign;
|
|
417
|
+
mark.textContent = highlight.sign;
|
|
415
418
|
if (label !== null) {
|
|
416
419
|
mark.setAttribute('aria-hidden', 'true');
|
|
417
420
|
}
|
|
418
|
-
mark.style.color = colorValue(
|
|
421
|
+
mark.style.color = colorValue(highlight.color);
|
|
419
422
|
item.append(mark);
|
|
420
423
|
}
|
|
421
424
|
if (highlight?.bar !== undefined) {
|
|
@@ -495,7 +498,6 @@ export function paintOver(container, layers) {
|
|
|
495
498
|
}
|
|
496
499
|
container.replaceChildren(fragment);
|
|
497
500
|
}
|
|
498
|
-
/* The carets, above the text. */
|
|
499
501
|
export function paintCarets(container, carets) {
|
|
500
502
|
const document = container.ownerDocument;
|
|
501
503
|
container.replaceChildren(...carets.map((rect) => box(document, rect.drop ? 'se-drop-caret' : rect.primary ? 'se-caret se-primary-caret' : 'se-caret', { ...rect, width: 2 })));
|
|
@@ -571,14 +573,15 @@ function appendTabSigns(element, geometry, lineHeight, band) {
|
|
|
571
573
|
if (geometry.text[offsetOfStop(geometry, stop)] !== '\t') {
|
|
572
574
|
continue;
|
|
573
575
|
}
|
|
576
|
+
const row = rowOfStop(geometry, stop);
|
|
574
577
|
const left = xOfStop(geometry, stop);
|
|
575
|
-
const right = rowOfStop(geometry, stop + 1) ===
|
|
578
|
+
const right = rowOfStop(geometry, stop + 1) === row ? xOfStop(geometry, stop + 1) : geometry.rowEnds[row];
|
|
576
579
|
if (right - left < 3) {
|
|
577
580
|
continue;
|
|
578
581
|
}
|
|
579
582
|
const sign = arrowSign(document, right - left - 2, lineHeight, 'se-tab-sign');
|
|
580
583
|
sign.style.left = `${left + 1}px`;
|
|
581
|
-
sign.style.top = `${
|
|
584
|
+
sign.style.top = `${row * lineHeight}px`;
|
|
582
585
|
element.append(sign);
|
|
583
586
|
}
|
|
584
587
|
}
|
package/dist/view.d.ts
CHANGED
|
@@ -194,6 +194,7 @@ export declare class EditorView {
|
|
|
194
194
|
setMarkers(markers: readonly ViewMarker[]): void;
|
|
195
195
|
setComposition(text: string | undefined): void;
|
|
196
196
|
notify(message: string): void;
|
|
197
|
+
blockedByReadOnly(): boolean;
|
|
197
198
|
private foldName;
|
|
198
199
|
announce(message: string): void;
|
|
199
200
|
setInputPopup(popup: EditorInputPopup | null): void;
|
|
@@ -317,6 +318,7 @@ export declare class EditorView {
|
|
|
317
318
|
private wrapSigns;
|
|
318
319
|
private indentOf;
|
|
319
320
|
private effectiveIndent;
|
|
321
|
+
private nearestIndent;
|
|
320
322
|
private activeGuide;
|
|
321
323
|
private scopeGuide;
|
|
322
324
|
private caretOnGuide;
|
|
@@ -393,10 +395,9 @@ export declare class EditorView {
|
|
|
393
395
|
color: string;
|
|
394
396
|
}[]): void;
|
|
395
397
|
setLineHighlights(highlights: readonly EditorLineHighlight[]): void;
|
|
396
|
-
private
|
|
398
|
+
private runLines;
|
|
397
399
|
private highlightMarks;
|
|
398
400
|
renderCode(container: HTMLElement, text: string, options: EditorCodeBlockOptions): void;
|
|
399
|
-
private attributedLines;
|
|
400
401
|
onAttributionHover(listener: (hover: {
|
|
401
402
|
id: string;
|
|
402
403
|
rect: EditorRect;
|
package/dist/view.js
CHANGED
|
@@ -20,6 +20,9 @@ import { TokenCache } from './tokens.js';
|
|
|
20
20
|
function sameRect(left, right) {
|
|
21
21
|
return left.left === right.left && left.top === right.top && left.right === right.right && left.bottom === right.bottom;
|
|
22
22
|
}
|
|
23
|
+
function closestElement(target, selector) {
|
|
24
|
+
return target?.closest?.(selector) ?? null;
|
|
25
|
+
}
|
|
23
26
|
function mapRange(range, map) {
|
|
24
27
|
return { ...range, from: map(range.from), to: map(range.to) };
|
|
25
28
|
}
|
|
@@ -453,8 +456,7 @@ export class EditorView {
|
|
|
453
456
|
continue;
|
|
454
457
|
}
|
|
455
458
|
const line = this.model.positionAt(token.from).line;
|
|
456
|
-
const start = this.model.getLine(line)
|
|
457
|
-
const end = this.model.getLine(line).end;
|
|
459
|
+
const { start, end } = this.model.getLine(line);
|
|
458
460
|
if (token.to > end) {
|
|
459
461
|
continue;
|
|
460
462
|
}
|
|
@@ -486,6 +488,16 @@ export class EditorView {
|
|
|
486
488
|
this.notice.hidden = true;
|
|
487
489
|
}, NOTICE_MS);
|
|
488
490
|
}
|
|
491
|
+
/* Tells a person why nothing happened, when the editor is read only and has said why. */
|
|
492
|
+
blockedByReadOnly() {
|
|
493
|
+
if (!this.settings.readOnly) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
if (this.settings.readOnlyReason) {
|
|
497
|
+
this.notify(this.settings.readOnlyReason);
|
|
498
|
+
}
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
489
501
|
foldName(line, collapsed) {
|
|
490
502
|
const template = (collapsed ? this.settings.messages.unfold : this.settings.messages.fold) ??
|
|
491
503
|
(collapsed ? 'Unfold lines {start} to {end}' : 'Fold lines {start} to {end}');
|
|
@@ -1095,16 +1107,16 @@ export class EditorView {
|
|
|
1095
1107
|
this.anchorScroll(() => this.layout.configure({ wrapWidth: width }));
|
|
1096
1108
|
}
|
|
1097
1109
|
}
|
|
1098
|
-
bracketAt(
|
|
1099
|
-
if (
|
|
1110
|
+
bracketAt({ anchor, head }) {
|
|
1111
|
+
if (Math.abs(head - anchor) > 1) {
|
|
1100
1112
|
return undefined;
|
|
1101
1113
|
}
|
|
1102
|
-
const candidates =
|
|
1114
|
+
const candidates = anchor !== head ? [Math.min(anchor, head)] : [head, head - 1];
|
|
1103
1115
|
return candidates.find((at) => at >= 0 && /[()[\]{}]/.test(this.model.slice(at, at + 1)));
|
|
1104
1116
|
}
|
|
1105
1117
|
/* The offset a bracket at the caret pairs with, if it is one. */
|
|
1106
1118
|
matchingBracket(selection) {
|
|
1107
|
-
const at = this.bracketAt(
|
|
1119
|
+
const at = this.bracketAt(selection);
|
|
1108
1120
|
return at === undefined ? null : { at, mate: this.model.matchBracket(at, this.settings.language)?.mate };
|
|
1109
1121
|
}
|
|
1110
1122
|
/* Draws the highlights of the brackets at the carets that the last frame ran out of lexing for. */
|
|
@@ -1151,7 +1163,7 @@ export class EditorView {
|
|
|
1151
1163
|
}
|
|
1152
1164
|
/* The offset of the character under a point of the page, or null over anything that is not text. */
|
|
1153
1165
|
characterAtPoint(clientX, clientY, target) {
|
|
1154
|
-
if (target
|
|
1166
|
+
if (closestElement(target, '.se-inlay, .se-block, .se-line-action, .se-gutter, .se-sticky, .se-fold-chip, .se-overview')) {
|
|
1155
1167
|
return null;
|
|
1156
1168
|
}
|
|
1157
1169
|
const point = this.contentPoint(clientX, clientY);
|
|
@@ -1292,17 +1304,18 @@ export class EditorView {
|
|
|
1292
1304
|
if (own !== null) {
|
|
1293
1305
|
return own;
|
|
1294
1306
|
}
|
|
1307
|
+
return Math.min(this.nearestIndent(line, -1, GUIDE_SCAN_LINES), this.nearestIndent(line, 1, GUIDE_SCAN_LINES));
|
|
1308
|
+
}
|
|
1309
|
+
/* The indentation of the first line that is not blank within `reach` lines of `line` in `direction`, or 0. */
|
|
1310
|
+
nearestIndent(line, direction, reach) {
|
|
1295
1311
|
const last = this.model.getLineCount() - 1;
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
return indent;
|
|
1301
|
-
}
|
|
1312
|
+
for (let at = line + direction, steps = 0; at >= 0 && at <= last && steps < reach; at += direction, steps++) {
|
|
1313
|
+
const indent = this.indentOf(at);
|
|
1314
|
+
if (indent !== null) {
|
|
1315
|
+
return indent;
|
|
1302
1316
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
return Math.min(nearest(-1), nearest(1));
|
|
1317
|
+
}
|
|
1318
|
+
return 0;
|
|
1306
1319
|
}
|
|
1307
1320
|
/* The scope guide of the caret, worked out again only when the text or the caret's line is not the one it was for. */
|
|
1308
1321
|
activeGuide() {
|
|
@@ -1319,22 +1332,8 @@ export class EditorView {
|
|
|
1319
1332
|
const indent = this.effectiveIndent(line);
|
|
1320
1333
|
const count = this.model.getLineCount();
|
|
1321
1334
|
const deeper = (at, column) => at >= 0 && at < count && this.effectiveIndent(at) > column;
|
|
1322
|
-
const next = (
|
|
1323
|
-
|
|
1324
|
-
if (this.indentOf(at) !== null) {
|
|
1325
|
-
return this.indentOf(at);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
return 0;
|
|
1329
|
-
})();
|
|
1330
|
-
const previous = (() => {
|
|
1331
|
-
for (let at = line - 1; at >= 0 && at > line - GUIDE_SCAN_LINES; at--) {
|
|
1332
|
-
if (this.indentOf(at) !== null) {
|
|
1333
|
-
return this.indentOf(at);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
return 0;
|
|
1337
|
-
})();
|
|
1335
|
+
const next = this.nearestIndent(line, 1, GUIDE_SCAN_LINES - 1);
|
|
1336
|
+
const previous = this.nearestIndent(line, -1, GUIDE_SCAN_LINES - 1);
|
|
1338
1337
|
// A line that opens a block, or closes one, is at the edge of its guide: the one that sits at its own indentation.
|
|
1339
1338
|
const opens = indent % size === 0 && (next > indent || previous > indent) && this.indentOf(line) !== null;
|
|
1340
1339
|
const column = opens ? indent : indent > 0 ? Math.ceil(indent / size - 1) * size : -1;
|
|
@@ -1441,7 +1440,7 @@ export class EditorView {
|
|
|
1441
1440
|
}
|
|
1442
1441
|
/* The hint, and the part of its label, an event happened on, or null anywhere else. */
|
|
1443
1442
|
inlayAt(target) {
|
|
1444
|
-
const element = target
|
|
1443
|
+
const element = closestElement(target, '.se-inlay');
|
|
1445
1444
|
if (!element?.dataset.inlayId) {
|
|
1446
1445
|
return null;
|
|
1447
1446
|
}
|
|
@@ -1806,13 +1805,13 @@ export class EditorView {
|
|
|
1806
1805
|
const lastRow = rows.at(-1);
|
|
1807
1806
|
const firstLine = rows[0]?.line ?? 0;
|
|
1808
1807
|
const lastLine = lastRow?.kind === 'text' ? lastRow.lastLine : (lastRow?.line ?? 0);
|
|
1809
|
-
const highlighted = this.
|
|
1808
|
+
const highlighted = this.runLines(this.highlights, firstLine, lastLine);
|
|
1810
1809
|
const shownFrom = this.model.offsetAt({ line: firstLine, column: 0 });
|
|
1811
1810
|
const shownTo = lastLine + 1 < this.model.getLineCount() ? this.model.offsetAt({ line: lastLine + 1, column: 0 }) : this.model.getLength();
|
|
1812
1811
|
paintGutter(this.gutterLines, this.layout, rows, {
|
|
1813
1812
|
lineNumber: (line) => this.lineNumberLabel(line),
|
|
1814
|
-
changes: this.changedLines(
|
|
1815
|
-
attribution: this.
|
|
1813
|
+
changes: this.changedLines(firstLine, lastLine),
|
|
1814
|
+
attribution: this.runLines(this.attribution, firstLine, lastLine),
|
|
1816
1815
|
highlights: highlighted,
|
|
1817
1816
|
activeLines: new Set(selections
|
|
1818
1817
|
.filter((selection) => selection.head >= shownFrom && selection.head <= shownTo)
|
|
@@ -1893,7 +1892,7 @@ export class EditorView {
|
|
|
1893
1892
|
if (index < selections.length - 1 && (selection.head < visibleFrom - 1 || selection.head > visibleTo + 1)) {
|
|
1894
1893
|
continue;
|
|
1895
1894
|
}
|
|
1896
|
-
const at = this.bracketAt(
|
|
1895
|
+
const at = this.bracketAt(selection);
|
|
1897
1896
|
if (at === undefined) {
|
|
1898
1897
|
continue;
|
|
1899
1898
|
}
|
|
@@ -1916,9 +1915,7 @@ export class EditorView {
|
|
|
1916
1915
|
}
|
|
1917
1916
|
}
|
|
1918
1917
|
}
|
|
1919
|
-
const selected = selections.flatMap((
|
|
1920
|
-
? []
|
|
1921
|
-
: this.paintRectangles(Math.min(selection.anchor, selection.head), Math.max(selection.anchor, selection.head), rows));
|
|
1918
|
+
const selected = selections.flatMap(({ anchor, head }) => anchor === head ? [] : this.paintRectangles(Math.min(anchor, head), Math.max(anchor, head), rows));
|
|
1922
1919
|
paintOverlays(this.overlays, { currentLine, selections: selected, focused, marks });
|
|
1923
1920
|
paintOver(this.over, [
|
|
1924
1921
|
{ className: 'se-faded', rects: faded },
|
|
@@ -2006,12 +2003,8 @@ export class EditorView {
|
|
|
2006
2003
|
this.changeCache = { version: this.changeVersion, lines, runs };
|
|
2007
2004
|
return this.changeCache;
|
|
2008
2005
|
}
|
|
2009
|
-
|
|
2010
|
-
changedLines(rows) {
|
|
2006
|
+
changedLines(first, last) {
|
|
2011
2007
|
const { lines } = this.resolvedChanges();
|
|
2012
|
-
const first = rows[0]?.line ?? 0;
|
|
2013
|
-
const lastRow = rows.at(-1);
|
|
2014
|
-
const last = lastRow?.kind === 'text' ? lastRow.lastLine : (lastRow?.line ?? 0);
|
|
2015
2008
|
const visible = new Map();
|
|
2016
2009
|
for (let line = first; line <= last; line++) {
|
|
2017
2010
|
const kind = lines.get(line);
|
|
@@ -2112,8 +2105,8 @@ export class EditorView {
|
|
|
2112
2105
|
this.highlights.set(highlights.map((highlight) => ({ id: '', ...highlight })), this.model.getLineCount(), (line) => this.model.getLine(line));
|
|
2113
2106
|
this.render();
|
|
2114
2107
|
}
|
|
2115
|
-
|
|
2116
|
-
return
|
|
2108
|
+
runLines(runs, first, last) {
|
|
2109
|
+
return runs.linesIn(first, last, this.model.getLine(first).start, this.model.getLine(last).end, (offset) => this.model.positionAt(offset).line);
|
|
2117
2110
|
}
|
|
2118
2111
|
/* One box per row, so a row of the host's between two tinted lines stays clear. */
|
|
2119
2112
|
highlightMarks(rows, lines) {
|
|
@@ -2147,9 +2140,6 @@ export class EditorView {
|
|
|
2147
2140
|
renderCode(container, text, options) {
|
|
2148
2141
|
renderCodeBlock(container, text, options.tokens === undefined ? this.tokens.tokenizeText(text.split(/\r?\n/)) : options.tokens, this.settings.tabSize, options);
|
|
2149
2142
|
}
|
|
2150
|
-
attributedLines(first, last) {
|
|
2151
|
-
return this.attribution.linesIn(first, last, this.model.getLine(first).start, this.model.getLine(last).end, (offset) => this.model.positionAt(offset).line);
|
|
2152
|
-
}
|
|
2153
2143
|
onAttributionHover(listener) {
|
|
2154
2144
|
return subscribe(this.attributionListeners, listener);
|
|
2155
2145
|
}
|
|
@@ -2186,7 +2176,7 @@ export class EditorView {
|
|
|
2186
2176
|
if (row.kind !== 'text') {
|
|
2187
2177
|
return null;
|
|
2188
2178
|
}
|
|
2189
|
-
const bar = this.
|
|
2179
|
+
const bar = this.runLines(this.attribution, row.line, row.line).get(row.line);
|
|
2190
2180
|
if (bar === undefined) {
|
|
2191
2181
|
return null;
|
|
2192
2182
|
}
|
|
@@ -2203,7 +2193,7 @@ export class EditorView {
|
|
|
2203
2193
|
}
|
|
2204
2194
|
/* A problem's tick says what the problem is when the pointer rests on it, and a press goes to it. The rest of the track lets the pointer through. */
|
|
2205
2195
|
wireOverview() {
|
|
2206
|
-
const tickOf = (event) => event.target
|
|
2196
|
+
const tickOf = (event) => closestElement(event.target, '.se-tick[data-offset]');
|
|
2207
2197
|
this.overview.addEventListener('pointerover', (event) => {
|
|
2208
2198
|
const tick = tickOf(event);
|
|
2209
2199
|
const title = tick?.getAttribute('data-title');
|
|
@@ -2395,10 +2385,8 @@ export class EditorView {
|
|
|
2395
2385
|
onGutterMarker(listener) {
|
|
2396
2386
|
return subscribe(this.gutterMarkerListeners, listener);
|
|
2397
2387
|
}
|
|
2398
|
-
/* The id of the host's gutter marker when the target is it. */
|
|
2399
2388
|
gutterMarkerIdOf(target) {
|
|
2400
|
-
|
|
2401
|
-
return button?.dataset.gutterMarker ?? null;
|
|
2389
|
+
return closestElement(target, '[data-gutter-marker]')?.dataset.gutterMarker ?? null;
|
|
2402
2390
|
}
|
|
2403
2391
|
pressGutterMarker(id) {
|
|
2404
2392
|
emit(this.gutterMarkerListeners, id);
|
|
@@ -2415,17 +2403,16 @@ export class EditorView {
|
|
|
2415
2403
|
}
|
|
2416
2404
|
return byLine;
|
|
2417
2405
|
}
|
|
2418
|
-
/* The line of the host's gutter button when the target is it. */
|
|
2419
2406
|
gutterActionLineOf(target) {
|
|
2420
|
-
const button = target
|
|
2407
|
+
const button = closestElement(target, '[data-gutter-action]');
|
|
2421
2408
|
return button ? Number(button.dataset.gutterAction) : null;
|
|
2422
2409
|
}
|
|
2423
2410
|
pressGutterAction(line) {
|
|
2424
2411
|
emit(this.gutterActionListeners, line);
|
|
2425
2412
|
}
|
|
2426
|
-
/*
|
|
2413
|
+
/* The line a fold control in the gutter folds. */
|
|
2427
2414
|
foldLineOf(target) {
|
|
2428
|
-
const button = target
|
|
2415
|
+
const button = closestElement(target, '[data-fold-line]');
|
|
2429
2416
|
return button ? Number(button.dataset.foldLine) : null;
|
|
2430
2417
|
}
|
|
2431
2418
|
dispose() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"test": "bun test --conditions=source"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@adecore/editor-core": "0.
|
|
58
|
-
"linkedom": "^0.18.
|
|
59
|
-
"shiki": "^4.
|
|
57
|
+
"@adecore/editor-core": "0.40.1",
|
|
58
|
+
"linkedom": "^0.18.14",
|
|
59
|
+
"shiki": "^4.5.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@types/bun": "^1.4.
|
|
63
|
-
"@types/node": "^24.
|
|
62
|
+
"@types/bun": "^1.4.4",
|
|
63
|
+
"@types/node": "^24.19.2",
|
|
64
64
|
"typescript": "^7.0.2"
|
|
65
65
|
},
|
|
66
66
|
"license": "FSL-1.1-MIT",
|