@adecore/editor 0.42.0 → 0.44.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.
@@ -1,5 +1,5 @@
1
1
  import type { Keymap } from './keymap-table.ts';
2
- import type { EditorClickHandler, EditorInlayClickHandler, EditorContextMenu, EditorKeyHandler, EditorRunCommand, EditorSelectionRanges, KeyChord } from './types.ts';
2
+ import type { EditorClickHandler, EditorInlayClickHandler, EditorContextMenu, EditorCopy, EditorKeyHandler, EditorPasteOptions, EditorRunCommand, EditorSelectionRanges, KeyChord } from './types.ts';
3
3
  import type { EditorView } from './view.ts';
4
4
  export interface ControllerOptions {
5
5
  keymap?: Keymap;
@@ -10,12 +10,16 @@ export interface ControllerOptions {
10
10
  zoom(step: -1 | 0 | 1): void;
11
11
  wheelZooms(): boolean;
12
12
  }
13
+ export declare function rememberCopy(text: string, wholeLines: boolean): void;
13
14
  export declare class InputController {
14
15
  private readonly native;
15
16
  private readonly pointer;
16
17
  private readonly touch;
17
18
  private readonly cleanup;
18
19
  private readonly keyHandlers;
20
+ private readonly navigateListeners;
21
+ private readonly copyListeners;
22
+ private wordSources;
19
23
  private readonly gesture;
20
24
  private readonly clickHandlers;
21
25
  private readonly inlayHandlers;
@@ -41,14 +45,17 @@ export declare class InputController {
41
45
  private applyEdits;
42
46
  private replaceSelections;
43
47
  private unfoldCarets;
44
- private paste;
48
+ pasteText(text: string, options?: EditorPasteOptions): boolean;
49
+ onCopy(listener: (copy: EditorCopy) => void): () => void;
45
50
  private commandOptions;
51
+ setWordSources(source: (() => readonly string[]) | null): void;
46
52
  private viewCommand;
47
53
  private wheel;
48
54
  command(name: EditorRunCommand): boolean;
49
55
  setSelectionRanges(provider: EditorSelectionRanges | null): void;
50
56
  private expandThrough;
51
57
  private modelCommand;
58
+ onNavigate(listener: (from: number) => void): () => void;
52
59
  private typeText;
53
60
  private movePages;
54
61
  private toggleColumnMode;
@@ -16,9 +16,12 @@ const MODIFYING = new Set([
16
16
  'deleteLine',
17
17
  'moveLineUp',
18
18
  'moveLineDown',
19
+ 'moveStatementUp',
20
+ 'moveStatementDown',
19
21
  'toggleLineComment',
20
22
  'toggleBlockComment',
21
23
  'startNewLine',
24
+ 'completeCurrentStatement',
22
25
  'startNewLineBefore',
23
26
  'splitLine',
24
27
  'joinLines',
@@ -34,8 +37,11 @@ const MODIFYING = new Set([
34
37
  'reverseLines',
35
38
  'uniqueLines',
36
39
  'convertIndentsToSpaces',
37
- 'convertIndentsToTabs'
40
+ 'convertIndentsToTabs',
41
+ 'hippieCompletion',
42
+ 'hippieBackwardCompletion'
38
43
  ]);
44
+ const CODE_BLOCK_COMMANDS = new Set(['codeBlockStart', 'codeBlockEnd', 'codeBlockStartWithSelection', 'codeBlockEndWithSelection']);
39
45
  const VIEW_COMMANDS = new Set([
40
46
  'collapseRegion',
41
47
  'expandRegion',
@@ -54,11 +60,23 @@ const VIEW_COMMANDS = new Set([
54
60
  'toggleColumnMode',
55
61
  'increaseFontSize',
56
62
  'decreaseFontSize',
57
- 'resetFontSize'
63
+ 'resetFontSize',
64
+ 'methodUp',
65
+ 'methodDown',
66
+ 'nextChange',
67
+ 'previousChange',
68
+ 'pasteAsPlainText'
58
69
  ]);
59
70
  const ZOOM_STEPS = { increaseFontSize: 1, decreaseFontSize: -1, resetFontSize: 0 };
60
71
  /* 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. */
61
72
  let lineClip = null;
73
+ /*
74
+ * Makes `text` the copy that pastes back as whole lines (or not), for a paste from a history that kept what the
75
+ * copy was. The clipboard itself is the caller's.
76
+ */
77
+ export function rememberCopy(text, wholeLines) {
78
+ lineClip = wholeLines ? text : null;
79
+ }
62
80
  function selectionSignature(selections) {
63
81
  return selections.map((selection) => `${selection.anchor}:${selection.head}`).join(',');
64
82
  }
@@ -76,6 +94,9 @@ export class InputController {
76
94
  touch;
77
95
  cleanup = [];
78
96
  keyHandlers = new Set();
97
+ navigateListeners = new Set();
98
+ copyListeners = new Set();
99
+ wordSources = null;
79
100
  gesture;
80
101
  clickHandlers = new Set();
81
102
  inlayHandlers = new Set();
@@ -186,7 +207,7 @@ export class InputController {
186
207
  });
187
208
  this.listen(input, 'paste', (event) => {
188
209
  event.preventDefault();
189
- this.paste(event.clipboardData?.getData('text/plain') ?? '');
210
+ this.pasteText(event.clipboardData?.getData('text/plain') ?? '');
190
211
  });
191
212
  this.listen(input, 'copy', (event) => this.copy(event, false));
192
213
  this.listen(input, 'cut', (event) => this.copy(event, true));
@@ -245,6 +266,8 @@ export class InputController {
245
266
  }
246
267
  this.cleanup.length = 0;
247
268
  this.keyHandlers.clear();
269
+ this.navigateListeners.clear();
270
+ this.copyListeners.clear();
248
271
  }
249
272
  blockedByReadOnly() {
250
273
  return this.view.blockedByReadOnly();
@@ -278,15 +301,25 @@ export class InputController {
278
301
  this.view.ensureVisible(selection.head);
279
302
  }
280
303
  }
281
- paste(text) {
304
+ /* Pastes as a paste from the clipboard does; false when a read-only editor refused. */
305
+ pasteText(text, options = {}) {
282
306
  if (this.blockedByReadOnly()) {
283
- return;
307
+ return false;
284
308
  }
285
309
  this.unfoldCarets();
286
310
  this.historyGroup++;
287
- this.view.model.paste(this.native.normalizeNewlines(text), { ...this.commandOptions(), wholeLines: text !== '' && text === lineClip });
311
+ const pasted = this.view.model.paste(this.native.normalizeNewlines(text), {
312
+ ...this.commandOptions(),
313
+ ...(options.reindent === false ? { indentOnPaste: false } : {}),
314
+ wholeLines: options.wholeLines ?? (text !== '' && text === lineClip)
315
+ });
288
316
  this.historyGroup++;
289
317
  this.view.revealCaret();
318
+ return pasted;
319
+ }
320
+ /* Told what a copy or cut put on the clipboard. */
321
+ onCopy(listener) {
322
+ return subscribe(this.copyListeners, listener);
290
323
  }
291
324
  commandOptions() {
292
325
  const { settings } = this.view;
@@ -300,14 +333,21 @@ export class InputController {
300
333
  tabOutOfClosers: keys.tabOutOfClosers,
301
334
  smartEnter: keys.smartIndentOnEnter,
302
335
  indentOnPaste: keys.indentOnPaste,
336
+ indentGuides: settings.guides,
303
337
  smartSemicolon: keys.smartSemicolon,
304
338
  smartArrow: keys.smartArrow,
305
339
  language: settings.language ?? 'plaintext',
306
340
  camelCase: keys.camelHumps,
307
341
  visualLine: (offset) => this.view.rowOf(offset),
308
- lineSpan: (line) => this.view.lineSpan(line)
342
+ lineSpan: (line) => this.view.lineSpan(line),
343
+ structure: () => this.view.structureRanges(),
344
+ ...(this.wordSources === null ? {} : { wordSources: this.wordSources })
309
345
  };
310
346
  }
347
+ /* The texts Hippie completion takes words from after those of this document; null takes them away. */
348
+ setWordSources(source) {
349
+ this.wordSources = source;
350
+ }
311
351
  /* Folding is the view's, since it knows where the ranges are. */
312
352
  viewCommand(name) {
313
353
  const { view } = this;
@@ -315,6 +355,25 @@ export class InputController {
315
355
  this.options.zoom(ZOOM_STEPS[name]);
316
356
  return true;
317
357
  }
358
+ if (name === 'methodUp' || name === 'methodDown') {
359
+ const from = view.model.getPrimary().head;
360
+ const moved = view.goToMethod(name === 'methodUp' ? -1 : 1);
361
+ if (moved) {
362
+ emit(this.navigateListeners, from);
363
+ }
364
+ return moved;
365
+ }
366
+ if (name === 'pasteAsPlainText') {
367
+ // The clipboard is read once a person asks, and an editor with no way to read it does nothing.
368
+ if (this.blockedByReadOnly() || globalThis.navigator?.clipboard?.readText === undefined) {
369
+ return false;
370
+ }
371
+ void navigator.clipboard.readText().then((text) => this.pasteText(text, { reindent: false }), () => undefined);
372
+ return true;
373
+ }
374
+ if (name === 'nextChange' || name === 'previousChange') {
375
+ return view.goToChange(name === 'nextChange' ? 1 : -1);
376
+ }
318
377
  if (view.settings.folding === false && name !== 'toggleColumnMode') {
319
378
  return false;
320
379
  }
@@ -413,7 +472,10 @@ export class InputController {
413
472
  }
414
473
  }
415
474
  }
416
- const folded = name === 'moveLineUp' || name === 'moveLineDown' ? this.view.getFolds().collapsed : [];
475
+ const before = model.getPrimary().head;
476
+ const folded = name === 'moveLineUp' || name === 'moveLineDown' || name === 'moveStatementUp' || name === 'moveStatementDown'
477
+ ? this.view.getFolds().collapsed
478
+ : [];
417
479
  const result = model.execute(name, {
418
480
  ...this.commandOptions(),
419
481
  onLinesMoved: (moves) => this.view.refoldMoved(folded, moves)
@@ -424,9 +486,19 @@ export class InputController {
424
486
  if (name === 'smartEnd' || name === 'selectSmartEnd') {
425
487
  this.view.markRowEnd(model.getPrimary().head);
426
488
  }
489
+ if (name === 'hippieCompletion' || name === 'hippieBackwardCompletion') {
490
+ this.view.setHippieSource(model.hippieSource);
491
+ }
427
492
  this.view.revealCaret();
493
+ if (CODE_BLOCK_COMMANDS.has(name) && model.getPrimary().head !== before) {
494
+ emit(this.navigateListeners, before);
495
+ }
428
496
  return result;
429
497
  }
498
+ /* Told the offset the primary caret left when a command that navigates moved it. */
499
+ onNavigate(listener) {
500
+ return subscribe(this.navigateListeners, listener);
501
+ }
430
502
  typeText(text) {
431
503
  if (this.blockedByReadOnly()) {
432
504
  return;
@@ -651,6 +723,7 @@ export class InputController {
651
723
  if (event.isComposing || this.native.composing) {
652
724
  return;
653
725
  }
726
+ this.view.setHippieSource(null);
654
727
  const added = this.gesture.keydown(event);
655
728
  if (added !== null) {
656
729
  event.preventDefault();
@@ -726,6 +799,7 @@ export class InputController {
726
799
  : selections.map((selection) => model.slice(Math.min(selection.anchor, selection.head), Math.max(selection.anchor, selection.head))).join('\n');
727
800
  event.clipboardData?.setData('text/plain', text);
728
801
  lineClip = empty ? text : null;
802
+ emit(this.copyListeners, { text, wholeLines: empty, cut });
729
803
  if (!cut || this.blockedByReadOnly()) {
730
804
  return;
731
805
  }
package/dist/editor.css CHANGED
@@ -652,11 +652,16 @@
652
652
  .se-find-match,
653
653
  .se-find-current,
654
654
  .se-occurrence,
655
+ .se-hippie-source,
655
656
  .se-bracket-match,
656
657
  .se-bracket-unmatched {
657
658
  position: absolute;
658
659
  border-radius: var(--radius-xs);
659
660
  }
661
+ .se-hippie-source {
662
+ background: color-mix(in srgb, var(--accent) 22%, transparent);
663
+ border-radius: var(--radius-sm);
664
+ }
660
665
  .se-find-match {
661
666
  background: var(--find-match);
662
667
  }
@@ -747,6 +752,14 @@
747
752
  white-space: pre;
748
753
  cursor: default;
749
754
  }
755
+ .se-swatch {
756
+ height: 12px;
757
+ margin-top: calc((var(--code-line-height, 20px) - 12px) / 2);
758
+ padding: 0;
759
+ border-radius: var(--radius-sm);
760
+ box-shadow: inset 0 0 0 1px var(--border);
761
+ cursor: pointer;
762
+ }
750
763
  .se-inlay-link {
751
764
  text-decoration: underline;
752
765
  text-underline-offset: 2px;
package/dist/engine.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { changedSpans, DocumentModel, replacementText } from '@adecore/editor-core';
2
2
  import { MAX_REPLACEMENT_CHARACTERS } from './search-protocol.js';
3
+ import { placeAt, postfixOperands, surroundShape, unwrapTargets } from './text-queries.js';
3
4
  import { GrammarPreparation } from './grammar-preparation.js';
4
5
  import { InputController } from './controller.js';
5
6
  import { resolveSmartKeys } from './smart-keys.js';
@@ -35,6 +36,8 @@ class SmartEditor {
35
36
  lensRevisions = new Map();
36
37
  tokenizerRequest = 0;
37
38
  settingText = false;
39
+ /* The hints of each owner, in the order the owners first set theirs. */
40
+ inlayOwners = new Map();
38
41
  disposed = false;
39
42
  engine;
40
43
  constructor(engine, element, options) {
@@ -302,14 +305,21 @@ class SmartEditor {
302
305
  ...(marker.message === undefined ? {} : { message: marker.message })
303
306
  })));
304
307
  }
305
- setInlayHints(hints) {
306
- this.view.setInlays(hints.map((hint, index) => ({
308
+ setInlayHints(hints, owner = DEFAULT_WIDGET_OWNER) {
309
+ if (hints.length === 0) {
310
+ this.inlayOwners.delete(owner);
311
+ }
312
+ else {
313
+ this.inlayOwners.set(owner, hints);
314
+ }
315
+ this.view.setInlays([...this.inlayOwners.values()].flat().map((hint, index) => ({
307
316
  id: hint.id ?? `hint-${index}`,
308
317
  at: this.offsetAt(hint.position),
309
318
  text: hint.label,
310
319
  ...(hint.parts === undefined ? {} : { parts: hint.parts }),
311
320
  ...(hint.paddingLeft ? { gapLeft: INLAY_GAP } : {}),
312
- ...(hint.paddingRight ? { gapRight: INLAY_GAP } : {})
321
+ ...(hint.paddingRight ? { gapRight: INLAY_GAP } : {}),
322
+ ...(hint.swatch === undefined ? {} : { swatch: hint.swatch })
313
323
  })));
314
324
  }
315
325
  onInlayHover(listener) {
@@ -681,6 +691,24 @@ class SmartEditor {
681
691
  setFoldHints(hints) {
682
692
  this.view.setFoldHints(hints === null ? null : this.foldHintsByOffset(hints));
683
693
  }
694
+ setDeclarations(positions) {
695
+ this.view.setDeclarations(positions === null ? null : positions.map((position) => this.offsetAt(position)));
696
+ }
697
+ onNavigate(listener) {
698
+ return this.controller.onNavigate((from) => listener(this.positionAt(from)));
699
+ }
700
+ hasFocus() {
701
+ return this.view.focused;
702
+ }
703
+ setWordSources(source) {
704
+ this.controller.setWordSources(source);
705
+ }
706
+ onCopy(listener) {
707
+ return this.controller.onCopy(listener);
708
+ }
709
+ paste(text, options) {
710
+ return this.controller.pasteText(text, options);
711
+ }
684
712
  foldHintsByOffset(hints) {
685
713
  const lineOf = (line) => this.model.getLine(Math.min(line, this.model.getLineCount() - 1));
686
714
  const within = (line, character) => Math.min(line.end, line.start + Math.max(0, character));
@@ -757,6 +785,19 @@ class SmartEditor {
757
785
  isReadOnly() {
758
786
  return this.settings.readOnly;
759
787
  }
788
+ placeAt(position) {
789
+ return placeAt(this.model, this, position, this.settings.language ?? 'plaintext');
790
+ }
791
+ postfixOperands(dot) {
792
+ return postfixOperands(this.model, this, dot, this.settings.language ?? 'plaintext');
793
+ }
794
+ surroundShape(range) {
795
+ return surroundShape(this.model, this, range, this.settings.language ?? 'plaintext');
796
+ }
797
+ unwrapTargets(position) {
798
+ const unit = this.settings.insertSpaces ? ' '.repeat(this.settings.tabSize) : '\t';
799
+ return unwrapTargets(this.model, this, position, this.settings.language ?? 'plaintext', unit);
800
+ }
760
801
  runCommand(command) {
761
802
  return this.controller.command(command);
762
803
  }
package/dist/fake.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Editor, EditorAttributionHover, EditorAttributionMark, EditorBlock, EditorCodeBlockOptions, EditorLineAction, EditorReplacement, EditorLineHighlight, LineToken, EditorChangeMark, EditorCodeVision, EditorClick, EditorClickHandler, EditorContextMenu, EditorContentChange, EditorEngine, EditorFindQuery, EditorFindState, EditorReplaceOptions, EditorGutterAction, EditorGutterMarker, EditorHighlight, EditorHover, EditorIndentation, EditorGhostText, EditorInlayClick, EditorInlayClickHandler, EditorInlayHint, EditorInlayTarget, EditorKeyHandler, EditorInputPopup, EditorMarker, EditorMessages, EditorOptions, EditorPosition, EditorRemoteCursor, EditorRange, EditorReveal, EditorRect, EditorRunCommand, EditorSelectionRanges, EditorFoldHints, EditorFoldOutline, EditorFolds, EditorSemanticToken, EditorSmartKeys, EditorApplyEditsOptions, EditorTextChange, EditorTrackedRange, EditorTheme, EditorWhitespaceCleanup, EditorWidget } from './types.ts';
1
+ import type { Editor, EditorAttributionHover, EditorAttributionMark, EditorBlock, EditorCodeBlockOptions, EditorLineAction, EditorReplacement, EditorLineHighlight, LineToken, EditorChangeMark, EditorCodeVision, EditorClick, EditorClickHandler, EditorContextMenu, EditorContentChange, EditorEngine, EditorFindQuery, EditorFindState, EditorReplaceOptions, EditorGutterAction, EditorGutterMarker, EditorHighlight, EditorHover, EditorIndentation, EditorGhostText, EditorInlayClick, EditorInlayClickHandler, EditorInlayHint, EditorInlayTarget, EditorKeyHandler, EditorInputPopup, EditorMarker, EditorMessages, EditorOptions, EditorPosition, EditorPostfixOperand, EditorSurroundShape, EditorTextPlace, EditorUnwrapTarget, EditorRemoteCursor, EditorRange, EditorReveal, EditorRect, EditorRunCommand, EditorSelectionRanges, EditorFoldHints, EditorFoldOutline, EditorFolds, EditorSemanticToken, EditorSmartKeys, EditorApplyEditsOptions, EditorCopy, EditorPasteOptions, EditorTextChange, EditorTrackedRange, EditorTheme, EditorWhitespaceCleanup, EditorWidget } from './types.ts';
2
2
  import { type Listener } from './listeners.ts';
3
3
  export declare class FakeEditor implements Editor {
4
4
  readonly element: HTMLElement;
@@ -142,6 +142,22 @@ export declare class FakeEditor implements Editor {
142
142
  todoWords: readonly string[] | undefined;
143
143
  setTodoWords(words: readonly string[]): void;
144
144
  setFoldHints(hints: EditorFoldHints | null): void;
145
+ declarations: readonly EditorPosition[] | null;
146
+ setDeclarations(positions: readonly EditorPosition[] | null): void;
147
+ private readonly navigations;
148
+ onNavigate(listener: (from: EditorPosition) => void): () => void;
149
+ navigate(from: EditorPosition): void;
150
+ hasFocus(): boolean;
151
+ private readonly copies;
152
+ onCopy(listener: (copy: EditorCopy) => void): () => void;
153
+ copy(text: string, wholeLines?: boolean, cut?: boolean): void;
154
+ readonly pastes: Array<{
155
+ text: string;
156
+ options: EditorPasteOptions | undefined;
157
+ }>;
158
+ paste(text: string, options?: EditorPasteOptions): boolean;
159
+ wordSources: (() => readonly string[]) | null;
160
+ setWordSources(source: (() => readonly string[]) | null): void;
145
161
  setSmartKeys(keys: Partial<EditorSmartKeys>): void;
146
162
  setIndentation(indentation: EditorIndentation): void;
147
163
  markers: readonly EditorMarker[];
@@ -161,7 +177,8 @@ export declare class FakeEditor implements Editor {
161
177
  private readonly keys;
162
178
  setMarkers(markers: readonly EditorMarker[]): void;
163
179
  inlayHints: readonly EditorInlayHint[];
164
- setInlayHints(hints: readonly EditorInlayHint[]): void;
180
+ private readonly inlayOwners;
181
+ setInlayHints(hints: readonly EditorInlayHint[], owner?: string): void;
165
182
  private readonly inlayHovers;
166
183
  private readonly inlayHandlers;
167
184
  inlayLink: {
@@ -240,6 +257,10 @@ export declare class FakeEditor implements Editor {
240
257
  zoomTo(size: number | null): void;
241
258
  setReadOnly(readOnly: boolean, reason?: string): void;
242
259
  isReadOnly(): boolean;
260
+ placeAt(position: EditorPosition): EditorTextPlace;
261
+ postfixOperands(dot: EditorPosition): readonly EditorPostfixOperand[];
262
+ surroundShape(range: EditorRange): EditorSurroundShape | null;
263
+ unwrapTargets(position: EditorPosition): readonly EditorUnwrapTarget[];
243
264
  readonly commands: EditorRunCommand[];
244
265
  runCommand(command: EditorRunCommand): boolean;
245
266
  focus(): void;
package/dist/fake.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { emit, subscribe } from './listeners.js';
2
- import { changedSpan, changedSpans } from '@adecore/editor-core';
2
+ import { changedSpan, changedSpans, DocumentModel } from '@adecore/editor-core';
3
+ import { placeAt, postfixOperands, surroundShape, unwrapTargets } from './text-queries.js';
3
4
  import { mapTrackedRange } from './tracked-range.js';
4
5
  const LINE_BREAK = /\r\n|\r|\n/g;
5
6
  /* Where each line starts and where its text ends, before its line break: `\r\n`, a lone `\r` or `\n`, as the real document counts them. */
@@ -581,6 +582,59 @@ export class FakeEditor {
581
582
  setFoldHints(hints) {
582
583
  this.foldHints = hints;
583
584
  }
585
+ declarations = null;
586
+ setDeclarations(positions) {
587
+ this.declarations = positions;
588
+ }
589
+ navigations = new Set();
590
+ onNavigate(listener) {
591
+ return subscribe(this.navigations, listener);
592
+ }
593
+ /* Stands for a command that navigated: tells the listeners the caret left `from`. */
594
+ navigate(from) {
595
+ emit(this.navigations, from);
596
+ }
597
+ hasFocus() {
598
+ return this.focused;
599
+ }
600
+ copies = new Set();
601
+ onCopy(listener) {
602
+ return subscribe(this.copies, listener);
603
+ }
604
+ /* Stands for a copy or cut of a person: tells the listeners what went on the clipboard. */
605
+ copy(text, wholeLines = false, cut = false) {
606
+ emit(this.copies, { text, wholeLines, cut });
607
+ }
608
+ /* Every paste a host asked for, with its options. */
609
+ pastes = [];
610
+ /* Puts the text at every caret, one line per caret when the lines are as many as the carets, and leaves the carets behind it. */
611
+ paste(text, options) {
612
+ this.assertLive();
613
+ this.pastes.push({ text, options });
614
+ if (this.readOnly || text === '') {
615
+ return false;
616
+ }
617
+ const lines = text.split('\n');
618
+ const selections = [...this.selections].sort((left, right) => Math.min(left.anchor, left.head) - Math.min(right.anchor, right.head));
619
+ const perCaret = lines.length === selections.length;
620
+ const edits = selections.map((selection, index) => ({
621
+ from: Math.min(selection.anchor, selection.head),
622
+ to: Math.max(selection.anchor, selection.head),
623
+ text: perCaret ? lines[index] : text
624
+ }));
625
+ return this.edit(edits, 'command', false, () => {
626
+ let shift = 0;
627
+ return edits.map((edit) => {
628
+ const caret = edit.from + shift + edit.text.length;
629
+ shift += edit.text.length - (edit.to - edit.from);
630
+ return { anchor: caret, head: caret };
631
+ });
632
+ });
633
+ }
634
+ wordSources = null;
635
+ setWordSources(source) {
636
+ this.wordSources = source;
637
+ }
584
638
  setSmartKeys(keys) {
585
639
  this.smartKeys = keys;
586
640
  }
@@ -601,10 +655,17 @@ export class FakeEditor {
601
655
  setMarkers(markers) {
602
656
  this.markers = markers;
603
657
  }
604
- /* What the host drew as hints last. */
658
+ /* What the host drew as hints last, every owner's together. */
605
659
  inlayHints = [];
606
- setInlayHints(hints) {
607
- this.inlayHints = hints;
660
+ inlayOwners = new Map();
661
+ setInlayHints(hints, owner = 'default') {
662
+ if (hints.length === 0) {
663
+ this.inlayOwners.delete(owner);
664
+ }
665
+ else {
666
+ this.inlayOwners.set(owner, hints);
667
+ }
668
+ this.inlayHints = [...this.inlayOwners.values()].flat();
608
669
  }
609
670
  inlayHovers = new Set();
610
671
  inlayHandlers = new Set();
@@ -858,6 +919,19 @@ export class FakeEditor {
858
919
  isReadOnly() {
859
920
  return this.readOnly;
860
921
  }
922
+ placeAt(position) {
923
+ return placeAt(new DocumentModel(this.text), this, position, this.language ?? 'plaintext');
924
+ }
925
+ postfixOperands(dot) {
926
+ return postfixOperands(new DocumentModel(this.text), this, dot, this.language ?? 'plaintext');
927
+ }
928
+ surroundShape(range) {
929
+ return surroundShape(new DocumentModel(this.text), this, range, this.language ?? 'plaintext');
930
+ }
931
+ unwrapTargets(position) {
932
+ const unit = this.indentation.insertSpaces ? ' '.repeat(this.indentation.tabSize) : '\t';
933
+ return unwrapTargets(new DocumentModel(this.text), this, position, this.language ?? 'plaintext', unit);
934
+ }
861
935
  /* The commands run, in order; of its own the fake carries out only undo and redo. */
862
936
  commands = [];
863
937
  runCommand(command) {
@@ -872,6 +946,8 @@ export class FakeEditor {
872
946
  this.changes.clear();
873
947
  this.textChanges.clear();
874
948
  this.carets.clear();
949
+ this.navigations.clear();
950
+ this.copies.clear();
875
951
  this.hovers.clear();
876
952
  this.inlayHovers.clear();
877
953
  this.inlayHandlers.clear();
package/dist/index.d.ts CHANGED
@@ -3,7 +3,9 @@ export { AGENT_COLORS } from './attribution.ts';
3
3
  export { findDocComments, MAX_DOC_COMMENTS, type DocComment } from '@adecore/editor-core';
4
4
  export { DEFAULT_TODO_WORDS, findTodos, type TodoMatch, type TodoOptions } from '@adecore/editor-core';
5
5
  export { detectIndentation, type DetectedIndentation } from '@adecore/editor-core';
6
+ export { commentSyntax, type CommentSyntax } from '@adecore/editor-core';
6
7
  export { createSmartEditorEngine } from './engine.ts';
8
+ export { rememberCopy } from './controller.ts';
7
9
  export { DEFAULT_SMART_KEYS } from './smart-keys.ts';
8
10
  export { shikiScopeColors, shikiTokenizers } from './shiki.ts';
9
- export type { Editor, EditorAttributionHover, EditorAttributionMark, EditorMarkColor, EditorBlock, EditorChangeKind, EditorChangeMark, EditorCodeVision, EditorCodeVisionEntry, EditorClick, EditorCodeBlockOptions, EditorClickHandler, EditorCommand, EditorContextMenu, EditorEngine, EditorFindQuery, EditorFoldHints, EditorFoldRangeHint, EditorFoldOutline, EditorFoldRange, EditorFoldSymbol, EditorFolds, EditorFindState, EditorReplaceOptions, EditorGutterAction, EditorGutterIcon, EditorGutterMarker, EditorReplacement, EditorIndentation, EditorContentChange, EditorHighlight, EditorHighlightKind, EditorHover, EditorGhostText, EditorInlayClick, EditorInlayClickHandler, EditorInlayHint, EditorInlayTarget, EditorKeyHandler, EditorLineAction, EditorLineHighlight, EditorMarker, EditorInputPopup, EditorMessages, EditorMarkerSeverity, EditorReveal, EditorRect, EditorSmartKeys, EditorOptions, EditorPosition, EditorRemoteCursor, EditorRange, EditorApplyEditsOptions, EditorTextChange, EditorTrackedRange, EditorRunCommand, EditorSelectionRanges, EditorTheme, EditorViewCommand, EditorWhitespaceCleanup, EditorWidget, EditorZoom, FoldRole, KeyChord, LineToken, LineTokenKind, ScopeColorSource, ScopeColors, ScopeStyle, EditorSemanticToken, LineTokenizer, SmartEditorEngineOptions, TokenizedLine, TokenizerSource } from './types.ts';
11
+ export type { Editor, EditorAttributionHover, EditorAttributionMark, EditorMarkColor, EditorBlock, EditorChangeKind, EditorChangeMark, EditorCodeVision, EditorCodeVisionEntry, EditorClick, EditorCodeBlockOptions, EditorClickHandler, EditorCommand, EditorContextMenu, EditorEngine, EditorFindQuery, EditorFoldHints, EditorFoldRangeHint, EditorFoldOutline, EditorFoldRange, EditorFoldSymbol, EditorFolds, EditorFindState, EditorReplaceOptions, EditorGutterAction, EditorGutterIcon, EditorGutterMarker, EditorReplacement, EditorIndentation, EditorContentChange, EditorHighlight, EditorHighlightKind, EditorHover, EditorGhostText, EditorInlayClick, EditorInlayClickHandler, EditorInlayHint, EditorInlayTarget, EditorKeyHandler, EditorLineAction, EditorLineHighlight, EditorMarker, EditorInputPopup, EditorMessages, EditorMarkerSeverity, EditorReveal, EditorRect, EditorSmartKeys, EditorOptions, EditorPosition, EditorRemoteCursor, EditorRange, EditorApplyEditsOptions, EditorCopy, EditorPasteOptions, EditorPostfixOperand, EditorSurroundShape, EditorTextPlace, EditorUnwrapTarget, EditorTextChange, EditorTrackedRange, EditorRunCommand, EditorSelectionRanges, EditorTheme, EditorViewCommand, EditorWhitespaceCleanup, EditorWidget, EditorZoom, FoldRole, KeyChord, LineToken, LineTokenKind, ScopeColorSource, ScopeColors, ScopeStyle, EditorSemanticToken, LineTokenizer, SmartEditorEngineOptions, TokenizedLine, TokenizerSource } from './types.ts';
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ export { AGENT_COLORS } from './attribution.js';
3
3
  export { findDocComments, MAX_DOC_COMMENTS } from '@adecore/editor-core';
4
4
  export { DEFAULT_TODO_WORDS, findTodos } from '@adecore/editor-core';
5
5
  export { detectIndentation } from '@adecore/editor-core';
6
+ export { commentSyntax } from '@adecore/editor-core';
6
7
  export { createSmartEditorEngine } from './engine.js';
8
+ export { rememberCopy } from './controller.js';
7
9
  export { DEFAULT_SMART_KEYS } from './smart-keys.js';
8
10
  export { shikiScopeColors, shikiTokenizers } from './shiki.js';
@@ -18,10 +18,15 @@ export declare const KEYMAP: {
18
18
  readonly deleteLine: KeyBinding;
19
19
  readonly moveLineUp: KeyBinding;
20
20
  readonly moveLineDown: KeyBinding;
21
+ readonly moveStatementUp: KeyBinding;
22
+ readonly moveStatementDown: KeyBinding;
21
23
  readonly toggleLineComment: KeyBinding;
22
24
  readonly toggleBlockComment: KeyBinding;
23
25
  readonly joinLines: KeyBinding;
24
26
  readonly startNewLine: KeyBinding;
27
+ readonly completeCurrentStatement: KeyBinding;
28
+ readonly hippieCompletion: KeyBinding;
29
+ readonly hippieBackwardCompletion: KeyBinding;
25
30
  readonly startNewLineBefore: KeyBinding;
26
31
  readonly splitLine: KeyBinding;
27
32
  readonly toggleCase: KeyBinding;
@@ -34,6 +39,18 @@ export declare const KEYMAP: {
34
39
  readonly expandSelection: KeyBinding;
35
40
  readonly shrinkSelection: KeyBinding;
36
41
  readonly matchBrace: KeyBinding;
42
+ readonly methodUp: KeyBinding;
43
+ readonly methodDown: KeyBinding;
44
+ readonly lastEditLocation: KeyBinding;
45
+ readonly nextEditLocation: KeyBinding;
46
+ readonly pasteFromHistory: KeyBinding;
47
+ readonly pasteAsPlainText: KeyBinding;
48
+ readonly nextChange: KeyBinding;
49
+ readonly previousChange: KeyBinding;
50
+ readonly codeBlockStart: KeyBinding;
51
+ readonly codeBlockEnd: KeyBinding;
52
+ readonly codeBlockStartWithSelection: KeyBinding;
53
+ readonly codeBlockEndWithSelection: KeyBinding;
37
54
  readonly selectNextOccurrence: KeyBinding;
38
55
  readonly unselectOccurrence: KeyBinding;
39
56
  readonly selectAllOccurrences: KeyBinding;
@@ -80,6 +97,12 @@ export declare const KEYMAP: {
80
97
  readonly renameSymbol: KeyBinding;
81
98
  readonly organizeImports: KeyBinding;
82
99
  readonly formatDocument: KeyBinding;
100
+ readonly insertLiveTemplate: KeyBinding;
101
+ readonly surroundWith: KeyBinding;
102
+ readonly surroundWithLiveTemplate: KeyBinding;
103
+ readonly unwrap: KeyBinding;
104
+ readonly saveAsLiveTemplate: KeyBinding;
105
+ readonly wrapWithAbbreviation: KeyBinding;
83
106
  readonly selectionToChat: KeyBinding;
84
107
  readonly inlineEdit: KeyBinding;
85
108
  readonly suggestInline: KeyBinding;
@@ -91,6 +114,7 @@ export declare const KEYMAP: {
91
114
  readonly peekDefinition: KeyBinding;
92
115
  readonly peekReferences: KeyBinding;
93
116
  readonly callHierarchy: KeyBinding;
117
+ readonly typeHierarchy: KeyBinding;
94
118
  readonly toggleRenderedDocs: KeyBinding;
95
119
  readonly historyBack: KeyBinding;
96
120
  readonly historyForward: KeyBinding;
@@ -104,7 +128,7 @@ export declare const KEYMAP: {
104
128
  readonly replace: KeyBinding;
105
129
  };
106
130
  export type KeymapId = keyof typeof KEYMAP;
107
- export type OptionalKeymapId = 'toggleRenderedDocs' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize' | 'sortLines' | 'reverseLines' | 'uniqueLines' | 'convertIndentsToSpaces' | 'convertIndentsToTabs';
131
+ export type OptionalKeymapId = 'toggleRenderedDocs' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize' | 'sortLines' | 'reverseLines' | 'uniqueLines' | 'convertIndentsToSpaces' | 'convertIndentsToTabs' | 'completeCurrentStatement' | 'hippieCompletion' | 'hippieBackwardCompletion' | 'moveStatementUp' | 'moveStatementDown' | 'lastEditLocation' | 'nextEditLocation' | 'methodUp' | 'methodDown' | 'pasteFromHistory' | 'pasteAsPlainText' | 'nextChange' | 'previousChange' | 'codeBlockStart' | 'codeBlockEnd' | 'codeBlockStartWithSelection' | 'codeBlockEndWithSelection' | 'wrapWithAbbreviation' | 'insertLiveTemplate' | 'surroundWith' | 'surroundWithLiveTemplate' | 'unwrap' | 'saveAsLiveTemplate' | 'typeHierarchy';
108
132
  export type Keymap = Readonly<Record<Exclude<KeymapId, OptionalKeymapId>, KeyBinding> & Partial<Record<OptionalKeymapId, KeyBinding>>>;
109
133
  export type KeymapOverrides = Partial<Record<KeymapId, Partial<KeyBinding>>>;
110
134
  export declare const KEYMAP_IDS: KeymapId[];