@adecore/editor 0.21.1 → 0.21.2

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.
@@ -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.createElement('div');
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;
@@ -45,6 +45,7 @@ export declare class InputController {
45
45
  private movePages;
46
46
  private toggleColumnMode;
47
47
  private extendBlock;
48
+ private blockOf;
48
49
  private moveCarets;
49
50
  private dropText;
50
51
  private goToMatchingBracket;
@@ -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) && name !== 'undo' && name !== 'redo';
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, layout } = view;
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
- const anchor = layout.caret(primary.anchor);
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 selections = model.getSelections();
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
- const primary = model.getPrimary();
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
- .getSelections()
443
- .map((selection) => `${selection.anchor}:${selection.head}`)
444
- .join(',');
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 && !extend && selection.anchor !== selection.head) {
466
- head = direction < 0 ? Math.min(selection.anchor, selection.head) : Math.max(selection.anchor, selection.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 hasSelection = !extend && selection.anchor !== selection.head;
471
- const from = hasSelection
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.add(handler);
582
- return () => {
583
- this.keyHandlers.delete(handler);
584
- };
578
+ return subscribe(this.keyHandlers, handler);
585
579
  }
586
580
  onClick(handler) {
587
- this.clickHandlers.add(handler);
588
- return () => {
589
- this.clickHandlers.delete(handler);
590
- };
581
+ return subscribe(this.clickHandlers, handler);
591
582
  }
592
583
  onContextMenu(listener) {
593
- this.contextListeners.add(listener);
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
- return { start: model.getLine(first).start, end: model.getLine(last).end, next: model.getLine(last).next };
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
- for (const listener of [...this.contextListeners]) {
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.add(listener);
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
- for (const listener of [...this.textChanges]) {
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
- for (const listener of [...this.carets]) {
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
- const { anchor, head } = this.model.getPrimary();
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(({ anchor, head }) => ({
176
- start: this.positionAt(Math.min(anchor, head)),
177
- end: this.positionAt(Math.max(anchor, head))
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.add(listener);
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 = 'default') {
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
- ? null
534
- : {
535
- symbols: (hints.symbols ?? []).map((symbol) => ({
536
- from: this.offsetAt(symbol.range.start),
537
- to: this.offsetAt(symbol.range.end),
538
- body: symbol.body
539
- })),
540
- ranges: (hints.ranges ?? []).map((range) => {
541
- const first = this.model.getLine(Math.min(range.startLine, this.model.getLineCount() - 1));
542
- const last = this.model.getLine(Math.min(range.endLine, this.model.getLineCount() - 1));
543
- const within = (line, character) => Math.min(line.end, line.start + Math.max(0, character));
544
- return {
545
- from: first.start,
546
- to: last.start,
547
- ...(range.kind === undefined ? {} : { kind: range.kind }),
548
- ...(range.startCharacter === undefined ? {} : { head: within(first, range.startCharacter) }),
549
- ...(range.endCharacter === undefined ? {} : { tail: within(last, range.endCharacter) })
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 };