@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/paint.ts
CHANGED
|
@@ -59,13 +59,17 @@ export function styleSpan(span: HTMLElement, token: LineToken): void {
|
|
|
59
59
|
if (token.color !== '') {
|
|
60
60
|
span.style.color = token.color;
|
|
61
61
|
}
|
|
62
|
+
|
|
62
63
|
if (token.fontStyle & ITALIC) {
|
|
63
64
|
span.style.fontStyle = 'italic';
|
|
64
65
|
}
|
|
66
|
+
|
|
65
67
|
if (token.fontStyle & BOLD) {
|
|
66
68
|
span.style.fontWeight = 'bold';
|
|
67
69
|
}
|
|
70
|
+
|
|
68
71
|
const decorations = [token.fontStyle & UNDERLINE ? 'underline' : '', token.fontStyle & STRIKETHROUGH ? 'line-through' : ''].filter(Boolean);
|
|
72
|
+
|
|
69
73
|
if (decorations.length > 0) {
|
|
70
74
|
span.style.textDecoration = decorations.join(' ');
|
|
71
75
|
}
|
|
@@ -75,9 +79,12 @@ export function styleSpan(span: HTMLElement, token: LineToken): void {
|
|
|
75
79
|
function setText(element: HTMLElement, text: string, whitespace: boolean): void {
|
|
76
80
|
if (!whitespace || !text.includes(' ')) {
|
|
77
81
|
element.textContent = text;
|
|
82
|
+
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
85
|
+
|
|
80
86
|
const document = element.ownerDocument;
|
|
87
|
+
|
|
81
88
|
for (const part of text.split(/( +)/)) {
|
|
82
89
|
if (part.startsWith(' ')) {
|
|
83
90
|
const dots = document.createElement('span');
|
|
@@ -102,20 +109,26 @@ function fillRun(
|
|
|
102
109
|
): void {
|
|
103
110
|
if (tokens === null) {
|
|
104
111
|
setText(run, geometry.text.slice(from - geometry.start, to - geometry.start), whitespace);
|
|
112
|
+
|
|
105
113
|
return;
|
|
106
114
|
}
|
|
115
|
+
|
|
107
116
|
let tokenStart = tokensFrom;
|
|
117
|
+
|
|
108
118
|
for (const token of tokens) {
|
|
109
119
|
const tokenEnd = tokenStart + token.length;
|
|
110
120
|
const start = Math.max(from, tokenStart);
|
|
111
121
|
const end = Math.min(to, tokenEnd);
|
|
122
|
+
|
|
112
123
|
if (start < end) {
|
|
113
124
|
const span = run.ownerDocument.createElement('span');
|
|
114
125
|
setText(span, geometry.text.slice(start - geometry.start, end - geometry.start), whitespace);
|
|
115
126
|
styleSpan(span, token);
|
|
116
127
|
run.append(span);
|
|
117
128
|
}
|
|
129
|
+
|
|
118
130
|
tokenStart = tokenEnd;
|
|
131
|
+
|
|
119
132
|
if (tokenStart >= to) {
|
|
120
133
|
break;
|
|
121
134
|
}
|
|
@@ -135,19 +148,24 @@ export function lineElement(
|
|
|
135
148
|
element.style.width = `${geometry.width}px`;
|
|
136
149
|
let first = 0;
|
|
137
150
|
let last = geometry.runs.length;
|
|
151
|
+
|
|
138
152
|
while (first < last) {
|
|
139
153
|
const middle = (first + last) >>> 1;
|
|
154
|
+
|
|
140
155
|
if (geometry.runs[middle]!.subRow < band.first) {
|
|
141
156
|
first = middle + 1;
|
|
142
157
|
} else {
|
|
143
158
|
last = middle;
|
|
144
159
|
}
|
|
145
160
|
}
|
|
161
|
+
|
|
146
162
|
for (let index = first; index < geometry.runs.length; index++) {
|
|
147
163
|
const run = geometry.runs[index]!;
|
|
164
|
+
|
|
148
165
|
if (run.subRow > band.last) {
|
|
149
166
|
break;
|
|
150
167
|
}
|
|
168
|
+
|
|
151
169
|
const span = document.createElement('span');
|
|
152
170
|
span.className = 'se-run';
|
|
153
171
|
span.style.left = `${run.x}px`;
|
|
@@ -155,18 +173,23 @@ export function lineElement(
|
|
|
155
173
|
fillRun(span, geometry, geometry.start + run.from, geometry.start + run.to, tokens, whitespace);
|
|
156
174
|
element.append(span);
|
|
157
175
|
}
|
|
176
|
+
|
|
158
177
|
if (whitespace) {
|
|
159
178
|
appendTabSigns(element, geometry, lineHeight, band);
|
|
160
179
|
}
|
|
180
|
+
|
|
161
181
|
appendBidiSigns(element, geometry, lineHeight, band);
|
|
182
|
+
|
|
162
183
|
for (const box of geometry.inlays) {
|
|
163
184
|
if (box.subRow < band.first || box.subRow > band.last) {
|
|
164
185
|
continue;
|
|
165
186
|
}
|
|
187
|
+
|
|
166
188
|
const span = document.createElement('span');
|
|
167
189
|
span.className = box.inlay.ghost ? 'se-ghost' : box.inlay.composition ? 'se-composition' : 'se-inlay';
|
|
168
190
|
span.dataset.inlayId = box.inlay.id;
|
|
169
191
|
const { parts } = box.inlay;
|
|
192
|
+
|
|
170
193
|
if (parts === undefined) {
|
|
171
194
|
span.textContent = visibleBidi(box.inlay.text);
|
|
172
195
|
} else {
|
|
@@ -178,12 +201,14 @@ export function lineElement(
|
|
|
178
201
|
span.append(element);
|
|
179
202
|
});
|
|
180
203
|
}
|
|
204
|
+
|
|
181
205
|
const gapLeft = box.inlay.gapLeft ?? 0;
|
|
182
206
|
span.style.left = `${box.x + gapLeft}px`;
|
|
183
207
|
span.style.top = `${box.subRow * lineHeight}px`;
|
|
184
208
|
span.style.width = `${box.width - gapLeft - (box.inlay.gapRight ?? 0)}px`;
|
|
185
209
|
element.append(span);
|
|
186
210
|
}
|
|
211
|
+
|
|
187
212
|
return element;
|
|
188
213
|
}
|
|
189
214
|
|
|
@@ -207,6 +232,7 @@ export class RowPainter {
|
|
|
207
232
|
/* Draws the rows and takes the rest away. Returns whether a widget's height moved the layout. */
|
|
208
233
|
paint(rows: readonly LayoutRow[], paint: RowPaint): boolean {
|
|
209
234
|
const visible = new Set(rows.map((row) => row.key));
|
|
235
|
+
|
|
210
236
|
for (const [key, entry] of this.rows) {
|
|
211
237
|
if (!visible.has(key)) {
|
|
212
238
|
this.observer?.unobserve(entry.element);
|
|
@@ -214,7 +240,9 @@ export class RowPainter {
|
|
|
214
240
|
this.rows.delete(key);
|
|
215
241
|
}
|
|
216
242
|
}
|
|
243
|
+
|
|
217
244
|
let measured = false;
|
|
245
|
+
|
|
218
246
|
for (const row of rows) {
|
|
219
247
|
if (row.kind === 'text' && row.replacement !== undefined) {
|
|
220
248
|
measured = this.paintReplacement(row, row.replacement, paint) || measured;
|
|
@@ -224,22 +252,26 @@ export class RowPainter {
|
|
|
224
252
|
measured = this.paintBlock(row, paint) || measured;
|
|
225
253
|
}
|
|
226
254
|
}
|
|
255
|
+
|
|
227
256
|
return measured;
|
|
228
257
|
}
|
|
229
258
|
|
|
230
259
|
private paintText(row: TextRow, paint: RowPaint): void {
|
|
231
260
|
const geometry = this.layout.geometry(row);
|
|
232
261
|
const tokens = paint.tokensOf(row.line);
|
|
262
|
+
|
|
233
263
|
// Block boundaries keep a scroll within the band from rebuilding its text DOM.
|
|
234
264
|
const band =
|
|
235
265
|
geometry.subRows <= 64
|
|
236
266
|
? { first: 0, last: geometry.subRows - 1 }
|
|
237
267
|
: subRowBand(row.top, geometry.subRows, this.layout.metrics.lineHeight, paint.window, 64);
|
|
268
|
+
|
|
238
269
|
const folded = row.lastLine > row.line;
|
|
239
270
|
const lastLine = folded && row.tail !== null ? this.layout.document.getLine(row.lastLine) : null;
|
|
240
271
|
const tail = lastLine === null ? '' : lastLine.text.slice(row.tail! - lastLine.start);
|
|
241
272
|
const tailTokens = tail === '' ? null : paint.tokensOf(row.lastLine);
|
|
242
273
|
let entry = this.rows.get(row.key);
|
|
274
|
+
|
|
243
275
|
if (
|
|
244
276
|
!entry ||
|
|
245
277
|
entry.text !== geometry.text ||
|
|
@@ -253,11 +285,13 @@ export class RowPainter {
|
|
|
253
285
|
entry.band?.last !== band.last
|
|
254
286
|
) {
|
|
255
287
|
const element = this.buildText(row, geometry, tokens, paint, tail, tailTokens, band);
|
|
288
|
+
|
|
256
289
|
if (entry) {
|
|
257
290
|
entry.element.replaceWith(element);
|
|
258
291
|
} else {
|
|
259
292
|
this.code.append(element);
|
|
260
293
|
}
|
|
294
|
+
|
|
261
295
|
entry = {
|
|
262
296
|
element,
|
|
263
297
|
text: geometry.text,
|
|
@@ -270,8 +304,10 @@ export class RowPainter {
|
|
|
270
304
|
revision: 0,
|
|
271
305
|
band
|
|
272
306
|
};
|
|
307
|
+
|
|
273
308
|
this.rows.set(row.key, entry);
|
|
274
309
|
}
|
|
310
|
+
|
|
275
311
|
entry.element.style.top = `${row.top}px`;
|
|
276
312
|
entry.element.style.height = `${row.height}px`;
|
|
277
313
|
entry.element.dataset.line = String(row.line);
|
|
@@ -289,13 +325,14 @@ export class RowPainter {
|
|
|
289
325
|
const document = this.code.ownerDocument;
|
|
290
326
|
const lineHeight = this.layout.metrics.lineHeight;
|
|
291
327
|
const element = lineElement(document, geometry, tokens, lineHeight, band, paint.whitespace);
|
|
328
|
+
|
|
292
329
|
if (row.lastLine > row.line && band.last === geometry.subRows - 1) {
|
|
293
330
|
const chip = document.createElement('button');
|
|
294
331
|
chip.type = 'button';
|
|
295
332
|
chip.className = 'se-fold-chip';
|
|
296
333
|
chip.textContent = '…';
|
|
297
334
|
chip.tabIndex = -1;
|
|
298
|
-
const chipLeft = geometry.rowEnds
|
|
335
|
+
const chipLeft = geometry.rowEnds.at(-1)! + FOLD_GAP;
|
|
299
336
|
const chipWidth = Math.ceil(this.layout.metrics.charWidth) + FOLD_CHIP_PADDING;
|
|
300
337
|
chip.style.left = `${chipLeft}px`;
|
|
301
338
|
chip.style.top = `${(geometry.subRows - 1) * lineHeight}px`;
|
|
@@ -303,10 +340,12 @@ export class RowPainter {
|
|
|
303
340
|
chip.addEventListener('pointerdown', (event) => event.stopPropagation());
|
|
304
341
|
chip.addEventListener('click', () => paint.onUnfold(row.line));
|
|
305
342
|
element.append(chip);
|
|
343
|
+
|
|
306
344
|
if (tail !== '' && row.tail !== null) {
|
|
307
345
|
this.appendTail(element, row, geometry, tail, tailTokens, chipLeft + chipWidth + FOLD_GAP);
|
|
308
346
|
}
|
|
309
347
|
}
|
|
348
|
+
|
|
310
349
|
return element;
|
|
311
350
|
}
|
|
312
351
|
|
|
@@ -315,6 +354,7 @@ export class RowPainter {
|
|
|
315
354
|
const document = this.code.ownerDocument;
|
|
316
355
|
const lineStart = this.layout.document.getLine(row.lastLine).start;
|
|
317
356
|
const geometry = scanLine({ start: row.tail!, end: row.tail! + tail.length, text: tail }, this.layout.metrics, null, []);
|
|
357
|
+
|
|
318
358
|
for (const run of geometry.runs) {
|
|
319
359
|
const span = document.createElement('span');
|
|
320
360
|
span.className = 'se-run';
|
|
@@ -323,6 +363,7 @@ export class RowPainter {
|
|
|
323
363
|
fillRun(span, geometry, geometry.start + run.from, geometry.start + run.to, tokens, false, lineStart);
|
|
324
364
|
element.append(span);
|
|
325
365
|
}
|
|
366
|
+
|
|
326
367
|
appendBidiSigns(element, geometry, this.layout.metrics.lineHeight, { first: 0, last: 0 }, left, (header.subRows - 1) * this.layout.metrics.lineHeight);
|
|
327
368
|
}
|
|
328
369
|
|
|
@@ -330,38 +371,56 @@ export class RowPainter {
|
|
|
330
371
|
private paintReplacement(row: TextRow, replacement: BlockReplacement, paint: RowPaint): boolean {
|
|
331
372
|
const revision = replacement.revision ?? 0;
|
|
332
373
|
let entry = this.rows.get(row.key);
|
|
374
|
+
|
|
333
375
|
if (!entry) {
|
|
334
376
|
const element = this.code.ownerDocument.createElement('div');
|
|
335
377
|
element.className = 'se-block se-replacement';
|
|
336
378
|
element.dataset.rowKey = row.key;
|
|
337
379
|
element.dataset.replacementId = replacement.id;
|
|
338
380
|
element.setAttribute('role', 'group');
|
|
381
|
+
|
|
339
382
|
if (replacement.label !== undefined) {
|
|
340
383
|
element.setAttribute('aria-label', replacement.label);
|
|
341
384
|
}
|
|
385
|
+
|
|
342
386
|
replacement.render(element);
|
|
343
387
|
this.code.append(element);
|
|
344
|
-
entry =
|
|
345
|
-
this.rows.set(row.key, entry);
|
|
388
|
+
entry = this.hostEntry(row.key, element, revision);
|
|
346
389
|
} else if (entry.revision !== revision) {
|
|
347
390
|
entry.element.replaceChildren();
|
|
348
391
|
replacement.render(entry.element);
|
|
349
392
|
entry.revision = revision;
|
|
350
393
|
}
|
|
394
|
+
|
|
351
395
|
const indent = this.layout.indentX(row.line);
|
|
352
396
|
entry.element.style.top = `${row.top}px`;
|
|
353
397
|
entry.element.style.left = `${indent}px`;
|
|
354
398
|
entry.element.style.width = `${Math.max(80, paint.viewportWidth - indent)}px`;
|
|
355
|
-
|
|
356
|
-
this.
|
|
357
|
-
|
|
358
|
-
|
|
399
|
+
|
|
400
|
+
return this.measure(row.key, entry.element);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private hostEntry(key: string, element: HTMLElement, revision: number): PaintedRow {
|
|
404
|
+
const entry = { element, text: '', tokens: null, layoutVersion: 0, whitespace: false, folded: false, tail: '', tailTokens: null, revision };
|
|
405
|
+
this.rows.set(key, entry);
|
|
406
|
+
|
|
407
|
+
return entry;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* Lets the host's content set the height, and returns whether that moved the layout. */
|
|
411
|
+
private measure(key: string, element: HTMLElement): boolean {
|
|
412
|
+
element.style.height = '';
|
|
413
|
+
this.observer?.observe(element);
|
|
414
|
+
const height = element.getBoundingClientRect?.().height ?? 0;
|
|
415
|
+
|
|
416
|
+
return height > 0 && this.layout.setMeasuredHeight(key, height);
|
|
359
417
|
}
|
|
360
418
|
|
|
361
419
|
private paintBlock(row: Extract<LayoutRow, { kind: 'block' }>, paint: RowPaint): boolean {
|
|
362
420
|
const { widget } = row;
|
|
363
421
|
const revision = widget.revision ?? 0;
|
|
364
422
|
let entry = this.rows.get(row.key);
|
|
423
|
+
|
|
365
424
|
if (!entry) {
|
|
366
425
|
const element = this.code.ownerDocument.createElement('div');
|
|
367
426
|
element.className = 'se-block';
|
|
@@ -369,37 +428,42 @@ export class RowPainter {
|
|
|
369
428
|
element.dataset.widgetId = widget.id;
|
|
370
429
|
this.fillBlock(element, widget);
|
|
371
430
|
this.code.append(element);
|
|
431
|
+
|
|
372
432
|
if (!widget.lens && !widget.fixed) {
|
|
373
433
|
this.observer?.observe(element);
|
|
374
434
|
}
|
|
375
|
-
|
|
376
|
-
this.
|
|
435
|
+
|
|
436
|
+
entry = this.hostEntry(row.key, element, revision);
|
|
377
437
|
} else if (entry.revision !== revision) {
|
|
378
438
|
entry.element.replaceChildren();
|
|
379
439
|
this.fillBlock(entry.element, widget);
|
|
380
440
|
entry.revision = revision;
|
|
381
441
|
}
|
|
442
|
+
|
|
382
443
|
entry.element.style.top = `${row.top}px`;
|
|
444
|
+
|
|
383
445
|
if (widget.lens) {
|
|
384
446
|
entry.element.style.left = `${this.layout.indentX(row.line)}px`;
|
|
385
447
|
entry.element.style.height = `${row.height}px`;
|
|
448
|
+
|
|
386
449
|
return false;
|
|
387
450
|
}
|
|
451
|
+
|
|
388
452
|
if (widget.render) {
|
|
389
453
|
entry.element.style.left = `${paint.hostLeft}px`;
|
|
390
454
|
entry.element.style.width = `${paint.hostWidth}px`;
|
|
391
455
|
} else {
|
|
392
456
|
entry.element.style.width = `${Math.max(80, paint.viewportWidth)}px`;
|
|
393
457
|
}
|
|
458
|
+
|
|
394
459
|
if (widget.fixed) {
|
|
395
460
|
this.observer?.unobserve(entry.element);
|
|
396
461
|
entry.element.style.height = `${row.height}px`;
|
|
462
|
+
|
|
397
463
|
return false;
|
|
398
464
|
}
|
|
399
|
-
|
|
400
|
-
this.
|
|
401
|
-
const height = entry.element.getBoundingClientRect?.().height ?? 0;
|
|
402
|
-
return height > 0 && this.layout.setMeasuredHeight(row.key, height);
|
|
465
|
+
|
|
466
|
+
return this.measure(row.key, entry.element);
|
|
403
467
|
}
|
|
404
468
|
|
|
405
469
|
private fillBlock(element: HTMLElement, widget: BlockWidget): void {
|
|
@@ -415,6 +479,7 @@ export class RowPainter {
|
|
|
415
479
|
this.observer?.unobserve(entry.element);
|
|
416
480
|
entry.element.remove();
|
|
417
481
|
}
|
|
482
|
+
|
|
418
483
|
this.rows.clear();
|
|
419
484
|
}
|
|
420
485
|
}
|
|
@@ -446,6 +511,7 @@ function gutterButton(document: Document, className: string, label: string): HTM
|
|
|
446
511
|
button.className = className;
|
|
447
512
|
button.tabIndex = -1;
|
|
448
513
|
button.setAttribute('aria-label', label);
|
|
514
|
+
|
|
449
515
|
return button;
|
|
450
516
|
}
|
|
451
517
|
|
|
@@ -453,76 +519,96 @@ function gutterButton(document: Document, className: string, label: string): HTM
|
|
|
453
519
|
export function paintGutter(container: HTMLElement, layout: EditorLayout, rows: readonly LayoutRow[], paint: GutterPaint): void {
|
|
454
520
|
const document = container.ownerDocument;
|
|
455
521
|
const fragment = document.createDocumentFragment();
|
|
522
|
+
|
|
456
523
|
for (const row of rows) {
|
|
457
524
|
if (row.kind !== 'text') {
|
|
458
525
|
continue;
|
|
459
526
|
}
|
|
527
|
+
|
|
460
528
|
const item = document.createElement('div');
|
|
461
529
|
item.className = paint.activeLines.has(row.line) ? 'se-line-number se-active-number' : 'se-line-number';
|
|
462
530
|
item.style.top = `${row.top}px`;
|
|
463
531
|
item.style.height = `${layout.metrics.lineHeight}px`;
|
|
464
532
|
const highlight = paint.highlights.get(row.line);
|
|
465
533
|
const fill = highlight?.fill;
|
|
534
|
+
|
|
466
535
|
if (fill !== undefined) {
|
|
467
536
|
item.style.background = colorValue(fill);
|
|
468
537
|
}
|
|
538
|
+
|
|
469
539
|
if (highlight?.bar !== undefined) {
|
|
470
540
|
item.classList.add('se-line-marked');
|
|
471
541
|
item.style.height = `${row.height}px`;
|
|
472
542
|
item.style.setProperty('--se-line', `${layout.metrics.lineHeight}px`);
|
|
473
543
|
item.style.setProperty('--se-mark', colorValue(highlight.color));
|
|
544
|
+
|
|
474
545
|
if (fill === undefined) {
|
|
475
546
|
item.style.setProperty('--se-tint', tintValue(highlight.color));
|
|
476
547
|
}
|
|
477
548
|
}
|
|
549
|
+
|
|
478
550
|
const number = document.createElement('span');
|
|
479
551
|
number.textContent = paint.lineNumber(row.line);
|
|
480
552
|
const label = paint.lineLabel(row.line);
|
|
553
|
+
|
|
481
554
|
if (label !== null) {
|
|
482
555
|
number.setAttribute('aria-hidden', 'true');
|
|
483
556
|
}
|
|
557
|
+
|
|
484
558
|
item.append(number);
|
|
559
|
+
|
|
485
560
|
if (label !== null) {
|
|
486
561
|
const hidden = document.createElement('span');
|
|
487
562
|
hidden.className = 'se-sr-only';
|
|
488
563
|
hidden.textContent = label;
|
|
489
564
|
item.append(hidden);
|
|
490
565
|
}
|
|
566
|
+
|
|
491
567
|
if (paint.action?.line === row.line) {
|
|
492
568
|
const button = gutterButton(document, 'se-gutter-action', paint.action.label);
|
|
493
569
|
button.dataset.gutterAction = String(row.line);
|
|
494
570
|
item.append(button);
|
|
495
571
|
}
|
|
572
|
+
|
|
496
573
|
const marker = paint.markers.get(row.line);
|
|
574
|
+
|
|
497
575
|
if (marker !== undefined) {
|
|
498
576
|
const button = gutterButton(document, 'se-gutter-marker', marker.label);
|
|
499
577
|
button.dataset.gutterMarker = marker.id;
|
|
578
|
+
|
|
500
579
|
if (marker.icon !== undefined) {
|
|
501
580
|
button.dataset.icon = marker.icon;
|
|
502
581
|
}
|
|
582
|
+
|
|
503
583
|
if (marker.pressed !== undefined) {
|
|
504
584
|
button.setAttribute('aria-pressed', String(marker.pressed));
|
|
505
585
|
}
|
|
586
|
+
|
|
506
587
|
item.append(button);
|
|
507
588
|
}
|
|
508
|
-
|
|
509
|
-
if (sign) {
|
|
589
|
+
|
|
590
|
+
if (highlight?.sign) {
|
|
510
591
|
const mark = document.createElement('span');
|
|
511
592
|
mark.className = 'se-line-sign';
|
|
512
|
-
mark.textContent = sign;
|
|
593
|
+
mark.textContent = highlight.sign;
|
|
594
|
+
|
|
513
595
|
if (label !== null) {
|
|
514
596
|
mark.setAttribute('aria-hidden', 'true');
|
|
515
597
|
}
|
|
516
|
-
|
|
598
|
+
|
|
599
|
+
mark.style.color = colorValue(highlight.color);
|
|
517
600
|
item.append(mark);
|
|
518
601
|
}
|
|
602
|
+
|
|
519
603
|
if (highlight?.bar !== undefined) {
|
|
520
604
|
const mark = document.createElement('span');
|
|
521
605
|
mark.className = `se-line-bar se-line-bar-${highlight.bar}`;
|
|
522
606
|
mark.setAttribute('aria-hidden', 'true');
|
|
523
607
|
item.append(mark);
|
|
524
608
|
}
|
|
609
|
+
|
|
525
610
|
const bar = paint.attribution.get(row.line);
|
|
611
|
+
|
|
526
612
|
if (bar) {
|
|
527
613
|
const mark = document.createElement('span');
|
|
528
614
|
mark.className = 'se-attribution';
|
|
@@ -531,25 +617,33 @@ export function paintGutter(container: HTMLElement, layout: EditorLayout, rows:
|
|
|
531
617
|
mark.style.height = `${row.height}px`;
|
|
532
618
|
item.append(mark);
|
|
533
619
|
}
|
|
620
|
+
|
|
534
621
|
const change = paint.changes.get(row.line);
|
|
622
|
+
|
|
535
623
|
// The bar of an agent stands where the mark of a change would, and a removal is a triangle between lines that the bar never hides.
|
|
536
624
|
if (change && (!bar || change === 'deleted') && highlight?.bar === undefined) {
|
|
537
625
|
const mark = document.createElement('span');
|
|
538
626
|
mark.className = `se-change se-change-${change}`;
|
|
627
|
+
|
|
539
628
|
if (change !== 'deleted') {
|
|
540
629
|
mark.style.height = `${row.height}px`;
|
|
541
630
|
}
|
|
631
|
+
|
|
542
632
|
item.append(mark);
|
|
543
633
|
}
|
|
634
|
+
|
|
544
635
|
const collapsed = paint.foldable.get(row.line);
|
|
636
|
+
|
|
545
637
|
if (collapsed !== undefined) {
|
|
546
638
|
const button = gutterButton(document, collapsed ? 'se-fold-toggle se-folded' : 'se-fold-toggle', paint.foldName(row.line, collapsed));
|
|
547
639
|
button.setAttribute('aria-expanded', String(!collapsed));
|
|
548
640
|
button.dataset.foldLine = String(row.line);
|
|
549
641
|
item.append(button);
|
|
550
642
|
}
|
|
643
|
+
|
|
551
644
|
fragment.append(item);
|
|
552
645
|
}
|
|
646
|
+
|
|
553
647
|
container.replaceChildren(fragment);
|
|
554
648
|
}
|
|
555
649
|
|
|
@@ -567,6 +661,7 @@ function box(document: Document, className: string, rect: LayoutRect): HTMLEleme
|
|
|
567
661
|
element.style.top = `${rect.y}px`;
|
|
568
662
|
element.style.width = `${Math.max(1, rect.width)}px`;
|
|
569
663
|
element.style.height = `${rect.height}px`;
|
|
664
|
+
|
|
570
665
|
return element;
|
|
571
666
|
}
|
|
572
667
|
|
|
@@ -574,21 +669,27 @@ function box(document: Document, className: string, rect: LayoutRect): HTMLEleme
|
|
|
574
669
|
export function paintOverlays(container: HTMLElement, paint: OverlayPaint): void {
|
|
575
670
|
const document = container.ownerDocument;
|
|
576
671
|
const fragment = document.createDocumentFragment();
|
|
672
|
+
|
|
577
673
|
if (paint.currentLine) {
|
|
578
674
|
fragment.append(box(document, 'se-current-line', paint.currentLine));
|
|
579
675
|
}
|
|
676
|
+
|
|
580
677
|
for (const mark of paint.marks) {
|
|
581
678
|
for (const rect of mark.rects) {
|
|
582
679
|
const element = box(document, mark.className, rect);
|
|
680
|
+
|
|
583
681
|
if (mark.background) {
|
|
584
682
|
element.style.background = mark.background;
|
|
585
683
|
}
|
|
684
|
+
|
|
586
685
|
fragment.append(element);
|
|
587
686
|
}
|
|
588
687
|
}
|
|
688
|
+
|
|
589
689
|
for (const rect of paint.selections) {
|
|
590
690
|
fragment.append(box(document, paint.focused ? 'se-selection' : 'se-selection se-selection-inactive', rect));
|
|
591
691
|
}
|
|
692
|
+
|
|
592
693
|
container.replaceChildren(fragment);
|
|
593
694
|
}
|
|
594
695
|
|
|
@@ -596,17 +697,19 @@ export function paintOverlays(container: HTMLElement, paint: OverlayPaint): void
|
|
|
596
697
|
export function paintOver(container: HTMLElement, layers: readonly { className: string; rects: readonly LayoutRect[] }[]): void {
|
|
597
698
|
const document = container.ownerDocument;
|
|
598
699
|
const fragment = document.createDocumentFragment();
|
|
700
|
+
|
|
599
701
|
for (const layer of layers) {
|
|
600
702
|
for (const rect of layer.rects) {
|
|
601
703
|
fragment.append(box(document, layer.className, rect));
|
|
602
704
|
}
|
|
603
705
|
}
|
|
706
|
+
|
|
604
707
|
container.replaceChildren(fragment);
|
|
605
708
|
}
|
|
606
709
|
|
|
607
|
-
/* The carets, above the text. */
|
|
608
710
|
export function paintCarets(container: HTMLElement, carets: readonly (LayoutRect & { primary: boolean; drop?: boolean })[]): void {
|
|
609
711
|
const document = container.ownerDocument;
|
|
712
|
+
|
|
610
713
|
container.replaceChildren(
|
|
611
714
|
...carets.map((rect) => box(document, rect.drop ? 'se-drop-caret' : rect.primary ? 'se-caret se-primary-caret' : 'se-caret', { ...rect, width: 2 }))
|
|
612
715
|
);
|
|
@@ -626,6 +729,7 @@ export interface RemoteCaret {
|
|
|
626
729
|
/* The carets of agents with their names, above the text; they take no pointer and never move the real caret. */
|
|
627
730
|
export function paintRemoteCarets(container: HTMLElement, carets: readonly RemoteCaret[]): void {
|
|
628
731
|
const document = container.ownerDocument;
|
|
732
|
+
|
|
629
733
|
container.replaceChildren(
|
|
630
734
|
...carets.map((caret) => {
|
|
631
735
|
const element = box(document, 'se-remote-caret', { x: caret.x, y: caret.y, width: 2, height: caret.height });
|
|
@@ -635,10 +739,13 @@ export function paintRemoteCarets(container: HTMLElement, carets: readonly Remot
|
|
|
635
739
|
label.className = 'se-remote-label';
|
|
636
740
|
label.textContent = caret.name;
|
|
637
741
|
label.style.background = colorValue(caret.color);
|
|
742
|
+
|
|
638
743
|
if (caret.below) {
|
|
639
744
|
label.dataset.below = 'true';
|
|
640
745
|
}
|
|
746
|
+
|
|
641
747
|
element.append(label);
|
|
748
|
+
|
|
642
749
|
return element;
|
|
643
750
|
})
|
|
644
751
|
);
|
|
@@ -654,6 +761,7 @@ export interface StickyEntry {
|
|
|
654
761
|
/* The headers pinned at the top: a line number in the gutter's column, then the header's own text. */
|
|
655
762
|
export function paintSticky(container: HTMLElement, entries: readonly StickyEntry[], lineHeight: number): void {
|
|
656
763
|
const document = container.ownerDocument;
|
|
764
|
+
|
|
657
765
|
container.replaceChildren(
|
|
658
766
|
...entries.map((entry, depth) => {
|
|
659
767
|
const row = document.createElement('div');
|
|
@@ -667,6 +775,7 @@ export function paintSticky(container: HTMLElement, entries: readonly StickyEntr
|
|
|
667
775
|
code.className = 'se-sticky-code';
|
|
668
776
|
code.append(lineElement(document, entry.geometry, entry.tokens, lineHeight, { first: 0, last: 0 }));
|
|
669
777
|
row.append(number, code);
|
|
778
|
+
|
|
670
779
|
return row;
|
|
671
780
|
})
|
|
672
781
|
);
|
|
@@ -695,6 +804,7 @@ export function arrowSign(document: Document, width: number, lineHeight: number,
|
|
|
695
804
|
path.setAttribute('stroke', 'currentColor');
|
|
696
805
|
path.setAttribute('stroke-width', '1');
|
|
697
806
|
svg.append(path);
|
|
807
|
+
|
|
698
808
|
return svg as SVGSVGElement;
|
|
699
809
|
}
|
|
700
810
|
|
|
@@ -703,20 +813,26 @@ function appendTabSigns(element: HTMLElement, geometry: LineGeometry, lineHeight
|
|
|
703
813
|
if (geometry.uniform !== undefined) {
|
|
704
814
|
return;
|
|
705
815
|
}
|
|
816
|
+
|
|
706
817
|
const document = element.ownerDocument;
|
|
707
818
|
const end = geometry.rowStarts[band.last + 1] ?? stopCount(geometry) - 1;
|
|
819
|
+
|
|
708
820
|
for (let stop = geometry.rowStarts[band.first]!; stop < end; stop++) {
|
|
709
821
|
if (geometry.text[offsetOfStop(geometry, stop)] !== '\t') {
|
|
710
822
|
continue;
|
|
711
823
|
}
|
|
824
|
+
|
|
825
|
+
const row = rowOfStop(geometry, stop);
|
|
712
826
|
const left = xOfStop(geometry, stop);
|
|
713
|
-
const right = rowOfStop(geometry, stop + 1) ===
|
|
827
|
+
const right = rowOfStop(geometry, stop + 1) === row ? xOfStop(geometry, stop + 1) : geometry.rowEnds[row]!;
|
|
828
|
+
|
|
714
829
|
if (right - left < 3) {
|
|
715
830
|
continue;
|
|
716
831
|
}
|
|
832
|
+
|
|
717
833
|
const sign = arrowSign(document, right - left - 2, lineHeight, 'se-tab-sign');
|
|
718
834
|
sign.style.left = `${left + 1}px`;
|
|
719
|
-
sign.style.top = `${
|
|
835
|
+
sign.style.top = `${row * lineHeight}px`;
|
|
720
836
|
element.append(sign);
|
|
721
837
|
}
|
|
722
838
|
}
|
|
@@ -729,11 +845,13 @@ function appendBidiSigns(element: HTMLElement, geometry: LineGeometry, lineHeigh
|
|
|
729
845
|
if (geometry.uniform !== undefined) {
|
|
730
846
|
return;
|
|
731
847
|
}
|
|
848
|
+
|
|
732
849
|
const document = element.ownerDocument;
|
|
733
850
|
const from = offsetOfStop(geometry, geometry.rowStarts[band.first]!);
|
|
734
851
|
const to = geometry.rowStarts[band.last + 1] === undefined ? geometry.text.length : offsetOfStop(geometry, geometry.rowStarts[band.last + 1]!);
|
|
735
852
|
const pattern = new RegExp(BIDI_PATTERN.source, 'g');
|
|
736
853
|
pattern.lastIndex = from;
|
|
854
|
+
|
|
737
855
|
for (let match = pattern.exec(geometry.text); match !== null && match.index < to; match = pattern.exec(geometry.text)) {
|
|
738
856
|
const stop = stopAtOffset(geometry, match.index);
|
|
739
857
|
const row = rowOfStop(geometry, stop);
|
|
@@ -760,11 +878,13 @@ export interface WrapSign {
|
|
|
760
878
|
/* The signs of the soft wraps: an arrow where a row ends in the middle of a line, and one at the start of the row that goes on. */
|
|
761
879
|
export function paintSigns(container: HTMLElement, signs: readonly WrapSign[], lineHeight: number): void {
|
|
762
880
|
const document = container.ownerDocument;
|
|
881
|
+
|
|
763
882
|
container.replaceChildren(
|
|
764
883
|
...signs.map((sign) => {
|
|
765
884
|
const arrow = arrowSign(document, sign.width, lineHeight, 'se-wrap-sign');
|
|
766
885
|
arrow.style.left = `${sign.x}px`;
|
|
767
886
|
arrow.style.top = `${sign.y}px`;
|
|
887
|
+
|
|
768
888
|
return arrow;
|
|
769
889
|
})
|
|
770
890
|
);
|
package/src/php-html.ts
CHANGED
|
@@ -38,6 +38,7 @@ export async function loadDocumentLanguage(highlighter: Highlighter, language: s
|
|
|
38
38
|
if (language !== PHP_DOCUMENT_ID || highlighter.getLoadedLanguages().includes(PHP_DOCUMENT_ID)) {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
+
|
|
41
42
|
await highlighter.loadLanguage('php');
|
|
42
43
|
await highlighter.loadLanguage(PHP_DOCUMENT);
|
|
43
44
|
}
|