@adecore/editor 0.41.1 → 0.42.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.
@@ -7,6 +7,8 @@ export interface ControllerOptions {
7
7
  apple: boolean;
8
8
  save(): void;
9
9
  blur(): void;
10
+ zoom(step: -1 | 0 | 1): void;
11
+ wheelZooms(): boolean;
10
12
  }
11
13
  export declare class InputController {
12
14
  private readonly native;
@@ -25,6 +27,7 @@ export declare class InputController {
25
27
  private selectionRanges;
26
28
  private expansions;
27
29
  private lastInputAt;
30
+ private readonly wheelZoom;
28
31
  private desiredXs;
29
32
  private keepDesiredXs;
30
33
  private readonly view;
@@ -41,6 +44,7 @@ export declare class InputController {
41
44
  private paste;
42
45
  private commandOptions;
43
46
  private viewCommand;
47
+ private wheel;
44
48
  command(name: EditorRunCommand): boolean;
45
49
  setSelectionRanges(provider: EditorSelectionRanges | null): void;
46
50
  private expandThrough;
@@ -6,6 +6,7 @@ import { NativeInput } from './native-input.js';
6
6
  import { columnSelections, sameCell } from './column-selection.js';
7
7
  import { PointerSelection } from './pointer.js';
8
8
  import { TouchSelection } from './touch.js';
9
+ import { WheelZoom } from './zoom.js';
9
10
  const MODIFYING = new Set([
10
11
  'deleteWordLeft',
11
12
  'deleteWordRight',
@@ -28,7 +29,12 @@ const MODIFYING = new Set([
28
29
  'outdent',
29
30
  'insertNewline',
30
31
  'smartBackspace',
31
- 'deleteForward'
32
+ 'deleteForward',
33
+ 'sortLines',
34
+ 'reverseLines',
35
+ 'uniqueLines',
36
+ 'convertIndentsToSpaces',
37
+ 'convertIndentsToTabs'
32
38
  ]);
33
39
  const VIEW_COMMANDS = new Set([
34
40
  'collapseRegion',
@@ -45,8 +51,12 @@ const VIEW_COMMANDS = new Set([
45
51
  'expandAllToLevel3',
46
52
  'expandAllToLevel4',
47
53
  'expandAllToLevel5',
48
- 'toggleColumnMode'
54
+ 'toggleColumnMode',
55
+ 'increaseFontSize',
56
+ 'decreaseFontSize',
57
+ 'resetFontSize'
49
58
  ]);
59
+ const ZOOM_STEPS = { increaseFontSize: 1, decreaseFontSize: -1, resetFontSize: 0 };
50
60
  /* 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
61
  let lineClip = null;
52
62
  function selectionSignature(selections) {
@@ -79,6 +89,7 @@ export class InputController {
79
89
  /* The Extend Selection presses that wait for a server's ranges, which are answered one after the other. */
80
90
  expansions = Promise.resolve();
81
91
  lastInputAt = 0;
92
+ wheelZoom = new WheelZoom();
82
93
  desiredXs = [];
83
94
  keepDesiredXs = false;
84
95
  view;
@@ -180,7 +191,7 @@ export class InputController {
180
191
  this.listen(input, 'copy', (event) => this.copy(event, false));
181
192
  this.listen(input, 'cut', (event) => this.copy(event, true));
182
193
  this.listen(viewport, 'scroll', () => this.view.scrolled());
183
- this.listen(viewport, 'wheel', () => this.view.cancelScroll());
194
+ this.listen(viewport, 'wheel', (event) => this.wheel(event));
184
195
  this.listen(viewport, 'pointermove', (event) => this.view.hoverMoved(event));
185
196
  this.listen(viewport, 'pointerleave', () => {
186
197
  this.view.setHover(null);
@@ -300,6 +311,10 @@ export class InputController {
300
311
  /* Folding is the view's, since it knows where the ranges are. */
301
312
  viewCommand(name) {
302
313
  const { view } = this;
314
+ if (name in ZOOM_STEPS) {
315
+ this.options.zoom(ZOOM_STEPS[name]);
316
+ return true;
317
+ }
303
318
  if (view.settings.folding === false && name !== 'toggleColumnMode') {
304
319
  return false;
305
320
  }
@@ -323,6 +338,18 @@ export class InputController {
323
338
  run[name]();
324
339
  return true;
325
340
  }
341
+ /* A pinch comes as the wheel with Ctrl, on every platform. */
342
+ wheel(event) {
343
+ this.view.cancelScroll();
344
+ if (!(event.ctrlKey || (this.options.apple && event.metaKey)) || !this.options.wheelZooms()) {
345
+ return;
346
+ }
347
+ event.preventDefault();
348
+ const step = this.wheelZoom.step(event);
349
+ if (step !== 0) {
350
+ this.options.zoom(step);
351
+ }
352
+ }
326
353
  /* Runs a model command on every caret, with the folds that would hide its result opened first. */
327
354
  command(name) {
328
355
  if (VIEW_COMMANDS.has(name)) {
package/dist/editor.css CHANGED
@@ -17,6 +17,7 @@
17
17
  --editor-added: var(--status-idle);
18
18
  --editor-modified: var(--accent);
19
19
  --editor-deleted: var(--status-error);
20
+ --editor-todo: var(--status-running);
20
21
  --editor-mark-number-mix: 72%;
21
22
  --editor-mark-bar-width: 3px;
22
23
  --editor-mark-gap: 4px;
@@ -38,6 +39,7 @@
38
39
  --editor-margin: color-mix(in srgb, var(--text) 16%, transparent);
39
40
  --editor-whitespace: color-mix(in srgb, var(--text) 32%, transparent);
40
41
  --editor-hatch: color-mix(in srgb, var(--text) 10%, transparent);
42
+ --editor-todo: var(--status-idle);
41
43
  --editor-mark-number-mix: 82%;
42
44
  --editor-sticky-shadow: color-mix(in srgb, black 60%, transparent);
43
45
  }
@@ -478,11 +480,15 @@
478
480
  left: 6px;
479
481
  background: var(--editor-occurrence);
480
482
  }
483
+ .se-tick-todo,
481
484
  .se-tick-error,
482
485
  .se-tick-warning,
483
486
  .se-tick-info {
484
487
  left: 10px;
485
488
  }
489
+ .se-tick-todo {
490
+ background: var(--editor-todo);
491
+ }
486
492
  /* The only part of the track that takes the pointer, with a wider reach than the 4px it draws. */
487
493
  .se-tick[data-offset] {
488
494
  pointer-events: auto;
package/dist/engine.js CHANGED
@@ -6,6 +6,7 @@ import { resolveSmartKeys } from './smart-keys.js';
6
6
  import { emit, subscribe } from './listeners.js';
7
7
  import { fillLens, lensSignature } from './lens.js';
8
8
  import { EditorView } from './view.js';
9
+ import { clampFontSize, fontSizeRange } from './zoom.js';
9
10
  const DEFAULT_TAB_SIZE = 4;
10
11
  const DEFAULT_LABEL = 'Code editor';
11
12
  const DEFAULT_WIDGET_OWNER = 'default';
@@ -23,6 +24,9 @@ class SmartEditor {
23
24
  caretOffset = 0;
24
25
  saves = new Set();
25
26
  blurs = new Set();
27
+ fontSizes = new Set();
28
+ fontSizeRange;
29
+ fontSize = null;
26
30
  subscription;
27
31
  language;
28
32
  theme;
@@ -35,6 +39,7 @@ class SmartEditor {
35
39
  engine;
36
40
  constructor(engine, element, options) {
37
41
  this.engine = engine;
42
+ this.fontSizeRange = fontSizeRange(options.zoom);
38
43
  this.model = new DocumentModel(options.text);
39
44
  this.language = options.language;
40
45
  this.theme = options.theme;
@@ -54,7 +59,8 @@ class SmartEditor {
54
59
  whitespace: options.whitespace === true,
55
60
  rightMargin: options.rightMargin ?? null,
56
61
  foldOutline: options.foldOutline ?? 'hover',
57
- folding: options.folding !== false
62
+ folding: options.folding !== false,
63
+ todoWords: options.todoWords
58
64
  };
59
65
  this.view = new EditorView(element, this.model, this.settings);
60
66
  this.grammarPreparation = new GrammarPreparation(this.model, () => this.view.grammarChanged());
@@ -63,8 +69,13 @@ class SmartEditor {
63
69
  keymap: engine.keymap,
64
70
  apple: engine.apple ?? false,
65
71
  save: () => emit(this.saves),
66
- blur: () => emit(this.blurs)
72
+ blur: () => emit(this.blurs),
73
+ zoom: (step) => this.zoom(step),
74
+ wheelZooms: () => options.zoom?.wheel !== false
67
75
  });
76
+ if (options.fontSize !== undefined) {
77
+ this.setFontSize(options.fontSize);
78
+ }
68
79
  this.revision = this.model.getRevision();
69
80
  this.caretOffset = this.model.getPrimary().head;
70
81
  this.subscription = this.model.subscribe((snapshot) => {
@@ -273,6 +284,14 @@ class SmartEditor {
273
284
  return false;
274
285
  }
275
286
  }
287
+ cleanUpWhitespace(options) {
288
+ const changed = !this.settings.readOnly && this.model.cleanUpWhitespace({ ...options, language: this.settings.language });
289
+ this.model.markSaved();
290
+ return changed;
291
+ }
292
+ markSaved() {
293
+ this.model.markSaved();
294
+ }
276
295
  setMarkers(markers) {
277
296
  this.view.setMarkers(markers.map((marker) => ({
278
297
  severity: marker.severity,
@@ -655,6 +674,10 @@ class SmartEditor {
655
674
  this.settings.label = label;
656
675
  this.view.applySettings();
657
676
  }
677
+ setTodoWords(words) {
678
+ this.settings.todoWords = [...words];
679
+ this.view.applySettings();
680
+ }
658
681
  setFoldHints(hints) {
659
682
  this.view.setFoldHints(hints === null ? null : this.foldHintsByOffset(hints));
660
683
  }
@@ -700,6 +723,32 @@ class SmartEditor {
700
723
  refreshFont() {
701
724
  this.view.refreshFont();
702
725
  }
726
+ setFontSize(size) {
727
+ const next = size === null || !Number.isFinite(size) ? null : clampFontSize(size, this.fontSizeRange);
728
+ if (next !== this.fontSize) {
729
+ this.fontSize = next;
730
+ this.view.setFontSize(next);
731
+ }
732
+ }
733
+ getFontSize() {
734
+ return this.view.getFontSize();
735
+ }
736
+ onFontSizeChange(listener) {
737
+ return subscribe(this.fontSizes, listener);
738
+ }
739
+ /* Steps from the size on screen, so the first step from the page's size is one pixel away from it. */
740
+ zoom(step) {
741
+ const before = this.fontSize;
742
+ const current = this.view.getFontSize();
743
+ // A page size outside the range must not make a step the other way.
744
+ if (step !== 0 && Math.sign(clampFontSize(current + step, this.fontSizeRange) - current) !== step) {
745
+ return;
746
+ }
747
+ this.setFontSize(step === 0 ? null : current + step);
748
+ if (this.fontSize !== before) {
749
+ emit(this.fontSizes, this.fontSize);
750
+ }
751
+ }
703
752
  setReadOnly(readOnly, reason) {
704
753
  this.settings.readOnly = readOnly;
705
754
  this.settings.readOnlyReason = reason;
@@ -728,6 +777,7 @@ class SmartEditor {
728
777
  this.carets.clear();
729
778
  this.saves.clear();
730
779
  this.blurs.clear();
780
+ this.fontSizes.clear();
731
781
  this.subscription.dispose();
732
782
  this.controller.dispose();
733
783
  this.view.dispose();
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, 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, EditorRemoteCursor, EditorRange, EditorReveal, EditorRect, EditorRunCommand, EditorSelectionRanges, EditorFoldHints, EditorFoldOutline, EditorFolds, EditorSemanticToken, EditorSmartKeys, EditorApplyEditsOptions, 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;
@@ -139,6 +139,8 @@ export declare class FakeEditor implements Editor {
139
139
  setFoldOutline(outline: EditorFoldOutline): void;
140
140
  label: string;
141
141
  setLabel(label: string): void;
142
+ todoWords: readonly string[] | undefined;
143
+ setTodoWords(words: readonly string[]): void;
142
144
  setFoldHints(hints: EditorFoldHints | null): void;
143
145
  setSmartKeys(keys: Partial<EditorSmartKeys>): void;
144
146
  setIndentation(indentation: EditorIndentation): void;
@@ -224,8 +226,18 @@ export declare class FakeEditor implements Editor {
224
226
  openContextMenu(menu: EditorContextMenu): void;
225
227
  press(event: Pick<KeyboardEvent, 'key'> & Partial<KeyboardEvent>): boolean;
226
228
  applyEdits(edits: readonly EditorContentChange[], options?: EditorApplyEditsOptions): boolean;
229
+ readonly cleanups: EditorWhitespaceCleanup[];
230
+ savedMarks: number;
231
+ cleanUpWhitespace(options: EditorWhitespaceCleanup): boolean;
232
+ markSaved(): void;
227
233
  setTheme(theme: EditorTheme): void;
228
234
  refreshFont(): void;
235
+ fontSize: number | null;
236
+ private readonly fontSizeListeners;
237
+ setFontSize(size: number | null): void;
238
+ getFontSize(): number;
239
+ onFontSizeChange(listener: (size: number | null) => void): () => void;
240
+ zoomTo(size: number | null): void;
229
241
  setReadOnly(readOnly: boolean, reason?: string): void;
230
242
  isReadOnly(): boolean;
231
243
  readonly commands: EditorRunCommand[];
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,6 +574,10 @@ 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
  }
@@ -810,12 +816,41 @@ export class FakeEditor {
810
816
  }));
811
817
  return this.edit(ordered, 'command', options.mergeUndo === true, place);
812
818
  }
819
+ /* The options of every cleanup asked for, in order; the fake changes no text for them. */
820
+ cleanups = [];
821
+ /* How often the text was marked as saved, by a cleanup or on its own. */
822
+ savedMarks = 0;
823
+ cleanUpWhitespace(options) {
824
+ this.cleanups.push({ ...options });
825
+ this.markSaved();
826
+ return false;
827
+ }
828
+ markSaved() {
829
+ this.savedMarks += 1;
830
+ }
813
831
  setTheme(theme) {
814
832
  this.theme = theme;
815
833
  }
816
834
  refreshFont() {
817
835
  this.fontRefreshes += 1;
818
836
  }
837
+ /* The size `setFontSize` set, null for the page's. */
838
+ fontSize = null;
839
+ fontSizeListeners = new Set();
840
+ setFontSize(size) {
841
+ this.fontSize = size;
842
+ }
843
+ getFontSize() {
844
+ return this.fontSize ?? 13;
845
+ }
846
+ onFontSizeChange(listener) {
847
+ return subscribe(this.fontSizeListeners, listener);
848
+ }
849
+ /* What a person zooming would do: the new size goes to the listeners. */
850
+ zoomTo(size) {
851
+ this.fontSize = size;
852
+ emit(this.fontSizeListeners, size);
853
+ }
819
854
  setReadOnly(readOnly, reason) {
820
855
  this.readOnly = readOnly;
821
856
  this.readOnlyReason = reason;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
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';
5
7
  export { DEFAULT_SMART_KEYS } from './smart-keys.ts';
6
8
  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';
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';
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
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';
5
7
  export { DEFAULT_SMART_KEYS } from './smart-keys.js';
6
8
  export { shikiScopeColors, shikiTokenizers } from './shiki.js';
@@ -26,6 +26,11 @@ export declare const KEYMAP: {
26
26
  readonly splitLine: KeyBinding;
27
27
  readonly toggleCase: KeyBinding;
28
28
  readonly autoIndentLines: KeyBinding;
29
+ readonly sortLines: KeyBinding;
30
+ readonly reverseLines: KeyBinding;
31
+ readonly uniqueLines: KeyBinding;
32
+ readonly convertIndentsToSpaces: KeyBinding;
33
+ readonly convertIndentsToTabs: KeyBinding;
29
34
  readonly expandSelection: KeyBinding;
30
35
  readonly shrinkSelection: KeyBinding;
31
36
  readonly matchBrace: KeyBinding;
@@ -64,6 +69,9 @@ export declare const KEYMAP: {
64
69
  readonly expandAllToLevel3: KeyBinding;
65
70
  readonly expandAllToLevel4: KeyBinding;
66
71
  readonly expandAllToLevel5: KeyBinding;
72
+ readonly increaseFontSize: KeyBinding;
73
+ readonly decreaseFontSize: KeyBinding;
74
+ readonly resetFontSize: KeyBinding;
67
75
  readonly triggerCompletion: KeyBinding;
68
76
  readonly parameterInfo: KeyBinding;
69
77
  readonly quickInfo: KeyBinding;
@@ -96,7 +104,7 @@ export declare const KEYMAP: {
96
104
  readonly replace: KeyBinding;
97
105
  };
98
106
  export type KeymapId = keyof typeof KEYMAP;
99
- export type OptionalKeymapId = 'toggleRenderedDocs';
107
+ export type OptionalKeymapId = 'toggleRenderedDocs' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize' | 'sortLines' | 'reverseLines' | 'uniqueLines' | 'convertIndentsToSpaces' | 'convertIndentsToTabs';
100
108
  export type Keymap = Readonly<Record<Exclude<KeymapId, OptionalKeymapId>, KeyBinding> & Partial<Record<OptionalKeymapId, KeyBinding>>>;
101
109
  export type KeymapOverrides = Partial<Record<KeymapId, Partial<KeyBinding>>>;
102
110
  export declare const KEYMAP_IDS: KeymapId[];
@@ -23,6 +23,12 @@ export const KEYMAP = {
23
23
  splitLine: bind('Mod+Enter'),
24
24
  toggleCase: bind('Mod+Shift+U'),
25
25
  autoIndentLines: bind('Ctrl+Alt+I'),
26
+ // The platform binds no chord to these, so they live in the menu and the palette.
27
+ sortLines: bind(null),
28
+ reverseLines: bind(null),
29
+ uniqueLines: bind(null),
30
+ convertIndentsToSpaces: bind(null),
31
+ convertIndentsToTabs: bind(null),
26
32
  expandSelection: bind('Alt+ArrowUp', 'Ctrl+W'),
27
33
  shrinkSelection: bind('Alt+ArrowDown'),
28
34
  matchBrace: bind('Ctrl+M', 'Ctrl+Shift+M'),
@@ -63,6 +69,10 @@ export const KEYMAP = {
63
69
  expandAllToLevel3: bind(null),
64
70
  expandAllToLevel4: bind(null),
65
71
  expandAllToLevel5: bind(null),
72
+ // The platform binds no chord to these, and the zoom chords of other editors are the fold keys here.
73
+ increaseFontSize: bind(null),
74
+ decreaseFontSize: bind(null),
75
+ resetFontSize: bind(null),
66
76
  triggerCompletion: bind('Ctrl+Space'),
67
77
  parameterInfo: bind('Mod+P'),
68
78
  quickInfo: bind('Ctrl+J', 'Ctrl+Q'),
package/dist/keymap.js CHANGED
@@ -24,6 +24,11 @@ const ACTIONS = {
24
24
  splitLine: command('splitLine'),
25
25
  toggleCase: command('toggleCase'),
26
26
  autoIndentLines: command('autoIndentLines'),
27
+ sortLines: command('sortLines'),
28
+ reverseLines: command('reverseLines'),
29
+ uniqueLines: command('uniqueLines'),
30
+ convertIndentsToSpaces: command('convertIndentsToSpaces'),
31
+ convertIndentsToTabs: command('convertIndentsToTabs'),
27
32
  expandSelection: command('expandSelection'),
28
33
  shrinkSelection: command('shrinkSelection'),
29
34
  matchBrace: { type: 'bracket' },
@@ -59,7 +64,10 @@ const ACTIONS = {
59
64
  expandAllToLevel3: view('expandAllToLevel3'),
60
65
  expandAllToLevel4: view('expandAllToLevel4'),
61
66
  expandAllToLevel5: view('expandAllToLevel5'),
62
- toggleColumnMode: view('toggleColumnMode')
67
+ toggleColumnMode: view('toggleColumnMode'),
68
+ increaseFontSize: view('increaseFontSize'),
69
+ decreaseFontSize: view('decreaseFontSize'),
70
+ resetFontSize: view('resetFontSize')
63
71
  };
64
72
  const PUNCTUATION_CODES = {
65
73
  ',': 'Comma',
@@ -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;
@@ -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
+ }
package/dist/types.d.ts CHANGED
@@ -1,9 +1,15 @@
1
1
  import type { EditorSearchClientOptions } from './search-client.ts';
2
2
  import type { Keymap } from './keymap-table.ts';
3
- import type { EditorCommand, FoldRole } from '@adecore/editor-core';
3
+ import type { EditorCommand, FoldRole, WhitespaceCleanupOptions } from '@adecore/editor-core';
4
4
  export type { EditorCommand, FoldRole };
5
+ export type EditorWhitespaceCleanup = WhitespaceCleanupOptions;
5
6
  export type EditorFoldOutline = 'off' | 'hover' | 'always';
6
- export type EditorViewCommand = 'collapseRegion' | 'expandRegion' | 'collapseAllRegions' | 'expandAllRegions' | 'collapseRegionRecursively' | 'expandRegionRecursively' | 'foldSelection' | 'collapseDocComments' | 'expandDocComments' | 'expandAllToLevel1' | 'expandAllToLevel2' | 'expandAllToLevel3' | 'expandAllToLevel4' | 'expandAllToLevel5' | 'toggleColumnMode';
7
+ export type EditorViewCommand = 'collapseRegion' | 'expandRegion' | 'collapseAllRegions' | 'expandAllRegions' | 'collapseRegionRecursively' | 'expandRegionRecursively' | 'foldSelection' | 'collapseDocComments' | 'expandDocComments' | 'expandAllToLevel1' | 'expandAllToLevel2' | 'expandAllToLevel3' | 'expandAllToLevel4' | 'expandAllToLevel5' | 'toggleColumnMode' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize';
8
+ export interface EditorZoom {
9
+ readonly min?: number;
10
+ readonly max?: number;
11
+ readonly wheel?: boolean;
12
+ }
7
13
  export type EditorRunCommand = EditorCommand | EditorViewCommand;
8
14
  export interface EditorFoldRange {
9
15
  readonly startLine: number;
@@ -38,6 +44,9 @@ export interface EditorOptions {
38
44
  readonly line?: number;
39
45
  readonly column?: number;
40
46
  readonly scrollTop?: number;
47
+ readonly todoWords?: readonly string[];
48
+ readonly fontSize?: number;
49
+ readonly zoom?: EditorZoom;
41
50
  }
42
51
  export interface EditorMessages {
43
52
  readonly noMoreOccurrences: string;
@@ -324,6 +333,7 @@ export interface Editor {
324
333
  setWhitespace(whitespace: boolean): void;
325
334
  setRightMargin(column: number | null): void;
326
335
  setFoldOutline(outline: EditorFoldOutline): void;
336
+ setTodoWords?(words: readonly string[]): void;
327
337
  setLabel(label: string): void;
328
338
  setFoldHints(hints: EditorFoldHints | null): void;
329
339
  setChangeMarks(marks: readonly EditorChangeMark[], layer?: string): void;
@@ -385,8 +395,13 @@ export interface Editor {
385
395
  setSelectionRanges(provider: EditorSelectionRanges | null): void;
386
396
  trackRange(range: EditorRange): EditorTrackedRange;
387
397
  applyEdits(edits: readonly EditorContentChange[], options?: EditorApplyEditsOptions): boolean;
398
+ cleanUpWhitespace?(options: EditorWhitespaceCleanup): boolean;
399
+ markSaved?(): void;
388
400
  setTheme(theme: EditorTheme): void;
389
401
  refreshFont(): void;
402
+ setFontSize?(size: number | null): void;
403
+ getFontSize?(): number;
404
+ onFontSizeChange?(listener: (size: number | null) => void): () => void;
390
405
  setReadOnly(readOnly: boolean, reason?: string): void;
391
406
  isReadOnly?(): boolean;
392
407
  runCommand(command: EditorRunCommand): boolean;
package/dist/view.d.ts CHANGED
@@ -22,6 +22,7 @@ export interface ViewSettings {
22
22
  foldOutline: 'off' | 'hover' | 'always';
23
23
  folding?: boolean;
24
24
  stickyScroll?: boolean;
25
+ todoWords?: readonly string[];
25
26
  }
26
27
  export type ViewFold = Omit<FoldingRange, 'kind'> & {
27
28
  kind: FoldingRange['kind'] | 'custom';
@@ -59,6 +60,7 @@ export declare class EditorView {
59
60
  readonly layout: EditorLayout;
60
61
  readonly find: FindController;
61
62
  readonly tokens: TokenCache;
63
+ private readonly todos;
62
64
  private readonly content;
63
65
  private readonly gutter;
64
66
  private readonly gutterLines;
@@ -125,6 +127,7 @@ export declare class EditorView {
125
127
  private caretTimer;
126
128
  private caretKey;
127
129
  private renderedScroll;
130
+ private fontSize;
128
131
  private font;
129
132
  private layoutVersion;
130
133
  private revision;
@@ -171,6 +174,8 @@ export declare class EditorView {
171
174
  applySettings(): void;
172
175
  repaint(): void;
173
176
  refreshFont(): void;
177
+ setFontSize(size: number | null): void;
178
+ getFontSize(): number;
174
179
  setTokenizer(tokenizer: LineTokenizer | null): void;
175
180
  grammarChanged(): void;
176
181
  setLineTokens(tokens: ((line: number) => readonly LineToken[] | null | undefined) | null): void;
@@ -190,6 +195,7 @@ export declare class EditorView {
190
195
  setSemanticTokens(tokens: readonly ViewSemanticToken[] | null): void;
191
196
  setScopeColors(colors: ScopeColors | null): void;
192
197
  private styledTokensOf;
198
+ private semanticTokensOf;
193
199
  private semanticSpans;
194
200
  setMarkers(markers: readonly ViewMarker[]): void;
195
201
  setComposition(text: string | undefined): void;