@adecore/editor 0.41.1 → 0.43.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/fake.js CHANGED
@@ -148,6 +148,8 @@ export class FakeEditor {
148
148
  this.readOnly = options.readOnly ?? false;
149
149
  this.readOnlyReason = options.readOnlyReason;
150
150
  this.label = options.label ?? 'Code editor';
151
+ this.todoWords = options.todoWords;
152
+ this.fontSize = options.fontSize ?? null;
151
153
  this.revealedLine = options.line ?? null;
152
154
  this.column = options.column ?? null;
153
155
  this.scrollTop = options.scrollTop ?? null;
@@ -572,9 +574,66 @@ export class FakeEditor {
572
574
  setLabel(label) {
573
575
  this.label = label;
574
576
  }
577
+ todoWords;
578
+ setTodoWords(words) {
579
+ this.todoWords = [...words];
580
+ }
575
581
  setFoldHints(hints) {
576
582
  this.foldHints = hints;
577
583
  }
584
+ declarations = null;
585
+ setDeclarations(positions) {
586
+ this.declarations = positions;
587
+ }
588
+ navigations = new Set();
589
+ onNavigate(listener) {
590
+ return subscribe(this.navigations, listener);
591
+ }
592
+ /* Stands for a command that navigated: tells the listeners the caret left `from`. */
593
+ navigate(from) {
594
+ emit(this.navigations, from);
595
+ }
596
+ hasFocus() {
597
+ return this.focused;
598
+ }
599
+ copies = new Set();
600
+ onCopy(listener) {
601
+ return subscribe(this.copies, listener);
602
+ }
603
+ /* Stands for a copy or cut of a person: tells the listeners what went on the clipboard. */
604
+ copy(text, wholeLines = false, cut = false) {
605
+ emit(this.copies, { text, wholeLines, cut });
606
+ }
607
+ /* Every paste a host asked for, with its options. */
608
+ pastes = [];
609
+ /* 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. */
610
+ paste(text, options) {
611
+ this.assertLive();
612
+ this.pastes.push({ text, options });
613
+ if (this.readOnly || text === '') {
614
+ return false;
615
+ }
616
+ const lines = text.split('\n');
617
+ const selections = [...this.selections].sort((left, right) => Math.min(left.anchor, left.head) - Math.min(right.anchor, right.head));
618
+ const perCaret = lines.length === selections.length;
619
+ const edits = selections.map((selection, index) => ({
620
+ from: Math.min(selection.anchor, selection.head),
621
+ to: Math.max(selection.anchor, selection.head),
622
+ text: perCaret ? lines[index] : text
623
+ }));
624
+ return this.edit(edits, 'command', false, () => {
625
+ let shift = 0;
626
+ return edits.map((edit) => {
627
+ const caret = edit.from + shift + edit.text.length;
628
+ shift += edit.text.length - (edit.to - edit.from);
629
+ return { anchor: caret, head: caret };
630
+ });
631
+ });
632
+ }
633
+ wordSources = null;
634
+ setWordSources(source) {
635
+ this.wordSources = source;
636
+ }
578
637
  setSmartKeys(keys) {
579
638
  this.smartKeys = keys;
580
639
  }
@@ -595,10 +654,17 @@ export class FakeEditor {
595
654
  setMarkers(markers) {
596
655
  this.markers = markers;
597
656
  }
598
- /* What the host drew as hints last. */
657
+ /* What the host drew as hints last, every owner's together. */
599
658
  inlayHints = [];
600
- setInlayHints(hints) {
601
- this.inlayHints = hints;
659
+ inlayOwners = new Map();
660
+ setInlayHints(hints, owner = 'default') {
661
+ if (hints.length === 0) {
662
+ this.inlayOwners.delete(owner);
663
+ }
664
+ else {
665
+ this.inlayOwners.set(owner, hints);
666
+ }
667
+ this.inlayHints = [...this.inlayOwners.values()].flat();
602
668
  }
603
669
  inlayHovers = new Set();
604
670
  inlayHandlers = new Set();
@@ -810,12 +876,41 @@ export class FakeEditor {
810
876
  }));
811
877
  return this.edit(ordered, 'command', options.mergeUndo === true, place);
812
878
  }
879
+ /* The options of every cleanup asked for, in order; the fake changes no text for them. */
880
+ cleanups = [];
881
+ /* How often the text was marked as saved, by a cleanup or on its own. */
882
+ savedMarks = 0;
883
+ cleanUpWhitespace(options) {
884
+ this.cleanups.push({ ...options });
885
+ this.markSaved();
886
+ return false;
887
+ }
888
+ markSaved() {
889
+ this.savedMarks += 1;
890
+ }
813
891
  setTheme(theme) {
814
892
  this.theme = theme;
815
893
  }
816
894
  refreshFont() {
817
895
  this.fontRefreshes += 1;
818
896
  }
897
+ /* The size `setFontSize` set, null for the page's. */
898
+ fontSize = null;
899
+ fontSizeListeners = new Set();
900
+ setFontSize(size) {
901
+ this.fontSize = size;
902
+ }
903
+ getFontSize() {
904
+ return this.fontSize ?? 13;
905
+ }
906
+ onFontSizeChange(listener) {
907
+ return subscribe(this.fontSizeListeners, listener);
908
+ }
909
+ /* What a person zooming would do: the new size goes to the listeners. */
910
+ zoomTo(size) {
911
+ this.fontSize = size;
912
+ emit(this.fontSizeListeners, size);
913
+ }
819
914
  setReadOnly(readOnly, reason) {
820
915
  this.readOnly = readOnly;
821
916
  this.readOnlyReason = reason;
@@ -837,6 +932,8 @@ export class FakeEditor {
837
932
  this.changes.clear();
838
933
  this.textChanges.clear();
839
934
  this.carets.clear();
935
+ this.navigations.clear();
936
+ this.copies.clear();
840
937
  this.hovers.clear();
841
938
  this.inlayHovers.clear();
842
939
  this.inlayHandlers.clear();
package/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  export { MAX_REPLACEMENT_PAGE, MAX_REPLACEMENT_CHARACTERS } from './search-protocol.ts';
2
2
  export { AGENT_COLORS } from './attribution.ts';
3
3
  export { findDocComments, MAX_DOC_COMMENTS, type DocComment } from '@adecore/editor-core';
4
+ export { DEFAULT_TODO_WORDS, findTodos, type TodoMatch, type TodoOptions } from '@adecore/editor-core';
5
+ export { detectIndentation, type DetectedIndentation } from '@adecore/editor-core';
4
6
  export { createSmartEditorEngine } from './engine.ts';
7
+ export { rememberCopy } from './controller.ts';
5
8
  export { DEFAULT_SMART_KEYS } from './smart-keys.ts';
6
9
  export { shikiScopeColors, shikiTokenizers } from './shiki.ts';
7
- 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, EditorWidget, FoldRole, KeyChord, LineToken, LineTokenKind, ScopeColorSource, ScopeColors, ScopeStyle, EditorSemanticToken, LineTokenizer, SmartEditorEngineOptions, TokenizedLine, TokenizerSource } from './types.ts';
10
+ 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, 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
@@ -1,6 +1,9 @@
1
1
  export { MAX_REPLACEMENT_PAGE, MAX_REPLACEMENT_CHARACTERS } from './search-protocol.js';
2
2
  export { AGENT_COLORS } from './attribution.js';
3
3
  export { findDocComments, MAX_DOC_COMMENTS } from '@adecore/editor-core';
4
+ export { DEFAULT_TODO_WORDS, findTodos } from '@adecore/editor-core';
5
+ export { detectIndentation } from '@adecore/editor-core';
4
6
  export { createSmartEditorEngine } from './engine.js';
7
+ export { rememberCopy } from './controller.js';
5
8
  export { DEFAULT_SMART_KEYS } from './smart-keys.js';
6
9
  export { shikiScopeColors, shikiTokenizers } from './shiki.js';
@@ -18,17 +18,39 @@ 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;
28
33
  readonly autoIndentLines: KeyBinding;
34
+ readonly sortLines: KeyBinding;
35
+ readonly reverseLines: KeyBinding;
36
+ readonly uniqueLines: KeyBinding;
37
+ readonly convertIndentsToSpaces: KeyBinding;
38
+ readonly convertIndentsToTabs: KeyBinding;
29
39
  readonly expandSelection: KeyBinding;
30
40
  readonly shrinkSelection: KeyBinding;
31
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;
32
54
  readonly selectNextOccurrence: KeyBinding;
33
55
  readonly unselectOccurrence: KeyBinding;
34
56
  readonly selectAllOccurrences: KeyBinding;
@@ -64,6 +86,9 @@ export declare const KEYMAP: {
64
86
  readonly expandAllToLevel3: KeyBinding;
65
87
  readonly expandAllToLevel4: KeyBinding;
66
88
  readonly expandAllToLevel5: KeyBinding;
89
+ readonly increaseFontSize: KeyBinding;
90
+ readonly decreaseFontSize: KeyBinding;
91
+ readonly resetFontSize: KeyBinding;
67
92
  readonly triggerCompletion: KeyBinding;
68
93
  readonly parameterInfo: KeyBinding;
69
94
  readonly quickInfo: KeyBinding;
@@ -72,6 +97,7 @@ export declare const KEYMAP: {
72
97
  readonly renameSymbol: KeyBinding;
73
98
  readonly organizeImports: KeyBinding;
74
99
  readonly formatDocument: KeyBinding;
100
+ readonly wrapWithAbbreviation: KeyBinding;
75
101
  readonly selectionToChat: KeyBinding;
76
102
  readonly inlineEdit: KeyBinding;
77
103
  readonly suggestInline: KeyBinding;
@@ -83,6 +109,7 @@ export declare const KEYMAP: {
83
109
  readonly peekDefinition: KeyBinding;
84
110
  readonly peekReferences: KeyBinding;
85
111
  readonly callHierarchy: KeyBinding;
112
+ readonly typeHierarchy: KeyBinding;
86
113
  readonly toggleRenderedDocs: KeyBinding;
87
114
  readonly historyBack: KeyBinding;
88
115
  readonly historyForward: KeyBinding;
@@ -96,7 +123,7 @@ export declare const KEYMAP: {
96
123
  readonly replace: KeyBinding;
97
124
  };
98
125
  export type KeymapId = keyof typeof KEYMAP;
99
- export type OptionalKeymapId = 'toggleRenderedDocs';
126
+ 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' | 'typeHierarchy';
100
127
  export type Keymap = Readonly<Record<Exclude<KeymapId, OptionalKeymapId>, KeyBinding> & Partial<Record<OptionalKeymapId, KeyBinding>>>;
101
128
  export type KeymapOverrides = Partial<Record<KeymapId, Partial<KeyBinding>>>;
102
129
  export declare const KEYMAP_IDS: KeymapId[];
@@ -15,17 +15,41 @@ export const KEYMAP = {
15
15
  deleteLine: bind('Mod+Backspace', 'Mod+Y'),
16
16
  moveLineUp: bind('Alt+Shift+ArrowUp'),
17
17
  moveLineDown: bind('Alt+Shift+ArrowDown'),
18
+ moveStatementUp: bind('Mod+Shift+ArrowUp'),
19
+ moveStatementDown: bind('Mod+Shift+ArrowDown'),
18
20
  toggleLineComment: bind('Mod+/'),
19
21
  toggleBlockComment: bind('Mod+Alt+/', 'Mod+Shift+/'),
20
22
  joinLines: bind('Ctrl+Shift+J'),
21
23
  startNewLine: bind('Shift+Enter'),
24
+ completeCurrentStatement: bind('Mod+Shift+Enter'),
25
+ hippieCompletion: bind('Alt+/'),
26
+ hippieBackwardCompletion: bind('Alt+Shift+/'),
22
27
  startNewLineBefore: bind('Mod+Alt+Enter'),
23
28
  splitLine: bind('Mod+Enter'),
24
29
  toggleCase: bind('Mod+Shift+U'),
25
30
  autoIndentLines: bind('Ctrl+Alt+I'),
31
+ // The platform binds no chord to these, so they live in the menu and the palette.
32
+ sortLines: bind(null),
33
+ reverseLines: bind(null),
34
+ uniqueLines: bind(null),
35
+ convertIndentsToSpaces: bind(null),
36
+ convertIndentsToTabs: bind(null),
26
37
  expandSelection: bind('Alt+ArrowUp', 'Ctrl+W'),
27
- shrinkSelection: bind('Alt+ArrowDown'),
38
+ // Off macOS Alt+ArrowDown is Next Method, as in IntelliJ, so Shrink Selection takes its own chord there.
39
+ shrinkSelection: bind('Alt+ArrowDown', 'Ctrl+Shift+W'),
28
40
  matchBrace: bind('Ctrl+M', 'Ctrl+Shift+M'),
41
+ methodUp: bind('Ctrl+Shift+ArrowUp', 'Alt+ArrowUp'),
42
+ methodDown: bind('Ctrl+Shift+ArrowDown', 'Alt+ArrowDown'),
43
+ lastEditLocation: bind('Mod+Shift+Backspace'),
44
+ nextEditLocation: bind(null),
45
+ pasteFromHistory: bind('Mod+Shift+V'),
46
+ pasteAsPlainText: bind('Mod+Alt+Shift+V'),
47
+ nextChange: bind('Ctrl+Alt+Shift+ArrowDown'),
48
+ previousChange: bind('Ctrl+Alt+Shift+ArrowUp'),
49
+ codeBlockStart: bind('Mod+Alt+[', 'Ctrl+['),
50
+ codeBlockEnd: bind('Mod+Alt+]', 'Ctrl+]'),
51
+ codeBlockStartWithSelection: bind('Mod+Alt+Shift+[', 'Ctrl+Shift+['),
52
+ codeBlockEndWithSelection: bind('Mod+Alt+Shift+]', 'Ctrl+Shift+]'),
29
53
  selectNextOccurrence: bind('Ctrl+G', 'Alt+J'),
30
54
  unselectOccurrence: bind('Ctrl+Shift+G', 'Alt+Shift+J'),
31
55
  selectAllOccurrences: bind('Meta+Ctrl+G', 'Ctrl+Alt+Shift+J'),
@@ -45,9 +69,10 @@ export const KEYMAP = {
45
69
  smartEnd: bind('Meta+ArrowRight', null),
46
70
  selectSmartEnd: bind('Meta+Shift+ArrowRight', null),
47
71
  textStart: bind('Meta+ArrowUp', null),
48
- selectTextStart: bind('Meta+Shift+ArrowUp', null),
72
+ // IntelliJ binds Cmd+Shift+ArrowUp and ArrowDown to Move Statement on macOS, where Cmd+Shift+Home and End still select to the text edges.
73
+ selectTextStart: bind(null),
49
74
  textEnd: bind('Meta+ArrowDown', null),
50
- selectTextEnd: bind('Meta+Shift+ArrowDown', null),
75
+ selectTextEnd: bind(null),
51
76
  collapse: bind('Mod+-'),
52
77
  expand: bind('Mod+='),
53
78
  collapseAll: bind('Mod+Shift+-'),
@@ -63,6 +88,10 @@ export const KEYMAP = {
63
88
  expandAllToLevel3: bind(null),
64
89
  expandAllToLevel4: bind(null),
65
90
  expandAllToLevel5: bind(null),
91
+ // The platform binds no chord to these, and the zoom chords of other editors are the fold keys here.
92
+ increaseFontSize: bind(null),
93
+ decreaseFontSize: bind(null),
94
+ resetFontSize: bind(null),
66
95
  triggerCompletion: bind('Ctrl+Space'),
67
96
  parameterInfo: bind('Mod+P'),
68
97
  quickInfo: bind('Ctrl+J', 'Ctrl+Q'),
@@ -71,6 +100,7 @@ export const KEYMAP = {
71
100
  renameSymbol: bind('Shift+F6'),
72
101
  organizeImports: bind('Ctrl+Alt+O'),
73
102
  formatDocument: bind('Mod+Alt+L'),
103
+ wrapWithAbbreviation: bind('Mod+Alt+J'),
74
104
  selectionToChat: bind(null),
75
105
  inlineEdit: bind(null),
76
106
  suggestInline: bind(null),
@@ -82,6 +112,7 @@ export const KEYMAP = {
82
112
  peekDefinition: bind('Alt+Space', 'Ctrl+Shift+I'),
83
113
  peekReferences: bind('Alt+F7'),
84
114
  callHierarchy: bind('Ctrl+Alt+H'),
115
+ typeHierarchy: bind('Ctrl+H'),
85
116
  toggleRenderedDocs: bind('Ctrl+Alt+Q'),
86
117
  historyBack: bind('Mod+[', 'Ctrl+Alt+ArrowLeft'),
87
118
  historyForward: bind('Mod+]', 'Ctrl+Alt+ArrowRight'),
package/dist/keymap.js CHANGED
@@ -16,17 +16,36 @@ const ACTIONS = {
16
16
  deleteLine: command('deleteLine'),
17
17
  moveLineUp: command('moveLineUp'),
18
18
  moveLineDown: command('moveLineDown'),
19
+ moveStatementUp: command('moveStatementUp'),
20
+ moveStatementDown: command('moveStatementDown'),
19
21
  toggleLineComment: command('toggleLineComment'),
20
22
  toggleBlockComment: command('toggleBlockComment'),
21
23
  joinLines: command('joinLines'),
22
24
  startNewLine: command('startNewLine'),
25
+ completeCurrentStatement: command('completeCurrentStatement'),
26
+ hippieCompletion: command('hippieCompletion'),
27
+ hippieBackwardCompletion: command('hippieBackwardCompletion'),
23
28
  startNewLineBefore: command('startNewLineBefore'),
24
29
  splitLine: command('splitLine'),
25
30
  toggleCase: command('toggleCase'),
26
31
  autoIndentLines: command('autoIndentLines'),
32
+ sortLines: command('sortLines'),
33
+ reverseLines: command('reverseLines'),
34
+ uniqueLines: command('uniqueLines'),
35
+ convertIndentsToSpaces: command('convertIndentsToSpaces'),
36
+ convertIndentsToTabs: command('convertIndentsToTabs'),
27
37
  expandSelection: command('expandSelection'),
28
38
  shrinkSelection: command('shrinkSelection'),
29
39
  matchBrace: { type: 'bracket' },
40
+ methodUp: view('methodUp'),
41
+ methodDown: view('methodDown'),
42
+ pasteAsPlainText: view('pasteAsPlainText'),
43
+ nextChange: view('nextChange'),
44
+ previousChange: view('previousChange'),
45
+ codeBlockStart: command('codeBlockStart'),
46
+ codeBlockEnd: command('codeBlockEnd'),
47
+ codeBlockStartWithSelection: command('codeBlockStartWithSelection'),
48
+ codeBlockEndWithSelection: command('codeBlockEndWithSelection'),
30
49
  selectNextOccurrence: command('selectNextOccurrence'),
31
50
  unselectOccurrence: command('unselectOccurrence'),
32
51
  selectAllOccurrences: command('selectAllOccurrences'),
@@ -59,7 +78,10 @@ const ACTIONS = {
59
78
  expandAllToLevel3: view('expandAllToLevel3'),
60
79
  expandAllToLevel4: view('expandAllToLevel4'),
61
80
  expandAllToLevel5: view('expandAllToLevel5'),
62
- toggleColumnMode: view('toggleColumnMode')
81
+ toggleColumnMode: view('toggleColumnMode'),
82
+ increaseFontSize: view('increaseFontSize'),
83
+ decreaseFontSize: view('decreaseFontSize'),
84
+ resetFontSize: view('resetFontSize')
63
85
  };
64
86
  const PUNCTUATION_CODES = {
65
87
  ',': 'Comma',
package/dist/layout.d.ts CHANGED
@@ -36,7 +36,9 @@ export interface Inlay {
36
36
  gapRight?: number;
37
37
  composition?: boolean;
38
38
  ghost?: boolean;
39
+ swatch?: string;
39
40
  }
41
+ export declare const SWATCH_SIZE = 12;
40
42
  export interface BlockWidget {
41
43
  id: string;
42
44
  at: number;
package/dist/layout.js CHANGED
@@ -11,6 +11,8 @@ export function subRowBand(top, subRows, lineHeight, window, block = 1) {
11
11
  last: Math.min(subRows - 1, Math.ceil((last + 1) / block) * block - 1)
12
12
  };
13
13
  }
14
+ /* The side of a swatch in pixels, without its gaps. */
15
+ export const SWATCH_SIZE = 12;
14
16
  export const TEXT_PADDING = 0;
15
17
  export const BOTTOM_PADDING = 8;
16
18
  export const RIGHT_PADDING = 16;
@@ -154,9 +156,12 @@ export function scanDenseLine(line, metrics, limit, lineInlays) {
154
156
  flushRun(offset);
155
157
  const inlay = lineInlays[nextInlay++];
156
158
  const shown = visibleBidi(inlay.text);
159
+ const gaps = (inlay.gapLeft ?? 0) + (inlay.gapRight ?? 0);
157
160
  const width = inlay.composition || inlay.ghost
158
161
  ? metrics.measureText(shown)
159
- : Math.max(8, metrics.measureInlay(shown) + 12) + (inlay.gapLeft ?? 0) + (inlay.gapRight ?? 0);
162
+ : inlay.swatch !== undefined
163
+ ? SWATCH_SIZE + gaps
164
+ : Math.max(8, metrics.measureInlay(shown) + 12) + gaps;
160
165
  geometry.inlays.push({ inlay, x, width, subRow: row });
161
166
  x += width;
162
167
  }
package/dist/outline.d.ts CHANGED
@@ -37,6 +37,7 @@ export declare class Outline {
37
37
  setStructure(entries: OutlineEntry[]): void;
38
38
  setProvided(blocks: readonly EditorBlock[] | null, document: OutlineDocument): void;
39
39
  edited(batches: readonly (readonly DocumentChange[])[]): void;
40
+ declarationStops(document: OutlineDocument): number[];
40
41
  blocks(document: OutlineDocument): readonly OutlineBlock[];
41
42
  }
42
43
  export interface StickyRows {
package/dist/outline.js CHANGED
@@ -140,6 +140,16 @@ export class Outline {
140
140
  this.provided = this.provided === null ? null : move(this.provided);
141
141
  this.version++;
142
142
  }
143
+ /* Where the named blocks of the structure declare their name, in no order: the stops of Method Up and Down. */
144
+ declarationStops(document) {
145
+ return this.structure.flatMap((entry) => {
146
+ if (entry.name === undefined) {
147
+ return [];
148
+ }
149
+ const header = document.getLine(document.positionAt(entry.from).line);
150
+ return [header.start + Math.max(0, header.text.indexOf(entry.name))];
151
+ });
152
+ }
143
153
  /* Outermost first. Of two blocks that start on one line, only the larger one. */
144
154
  blocks(document) {
145
155
  if (this.cache?.version !== this.version) {
@@ -1,4 +1,4 @@
1
- export type OverviewKind = 'added' | 'modified' | 'deleted' | 'find' | 'find-current' | 'occurrence' | 'info' | 'warning' | 'error';
1
+ export type OverviewKind = 'added' | 'modified' | 'deleted' | 'find' | 'find-current' | 'occurrence' | 'todo' | 'info' | 'warning' | 'error';
2
2
  export interface OverviewSpan {
3
3
  kind: OverviewKind;
4
4
  top: number;
package/dist/paint.js CHANGED
@@ -102,10 +102,20 @@ export function lineElement(document, geometry, tokens, lineHeight, band, whites
102
102
  continue;
103
103
  }
104
104
  const span = document.createElement('span');
105
- span.className = box.inlay.ghost ? 'se-ghost' : box.inlay.composition ? 'se-composition' : 'se-inlay';
105
+ span.className = box.inlay.ghost
106
+ ? 'se-ghost'
107
+ : box.inlay.composition
108
+ ? 'se-composition'
109
+ : box.inlay.swatch !== undefined
110
+ ? 'se-inlay se-swatch'
111
+ : 'se-inlay';
106
112
  span.dataset.inlayId = box.inlay.id;
107
113
  const { parts } = box.inlay;
108
- if (parts === undefined) {
114
+ if (box.inlay.swatch !== undefined) {
115
+ span.style.backgroundColor = box.inlay.swatch;
116
+ span.setAttribute('aria-label', box.inlay.text);
117
+ }
118
+ else if (parts === undefined) {
109
119
  span.textContent = visibleBidi(box.inlay.text);
110
120
  }
111
121
  else {
@@ -0,0 +1,23 @@
1
+ import { type DocumentModel, type TodoMatch } from '@adecore/editor-core';
2
+ import type { LineToken } from './types.ts';
3
+ export type TodoMark = TodoMatch & {
4
+ readonly text: string;
5
+ };
6
+ export declare class TodoMarks {
7
+ private readonly model;
8
+ private readonly changed;
9
+ private words;
10
+ private language;
11
+ private revision;
12
+ private readonly lines;
13
+ private timer;
14
+ private all;
15
+ version: number;
16
+ constructor(model: DocumentModel, changed: () => void);
17
+ get marks(): readonly TodoMark[];
18
+ configure(language: string | undefined, words: readonly string[] | undefined): void;
19
+ styled(line: number, base: readonly LineToken[] | null): readonly LineToken[] | null;
20
+ schedule(): void;
21
+ refresh(): void;
22
+ dispose(): void;
23
+ }
package/dist/todos.js ADDED
@@ -0,0 +1,86 @@
1
+ import { DEFAULT_TODO_WORDS } from '@adecore/editor-core';
2
+ import { overlayTokens } from './semantic.js';
3
+ const TODO_COLOR = 'var(--editor-todo)';
4
+ const ITALIC = 1;
5
+ /* The marks in the scroll track read the whole document, so they wait for a pause in editing as the folds do. */
6
+ const TODO_DELAY_MS = 300;
7
+ const TODO_LIMIT = 2_000_000;
8
+ const CACHED_LINES = 600;
9
+ /*
10
+ * The TODO comments of a view: drawn in their own color over the grammar's on the lines being painted,
11
+ * and listed for the whole document a moment after the last edit, for the ticks in the scroll track.
12
+ */
13
+ export class TodoMarks {
14
+ model;
15
+ changed;
16
+ words = DEFAULT_TODO_WORDS;
17
+ /* Empty until the first `configure`, which reads the document. */
18
+ language = '';
19
+ revision = -1;
20
+ lines = new Map();
21
+ timer;
22
+ all = [];
23
+ /* Counts every new list, so a painter can tell its ticks are stale. */
24
+ version = 0;
25
+ constructor(model, changed) {
26
+ this.model = model;
27
+ this.changed = changed;
28
+ }
29
+ get marks() {
30
+ return this.all;
31
+ }
32
+ configure(language, words) {
33
+ const nextLanguage = language ?? 'plaintext';
34
+ const nextWords = words ?? DEFAULT_TODO_WORDS;
35
+ if (nextLanguage === this.language && nextWords.join('\n') === this.words.join('\n')) {
36
+ return;
37
+ }
38
+ this.language = nextLanguage;
39
+ this.words = nextWords;
40
+ this.revision = -1;
41
+ this.refresh();
42
+ }
43
+ /* The line's tokens with its TODOs drawn over them, the same array for as long as neither changed. */
44
+ styled(line, base) {
45
+ if (this.words.length === 0) {
46
+ return base;
47
+ }
48
+ const revision = this.model.getRevision();
49
+ if (revision !== this.revision || this.lines.size > CACHED_LINES) {
50
+ this.revision = revision;
51
+ this.lines.clear();
52
+ }
53
+ const cached = this.lines.get(line);
54
+ if (cached !== undefined && cached.base === base) {
55
+ return cached.result;
56
+ }
57
+ const matches = this.model.findTodos(this.language, { words: this.words }, line, line);
58
+ let result = base;
59
+ if (matches.length > 0) {
60
+ const { start, end } = this.model.getLine(line);
61
+ const spans = matches.map((match) => ({ from: match.from - start, to: match.to - start, color: TODO_COLOR, fontStyle: ITALIC }));
62
+ result = overlayTokens(base ?? [{ length: end - start, color: '', fontStyle: 0 }], spans);
63
+ }
64
+ this.lines.set(line, { base, result });
65
+ return result;
66
+ }
67
+ schedule() {
68
+ clearTimeout(this.timer);
69
+ this.timer = setTimeout(() => this.refresh(), TODO_DELAY_MS);
70
+ }
71
+ /* Reads the TODOs of the whole document now, which otherwise waits for a pause in editing. */
72
+ refresh() {
73
+ clearTimeout(this.timer);
74
+ const found = this.words.length === 0 || this.model.getLength() > TODO_LIMIT ? [] : this.model.findTodos(this.language, { words: this.words });
75
+ const next = found.map((match) => ({ ...match, text: this.model.slice(match.from, match.to) }));
76
+ if (next.length === 0 && this.all.length === 0) {
77
+ return;
78
+ }
79
+ this.all = next;
80
+ this.version++;
81
+ this.changed();
82
+ }
83
+ dispose() {
84
+ clearTimeout(this.timer);
85
+ }
86
+ }