@adecore/editor 0.21.1 → 0.22.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/code-block.js +26 -22
- package/dist/controller.d.ts +1 -0
- package/dist/controller.js +29 -43
- package/dist/engine.js +34 -42
- package/dist/fake.js +44 -97
- package/dist/keymap.d.ts +1 -2
- package/dist/keymap.js +18 -15
- package/dist/layout.d.ts +1 -1
- package/dist/layout.js +7 -6
- package/dist/listeners.d.ts +2 -2
- package/dist/listeners.js +2 -2
- package/dist/modifier-gesture.js +2 -2
- package/dist/native-input.d.ts +1 -0
- package/dist/native-input.js +5 -3
- package/dist/outline.js +2 -2
- package/dist/overview.js +0 -1
- package/dist/paint.js +11 -15
- package/dist/pointer.js +9 -2
- package/dist/testing.js +8 -5
- package/dist/tokens.d.ts +1 -0
- package/dist/tokens.js +9 -10
- package/dist/view.d.ts +1 -0
- package/dist/view.js +63 -92
- package/package.json +2 -2
- package/src/code-block.ts +27 -22
- package/src/controller.ts +32 -43
- package/src/engine.ts +37 -46
- package/src/fake.ts +44 -97
- package/src/keymap.ts +20 -17
- package/src/layout.ts +7 -6
- package/src/listeners.ts +3 -3
- package/src/modifier-gesture.ts +2 -2
- package/src/native-input.ts +6 -3
- package/src/outline.ts +2 -2
- package/src/overview.ts +0 -1
- package/src/paint.ts +12 -15
- package/src/pointer.ts +10 -2
- package/src/testing.ts +9 -5
- package/src/tokens.ts +10 -10
- package/src/types.ts +14 -8
- package/src/view.ts +73 -99
package/dist/code-block.js
CHANGED
|
@@ -17,28 +17,7 @@ export function renderCodeBlock(container, text, tokens, tabSize, options) {
|
|
|
17
17
|
if (options.color !== undefined) {
|
|
18
18
|
row.style.background = tintValue(options.color);
|
|
19
19
|
}
|
|
20
|
-
const gutter = document
|
|
21
|
-
gutter.className = 'se-code-gutter';
|
|
22
|
-
if (options.firstLine !== undefined) {
|
|
23
|
-
const number = document.createElement('span');
|
|
24
|
-
number.textContent = String(options.firstLine + index);
|
|
25
|
-
gutter.append(number);
|
|
26
|
-
}
|
|
27
|
-
if (options.sign) {
|
|
28
|
-
const sign = document.createElement('span');
|
|
29
|
-
sign.className = 'se-line-sign';
|
|
30
|
-
sign.textContent = options.sign;
|
|
31
|
-
if (options.color !== undefined) {
|
|
32
|
-
sign.style.color = colorValue(options.color);
|
|
33
|
-
}
|
|
34
|
-
gutter.append(sign);
|
|
35
|
-
}
|
|
36
|
-
if (options.color !== undefined) {
|
|
37
|
-
const bar = document.createElement('span');
|
|
38
|
-
bar.className = 'se-attribution';
|
|
39
|
-
bar.style.background = colorValue(options.color);
|
|
40
|
-
gutter.append(bar);
|
|
41
|
-
}
|
|
20
|
+
const gutter = codeGutter(document, options, index);
|
|
42
21
|
const code = document.createElement('div');
|
|
43
22
|
code.className = 'se-code-text';
|
|
44
23
|
if (options.faded) {
|
|
@@ -54,6 +33,31 @@ export function renderCodeBlock(container, text, tokens, tabSize, options) {
|
|
|
54
33
|
});
|
|
55
34
|
container.append(block);
|
|
56
35
|
}
|
|
36
|
+
function codeGutter(document, options, index) {
|
|
37
|
+
const gutter = document.createElement('div');
|
|
38
|
+
gutter.className = 'se-code-gutter';
|
|
39
|
+
if (options.firstLine !== undefined) {
|
|
40
|
+
const number = document.createElement('span');
|
|
41
|
+
number.textContent = String(options.firstLine + index);
|
|
42
|
+
gutter.append(number);
|
|
43
|
+
}
|
|
44
|
+
if (options.sign) {
|
|
45
|
+
const sign = document.createElement('span');
|
|
46
|
+
sign.className = 'se-line-sign';
|
|
47
|
+
sign.textContent = options.sign;
|
|
48
|
+
if (options.color !== undefined) {
|
|
49
|
+
sign.style.color = colorValue(options.color);
|
|
50
|
+
}
|
|
51
|
+
gutter.append(sign);
|
|
52
|
+
}
|
|
53
|
+
if (options.color !== undefined) {
|
|
54
|
+
const bar = document.createElement('span');
|
|
55
|
+
bar.className = 'se-attribution';
|
|
56
|
+
bar.style.background = colorValue(options.color);
|
|
57
|
+
gutter.append(bar);
|
|
58
|
+
}
|
|
59
|
+
return gutter;
|
|
60
|
+
}
|
|
57
61
|
function fill(element, line, tokens, emphasis) {
|
|
58
62
|
if (tokens === null && emphasis.length === 0) {
|
|
59
63
|
element.textContent = line;
|
package/dist/controller.d.ts
CHANGED
package/dist/controller.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { replacementEdits } from './input.js';
|
|
2
|
+
import { emit, subscribe } from './listeners.js';
|
|
2
3
|
import { chordMatches, keyAction } from './keymap.js';
|
|
3
4
|
import { DoubleModifierGesture } from './modifier-gesture.js';
|
|
4
5
|
import { NativeInput } from './native-input.js';
|
|
5
6
|
import { columnSelections, sameCell } from './column-selection.js';
|
|
6
7
|
import { PointerSelection } from './pointer.js';
|
|
7
8
|
const MODIFYING = new Set([
|
|
8
|
-
'undo',
|
|
9
|
-
'redo',
|
|
10
9
|
'deleteWordLeft',
|
|
11
10
|
'deleteWordRight',
|
|
12
11
|
'deleteCamelLeft',
|
|
@@ -49,6 +48,9 @@ const VIEW_COMMANDS = new Set([
|
|
|
49
48
|
]);
|
|
50
49
|
/* The clipboard text of the last copy or cut of a bare caret, which pastes back as whole lines. Shared, since it can be pasted in another editor. */
|
|
51
50
|
let lineClip = null;
|
|
51
|
+
function selectionSignature(selections) {
|
|
52
|
+
return selections.map((selection) => `${selection.anchor}:${selection.head}`).join(',');
|
|
53
|
+
}
|
|
52
54
|
/* Typing in the same stretch is one undo step until a pause this long. */
|
|
53
55
|
const TYPING_PAUSE_MS = 750;
|
|
54
56
|
/*
|
|
@@ -330,7 +332,7 @@ export class InputController {
|
|
|
330
332
|
}
|
|
331
333
|
modelCommand(name) {
|
|
332
334
|
const { model } = this.view;
|
|
333
|
-
const modifies = MODIFYING.has(name)
|
|
335
|
+
const modifies = MODIFYING.has(name);
|
|
334
336
|
if ((modifies || name === 'undo' || name === 'redo') && this.blockedByReadOnly()) {
|
|
335
337
|
return false;
|
|
336
338
|
}
|
|
@@ -402,7 +404,7 @@ export class InputController {
|
|
|
402
404
|
/* Column mode on drops every caret but the first and keeps a selection as a box; off it makes one selection of the box. */
|
|
403
405
|
toggleColumnMode() {
|
|
404
406
|
const { view } = this;
|
|
405
|
-
const { model
|
|
407
|
+
const { model } = view;
|
|
406
408
|
this.block = null;
|
|
407
409
|
if (view.columnMode) {
|
|
408
410
|
const first = model.getSelections()[0];
|
|
@@ -415,22 +417,16 @@ export class InputController {
|
|
|
415
417
|
const primary = model.getPrimary();
|
|
416
418
|
model.setSelections([primary]);
|
|
417
419
|
if (primary.anchor !== primary.head) {
|
|
418
|
-
|
|
419
|
-
const head = layout.caret(primary.head);
|
|
420
|
-
this.block = { anchor: { x: anchor.x, y: anchor.y }, head: { x: head.x, y: head.y }, signature: '' };
|
|
420
|
+
this.block = this.blockOf(primary, '');
|
|
421
421
|
this.extendBlock({ x: 0, y: 0 });
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
/* Moves the corner of the box by a step and selects the columns between it and the other corner. */
|
|
425
425
|
extendBlock(step) {
|
|
426
426
|
const { model, layout } = this.view;
|
|
427
|
-
const
|
|
428
|
-
const signature = selections.map((selection) => `${selection.anchor}:${selection.head}`).join(',');
|
|
427
|
+
const signature = selectionSignature(model.getSelections());
|
|
429
428
|
if (this.block === null || (this.block.signature !== '' && this.block.signature !== signature)) {
|
|
430
|
-
|
|
431
|
-
const anchor = layout.caret(primary.anchor);
|
|
432
|
-
const head = layout.caret(primary.head);
|
|
433
|
-
this.block = { anchor: { x: anchor.x, y: anchor.y }, head: { x: head.x, y: head.y }, signature };
|
|
429
|
+
this.block = this.blockOf(model.getPrimary(), signature);
|
|
434
430
|
}
|
|
435
431
|
const block = this.block;
|
|
436
432
|
block.head = {
|
|
@@ -438,10 +434,14 @@ export class InputController {
|
|
|
438
434
|
y: Math.max(0, Math.min(layout.height - layout.metrics.lineHeight, block.head.y + step.y))
|
|
439
435
|
};
|
|
440
436
|
model.setSelections(columnSelections(layout, block.anchor, block.head));
|
|
441
|
-
block.signature = model
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
437
|
+
block.signature = selectionSignature(model.getSelections());
|
|
438
|
+
}
|
|
439
|
+
/* A box between the two ends of a selection. */
|
|
440
|
+
blockOf(selection, signature) {
|
|
441
|
+
const { layout } = this.view;
|
|
442
|
+
const anchor = layout.caret(selection.anchor);
|
|
443
|
+
const head = layout.caret(selection.head);
|
|
444
|
+
return { anchor: { x: anchor.x, y: anchor.y }, head: { x: head.x, y: head.y }, signature };
|
|
445
445
|
}
|
|
446
446
|
moveCarets(key, extend) {
|
|
447
447
|
if (this.view.columnMode && extend && (key === 'ArrowUp' || key === 'ArrowDown' || key === 'ArrowLeft' || key === 'ArrowRight')) {
|
|
@@ -460,20 +460,17 @@ export class InputController {
|
|
|
460
460
|
const { model, layout } = this.view;
|
|
461
461
|
const vertical = key === 'ArrowUp' || key === 'ArrowDown';
|
|
462
462
|
const direction = key === 'ArrowLeft' || key === 'ArrowUp' ? -1 : 1;
|
|
463
|
+
const edge = (selection) => direction < 0 ? Math.min(selection.anchor, selection.head) : Math.max(selection.anchor, selection.head);
|
|
463
464
|
const selections = model.getSelections().map((selection, index) => {
|
|
465
|
+
const collapses = !extend && selection.anchor !== selection.head;
|
|
464
466
|
let head;
|
|
465
|
-
if (!vertical &&
|
|
466
|
-
head =
|
|
467
|
+
if (!vertical && collapses) {
|
|
468
|
+
head = edge(selection);
|
|
467
469
|
}
|
|
468
470
|
else if (vertical) {
|
|
469
471
|
// A selection first gives way to the edge in the direction of the move, as the platform's Up and Down do.
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
? direction < 0
|
|
473
|
-
? Math.min(selection.anchor, selection.head)
|
|
474
|
-
: Math.max(selection.anchor, selection.head)
|
|
475
|
-
: selection.head;
|
|
476
|
-
if (hasSelection) {
|
|
472
|
+
const from = collapses ? edge(selection) : selection.head;
|
|
473
|
+
if (collapses) {
|
|
477
474
|
delete this.desiredXs[index];
|
|
478
475
|
}
|
|
479
476
|
this.desiredXs[index] ??= layout.caret(from).x;
|
|
@@ -578,22 +575,13 @@ export class InputController {
|
|
|
578
575
|
}
|
|
579
576
|
/* A handler sees the key before the editor does, the page's own shortcuts included, since a list of suggestions owns the arrows while it is open. */
|
|
580
577
|
onKeyDown(handler) {
|
|
581
|
-
this.keyHandlers
|
|
582
|
-
return () => {
|
|
583
|
-
this.keyHandlers.delete(handler);
|
|
584
|
-
};
|
|
578
|
+
return subscribe(this.keyHandlers, handler);
|
|
585
579
|
}
|
|
586
580
|
onClick(handler) {
|
|
587
|
-
this.clickHandlers
|
|
588
|
-
return () => {
|
|
589
|
-
this.clickHandlers.delete(handler);
|
|
590
|
-
};
|
|
581
|
+
return subscribe(this.clickHandlers, handler);
|
|
591
582
|
}
|
|
592
583
|
onContextMenu(listener) {
|
|
593
|
-
this.contextListeners
|
|
594
|
-
return () => {
|
|
595
|
-
this.contextListeners.delete(listener);
|
|
596
|
-
};
|
|
584
|
+
return subscribe(this.contextListeners, listener);
|
|
597
585
|
}
|
|
598
586
|
keydown(event) {
|
|
599
587
|
if (event.isComposing || this.native.composing) {
|
|
@@ -667,9 +655,9 @@ export class InputController {
|
|
|
667
655
|
? selections
|
|
668
656
|
.map((selection) => {
|
|
669
657
|
const { first, last } = this.view.lineSpan(model.positionAt(selection.head).line);
|
|
670
|
-
|
|
658
|
+
const end = model.getLine(last);
|
|
659
|
+
return model.slice(model.getLine(first).start, end.next) + (end.next === end.end ? '\n' : '');
|
|
671
660
|
})
|
|
672
|
-
.map((lines) => model.slice(lines.start, lines.next) + (lines.next === lines.end ? '\n' : ''))
|
|
673
661
|
.join('')
|
|
674
662
|
: selections.map((selection) => model.slice(Math.min(selection.anchor, selection.head), Math.max(selection.anchor, selection.head))).join('\n');
|
|
675
663
|
event.clipboardData?.setData('text/plain', text);
|
|
@@ -730,9 +718,7 @@ export class InputController {
|
|
|
730
718
|
.getSelections()
|
|
731
719
|
.some((candidate) => Math.min(candidate.anchor, candidate.head) <= offset && offset <= Math.max(candidate.anchor, candidate.head));
|
|
732
720
|
const menu = { position: this.positionOf(offset), inSelection: selection, x: event.clientX, y: event.clientY };
|
|
733
|
-
|
|
734
|
-
listener(menu);
|
|
735
|
-
}
|
|
721
|
+
emit(this.contextListeners, menu);
|
|
736
722
|
}
|
|
737
723
|
/* A press on a pinned header goes to that header. */
|
|
738
724
|
stickyDown(event) {
|
package/dist/engine.js
CHANGED
|
@@ -134,10 +134,7 @@ class SmartEditor {
|
|
|
134
134
|
return subscribe(this.changes, listener);
|
|
135
135
|
}
|
|
136
136
|
onTextChange(listener) {
|
|
137
|
-
this.textChanges
|
|
138
|
-
return () => {
|
|
139
|
-
this.textChanges.delete(listener);
|
|
140
|
-
};
|
|
137
|
+
return subscribe(this.textChanges, listener);
|
|
141
138
|
}
|
|
142
139
|
announceTextChange(edits, source) {
|
|
143
140
|
const change = {
|
|
@@ -147,9 +144,7 @@ class SmartEditor {
|
|
|
147
144
|
text: edit.text
|
|
148
145
|
}))
|
|
149
146
|
};
|
|
150
|
-
|
|
151
|
-
listener(change);
|
|
152
|
-
}
|
|
147
|
+
emit(this.textChanges, change);
|
|
153
148
|
}
|
|
154
149
|
caretMoved(head) {
|
|
155
150
|
if (head === this.caretOffset) {
|
|
@@ -157,9 +152,7 @@ class SmartEditor {
|
|
|
157
152
|
}
|
|
158
153
|
this.caretOffset = head;
|
|
159
154
|
const position = this.positionAt(head);
|
|
160
|
-
|
|
161
|
-
listener(position);
|
|
162
|
-
}
|
|
155
|
+
emit(this.carets, position);
|
|
163
156
|
}
|
|
164
157
|
getCaret() {
|
|
165
158
|
return this.positionAt(this.model.getPrimary().head);
|
|
@@ -168,14 +161,14 @@ class SmartEditor {
|
|
|
168
161
|
return this.view.scrollPlace.y;
|
|
169
162
|
}
|
|
170
163
|
getSelection() {
|
|
171
|
-
|
|
172
|
-
return { start: this.positionAt(Math.min(anchor, head)), end: this.positionAt(Math.max(anchor, head)) };
|
|
164
|
+
return this.rangeOf(this.model.getPrimary());
|
|
173
165
|
}
|
|
174
166
|
getSelections() {
|
|
175
|
-
return this.model.getSelections().map((
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
return this.model.getSelections().map((selection) => this.rangeOf(selection));
|
|
168
|
+
}
|
|
169
|
+
/* A selection as a range, start before end. */
|
|
170
|
+
rangeOf({ anchor, head }) {
|
|
171
|
+
return { start: this.positionAt(Math.min(anchor, head)), end: this.positionAt(Math.max(anchor, head)) };
|
|
179
172
|
}
|
|
180
173
|
getIndentation() {
|
|
181
174
|
return { tabSize: this.settings.tabSize, insertSpaces: this.settings.insertSpaces };
|
|
@@ -199,10 +192,7 @@ class SmartEditor {
|
|
|
199
192
|
this.view.revealOffset(this.offsetAt(last.end), reveal);
|
|
200
193
|
}
|
|
201
194
|
onCaret(listener) {
|
|
202
|
-
this.carets
|
|
203
|
-
return () => {
|
|
204
|
-
this.carets.delete(listener);
|
|
205
|
-
};
|
|
195
|
+
return subscribe(this.carets, listener);
|
|
206
196
|
}
|
|
207
197
|
onHover(listener) {
|
|
208
198
|
return this.view.onHover((offset) => {
|
|
@@ -496,7 +486,7 @@ class SmartEditor {
|
|
|
496
486
|
onGutterAction(listener) {
|
|
497
487
|
return this.view.onGutterAction(listener);
|
|
498
488
|
}
|
|
499
|
-
setGutterMarkers(markers, owner =
|
|
489
|
+
setGutterMarkers(markers, owner = DEFAULT_WIDGET_OWNER) {
|
|
500
490
|
this.view.setGutterMarkers(owner, markers);
|
|
501
491
|
}
|
|
502
492
|
onGutterMarker(listener) {
|
|
@@ -529,27 +519,29 @@ class SmartEditor {
|
|
|
529
519
|
this.view.applySettings();
|
|
530
520
|
}
|
|
531
521
|
setFoldHints(hints) {
|
|
532
|
-
this.view.setFoldHints(hints === null
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
|
|
522
|
+
this.view.setFoldHints(hints === null ? null : this.foldHintsByOffset(hints));
|
|
523
|
+
}
|
|
524
|
+
foldHintsByOffset(hints) {
|
|
525
|
+
const lineOf = (line) => this.model.getLine(Math.min(line, this.model.getLineCount() - 1));
|
|
526
|
+
const within = (line, character) => Math.min(line.end, line.start + Math.max(0, character));
|
|
527
|
+
return {
|
|
528
|
+
symbols: (hints.symbols ?? []).map((symbol) => ({
|
|
529
|
+
from: this.offsetAt(symbol.range.start),
|
|
530
|
+
to: this.offsetAt(symbol.range.end),
|
|
531
|
+
body: symbol.body
|
|
532
|
+
})),
|
|
533
|
+
ranges: (hints.ranges ?? []).map((range) => {
|
|
534
|
+
const first = lineOf(range.startLine);
|
|
535
|
+
const last = lineOf(range.endLine);
|
|
536
|
+
return {
|
|
537
|
+
from: first.start,
|
|
538
|
+
to: last.start,
|
|
539
|
+
...(range.kind === undefined ? {} : { kind: range.kind }),
|
|
540
|
+
...(range.startCharacter === undefined ? {} : { head: within(first, range.startCharacter) }),
|
|
541
|
+
...(range.endCharacter === undefined ? {} : { tail: within(last, range.endCharacter) })
|
|
542
|
+
};
|
|
543
|
+
})
|
|
544
|
+
};
|
|
553
545
|
}
|
|
554
546
|
setSmartKeys(keys) {
|
|
555
547
|
this.settings.smartKeys = { ...this.settings.smartKeys, ...keys };
|