@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.
@@ -15,10 +15,15 @@ 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'),
@@ -30,8 +35,21 @@ export const KEYMAP = {
30
35
  convertIndentsToSpaces: bind(null),
31
36
  convertIndentsToTabs: bind(null),
32
37
  expandSelection: bind('Alt+ArrowUp', 'Ctrl+W'),
33
- 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'),
34
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+]'),
35
53
  selectNextOccurrence: bind('Ctrl+G', 'Alt+J'),
36
54
  unselectOccurrence: bind('Ctrl+Shift+G', 'Alt+Shift+J'),
37
55
  selectAllOccurrences: bind('Meta+Ctrl+G', 'Ctrl+Alt+Shift+J'),
@@ -51,9 +69,10 @@ export const KEYMAP = {
51
69
  smartEnd: bind('Meta+ArrowRight', null),
52
70
  selectSmartEnd: bind('Meta+Shift+ArrowRight', null),
53
71
  textStart: bind('Meta+ArrowUp', null),
54
- 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),
55
74
  textEnd: bind('Meta+ArrowDown', null),
56
- selectTextEnd: bind('Meta+Shift+ArrowDown', null),
75
+ selectTextEnd: bind(null),
57
76
  collapse: bind('Mod+-'),
58
77
  expand: bind('Mod+='),
59
78
  collapseAll: bind('Mod+Shift+-'),
@@ -81,6 +100,13 @@ export const KEYMAP = {
81
100
  renameSymbol: bind('Shift+F6'),
82
101
  organizeImports: bind('Ctrl+Alt+O'),
83
102
  formatDocument: bind('Mod+Alt+L'),
103
+ insertLiveTemplate: bind('Mod+J'),
104
+ surroundWith: bind('Mod+Alt+T'),
105
+ surroundWithLiveTemplate: bind('Mod+Alt+J'),
106
+ unwrap: bind('Mod+Shift+Delete'),
107
+ saveAsLiveTemplate: bind(null),
108
+ // Surround with Emmet has no chord of its own: it is a row of Surround with Live Template, which has this one.
109
+ wrapWithAbbreviation: bind(null),
84
110
  selectionToChat: bind(null),
85
111
  inlineEdit: bind(null),
86
112
  suggestInline: bind(null),
@@ -92,6 +118,7 @@ export const KEYMAP = {
92
118
  peekDefinition: bind('Alt+Space', 'Ctrl+Shift+I'),
93
119
  peekReferences: bind('Alt+F7'),
94
120
  callHierarchy: bind('Ctrl+Alt+H'),
121
+ typeHierarchy: bind('Ctrl+H'),
95
122
  toggleRenderedDocs: bind('Ctrl+Alt+Q'),
96
123
  historyBack: bind('Mod+[', 'Ctrl+Alt+ArrowLeft'),
97
124
  historyForward: bind('Mod+]', 'Ctrl+Alt+ArrowRight'),
package/dist/keymap.js CHANGED
@@ -16,10 +16,15 @@ 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'),
@@ -32,6 +37,15 @@ const ACTIONS = {
32
37
  expandSelection: command('expandSelection'),
33
38
  shrinkSelection: command('shrinkSelection'),
34
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'),
35
49
  selectNextOccurrence: command('selectNextOccurrence'),
36
50
  unselectOccurrence: command('unselectOccurrence'),
37
51
  selectAllOccurrences: command('selectAllOccurrences'),
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) {
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,10 @@
1
+ import type { DocumentModel } from '@adecore/editor-core';
2
+ import type { EditorPosition, EditorPostfixOperand, EditorRange, EditorSurroundShape, EditorTextPlace, EditorUnwrapTarget } from './types.ts';
3
+ export interface Coordinates {
4
+ positionAt(offset: number): EditorPosition;
5
+ offsetAt(position: EditorPosition): number;
6
+ }
7
+ export declare function placeAt(model: DocumentModel, coordinates: Coordinates, position: EditorPosition, language: string): EditorTextPlace;
8
+ export declare function postfixOperands(model: DocumentModel, coordinates: Coordinates, dot: EditorPosition, language: string): EditorPostfixOperand[];
9
+ export declare function surroundShape(model: DocumentModel, coordinates: Coordinates, range: EditorRange, language: string): EditorSurroundShape | null;
10
+ export declare function unwrapTargets(model: DocumentModel, coordinates: Coordinates, position: EditorPosition, language: string, indentUnit: string): EditorUnwrapTarget[];
@@ -0,0 +1,24 @@
1
+ function rangeOf(coordinates, from, to) {
2
+ return { start: coordinates.positionAt(from), end: coordinates.positionAt(to) };
3
+ }
4
+ export function placeAt(model, coordinates, position, language) {
5
+ return model.placeAt(coordinates.offsetAt(position), language);
6
+ }
7
+ export function postfixOperands(model, coordinates, dot, language) {
8
+ const at = coordinates.offsetAt(dot);
9
+ return model.postfixOperands(at, language).map((operand) => ({
10
+ range: rangeOf(coordinates, operand.from, operand.from + model.slice(operand.from, at).trimEnd().length),
11
+ statement: operand.statement
12
+ }));
13
+ }
14
+ export function surroundShape(model, coordinates, range, language) {
15
+ const shape = model.surroundShape(coordinates.offsetAt(range.start), coordinates.offsetAt(range.end), language);
16
+ return shape === null ? null : { range: rangeOf(coordinates, shape.from, shape.to), statements: shape.statements, expression: shape.expression };
17
+ }
18
+ export function unwrapTargets(model, coordinates, position, language, indentUnit) {
19
+ return model.unwrapTargets(coordinates.offsetAt(position), language, indentUnit).map((target) => ({
20
+ kind: target.kind,
21
+ range: rangeOf(coordinates, target.range.from, target.range.to),
22
+ edits: target.edits.map((edit) => ({ range: rangeOf(coordinates, edit.from, edit.to), text: edit.text }))
23
+ }));
24
+ }
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { EditorSearchClientOptions } from './search-client.ts';
2
2
  import type { Keymap } from './keymap-table.ts';
3
- import type { EditorCommand, FoldRole, WhitespaceCleanupOptions } from '@adecore/editor-core';
3
+ import type { EditorCommand, FoldRole, TextPlaceKind, UnwrapKind, WhitespaceCleanupOptions } from '@adecore/editor-core';
4
4
  export type { EditorCommand, FoldRole };
5
5
  export type EditorWhitespaceCleanup = WhitespaceCleanupOptions;
6
6
  export type EditorFoldOutline = 'off' | 'hover' | 'always';
7
- export type EditorViewCommand = 'collapseRegion' | 'expandRegion' | 'collapseAllRegions' | 'expandAllRegions' | 'collapseRegionRecursively' | 'expandRegionRecursively' | 'foldSelection' | 'collapseDocComments' | 'expandDocComments' | 'expandAllToLevel1' | 'expandAllToLevel2' | 'expandAllToLevel3' | 'expandAllToLevel4' | 'expandAllToLevel5' | 'toggleColumnMode' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize';
7
+ export type EditorViewCommand = 'collapseRegion' | 'expandRegion' | 'collapseAllRegions' | 'expandAllRegions' | 'collapseRegionRecursively' | 'expandRegionRecursively' | 'foldSelection' | 'collapseDocComments' | 'expandDocComments' | 'expandAllToLevel1' | 'expandAllToLevel2' | 'expandAllToLevel3' | 'expandAllToLevel4' | 'expandAllToLevel5' | 'toggleColumnMode' | 'increaseFontSize' | 'decreaseFontSize' | 'resetFontSize' | 'methodUp' | 'methodDown' | 'nextChange' | 'previousChange' | 'pasteAsPlainText';
8
8
  export interface EditorZoom {
9
9
  readonly min?: number;
10
10
  readonly max?: number;
@@ -139,6 +139,33 @@ export interface EditorContentChange {
139
139
  readonly range: EditorRange;
140
140
  readonly text: string;
141
141
  }
142
+ export interface EditorTextPlace {
143
+ readonly language: string;
144
+ readonly kind: TextPlaceKind;
145
+ }
146
+ export interface EditorPostfixOperand {
147
+ readonly range: EditorRange;
148
+ readonly statement: boolean;
149
+ }
150
+ export interface EditorSurroundShape {
151
+ readonly range: EditorRange;
152
+ readonly statements: boolean;
153
+ readonly expression: boolean;
154
+ }
155
+ export interface EditorUnwrapTarget {
156
+ readonly kind: UnwrapKind;
157
+ readonly range: EditorRange;
158
+ readonly edits: readonly EditorContentChange[];
159
+ }
160
+ export interface EditorCopy {
161
+ readonly text: string;
162
+ readonly wholeLines: boolean;
163
+ readonly cut: boolean;
164
+ }
165
+ export interface EditorPasteOptions {
166
+ readonly wholeLines?: boolean;
167
+ readonly reindent?: boolean;
168
+ }
142
169
  export interface EditorApplyEditsOptions {
143
170
  readonly mapSelections?: (range: EditorRange) => EditorRange;
144
171
  readonly mergeUndo?: boolean;
@@ -240,6 +267,7 @@ export interface EditorInlayHint {
240
267
  readonly parts?: readonly string[];
241
268
  readonly paddingLeft?: boolean;
242
269
  readonly paddingRight?: boolean;
270
+ readonly swatch?: string;
243
271
  }
244
272
  export interface EditorInlayTarget {
245
273
  readonly id: string;
@@ -336,6 +364,12 @@ export interface Editor {
336
364
  setTodoWords?(words: readonly string[]): void;
337
365
  setLabel(label: string): void;
338
366
  setFoldHints(hints: EditorFoldHints | null): void;
367
+ setDeclarations?(positions: readonly EditorPosition[] | null): void;
368
+ onNavigate?(listener: (from: EditorPosition) => void): () => void;
369
+ hasFocus?(): boolean;
370
+ setWordSources?(source: (() => readonly string[]) | null): void;
371
+ onCopy?(listener: (copy: EditorCopy) => void): () => void;
372
+ paste?(text: string, options?: EditorPasteOptions): boolean;
339
373
  setChangeMarks(marks: readonly EditorChangeMark[], layer?: string): void;
340
374
  setBlocks(blocks: readonly EditorBlock[] | null): void;
341
375
  onScope(listener: (scope: readonly EditorBlock[]) => void): () => void;
@@ -351,7 +385,7 @@ export interface Editor {
351
385
  setHighlights(highlights: readonly EditorHighlight[]): void;
352
386
  setLink(range: EditorRange | null): void;
353
387
  setSemanticTokens(tokens: readonly EditorSemanticToken[] | null): void;
354
- setInlayHints(hints: readonly EditorInlayHint[]): void;
388
+ setInlayHints(hints: readonly EditorInlayHint[], owner?: string): void;
355
389
  onInlayHover?(listener: (target: EditorInlayTarget | null) => void): () => void;
356
390
  onInlayClick?(handler: EditorInlayClickHandler): () => void;
357
391
  setInlayLink?(target: {
@@ -404,6 +438,10 @@ export interface Editor {
404
438
  onFontSizeChange?(listener: (size: number | null) => void): () => void;
405
439
  setReadOnly(readOnly: boolean, reason?: string): void;
406
440
  isReadOnly?(): boolean;
441
+ placeAt?(position: EditorPosition): EditorTextPlace;
442
+ postfixOperands?(dot: EditorPosition): readonly EditorPostfixOperand[];
443
+ surroundShape?(range: EditorRange): EditorSurroundShape | null;
444
+ unwrapTargets?(position: EditorPosition): readonly EditorUnwrapTarget[];
407
445
  runCommand(command: EditorRunCommand): boolean;
408
446
  focus(): void;
409
447
  dispose(): void;
package/dist/view.d.ts CHANGED
@@ -143,6 +143,7 @@ export declare class EditorView {
143
143
  private lenses;
144
144
  private readonly replacements;
145
145
  private occurrences;
146
+ private hippieSource;
146
147
  private replacePreview;
147
148
  private dropCaret;
148
149
  private activeGuideCache;
@@ -157,6 +158,7 @@ export declare class EditorView {
157
158
  private customFolds;
158
159
  private collapsed;
159
160
  private foldHints;
161
+ private declarations;
160
162
  private defaultRoles;
161
163
  private settled;
162
164
  private foldTimer;
@@ -188,6 +190,10 @@ export declare class EditorView {
188
190
  setLenses(lenses: readonly BlockWidget[]): void;
189
191
  private allBlocks;
190
192
  setOccurrences(occurrences: readonly Occurrence[]): void;
193
+ setHippieSource(range: {
194
+ from: number;
195
+ to: number;
196
+ } | null): void;
191
197
  setLink(link: {
192
198
  from: number;
193
199
  to: number;
@@ -239,6 +245,7 @@ export declare class EditorView {
239
245
  private foldStates;
240
246
  private scheduleFolds;
241
247
  refreshFolds(): void;
248
+ structureRanges(): FoldingRange[];
242
249
  private withCustomFolds;
243
250
  private closerOf;
244
251
  private foldAt;
@@ -274,6 +281,10 @@ export declare class EditorView {
274
281
  endLine: number;
275
282
  }[];
276
283
  } | null, defaults?: readonly FoldRole[]): void;
284
+ setDeclarations(offsets: readonly number[] | null): void;
285
+ private methodStops;
286
+ goToChange(direction: -1 | 1): boolean;
287
+ goToMethod(direction: -1 | 1): boolean;
277
288
  setFoldHints(hints: FoldHints | null): void;
278
289
  private applyFoldDefaults;
279
290
  foldRole(role: FoldRole, collapse: boolean): void;
package/dist/view.js CHANGED
@@ -168,6 +168,7 @@ export class EditorView {
168
168
  lenses = [];
169
169
  replacements = new Map();
170
170
  occurrences = [];
171
+ hippieSource = null;
171
172
  replacePreview = null;
172
173
  dropCaret = null;
173
174
  activeGuideCache;
@@ -186,6 +187,7 @@ export class EditorView {
186
187
  collapsed = new Set();
187
188
  /* What a language server said about the folds, in offsets that follow the text through edits. */
188
189
  foldHints = null;
190
+ declarations = null;
189
191
  /* The roles that fold by themselves while the file is as it was opened; null once a person edited or the file opened with folds of its own. */
190
192
  defaultRoles = null;
191
193
  /* The folds somebody already decided on, a person or the defaults, so a later answer of a server leaves them as they are. */
@@ -427,6 +429,14 @@ export class EditorView {
427
429
  this.occurrences = [...occurrences];
428
430
  this.render();
429
431
  }
432
+ /* The word Hippie completion took from this document, shown until the next key or edit; null takes the mark away. */
433
+ setHippieSource(range) {
434
+ if (range === null && this.hippieSource === null) {
435
+ return;
436
+ }
437
+ this.hippieSource = range;
438
+ this.render();
439
+ }
430
440
  /* A name underlined as a link a press follows, with the pointer to match; null takes it away. */
431
441
  setLink(link) {
432
442
  this.link = link;
@@ -621,6 +631,7 @@ export class EditorView {
621
631
  this.markerVersion++;
622
632
  const anchor = this.topAnchor;
623
633
  this.occurrences = [];
634
+ this.hippieSource = null;
624
635
  this.link = null;
625
636
  this.content.classList.remove('se-content-link');
626
637
  this.outline.edited(batches);
@@ -668,6 +679,9 @@ export class EditorView {
668
679
  if (this.foldHints !== null) {
669
680
  this.foldHints = mapFoldHints(this.foldHints, map);
670
681
  }
682
+ if (this.declarations !== null) {
683
+ this.declarations = this.declarations.map((offset) => map(offset));
684
+ }
671
685
  this.customFolds = this.customFolds.map((fold) => mapRange(fold, map));
672
686
  this.find.mapBounds(changes);
673
687
  for (const [layer, marks] of this.changeLayers) {
@@ -801,20 +815,25 @@ export class EditorView {
801
815
  if (this.disposed || this.settings.folding === false) {
802
816
  return;
803
817
  }
804
- const ranges = this.model.getLength() > FOLD_LIMIT
805
- ? []
806
- : this.model.getFoldingRanges({
807
- indentation: /^(python|py|yaml|yml)$/.test(this.settings.language ?? ''),
808
- tabSize: this.settings.tabSize,
809
- ...(this.settings.language === undefined ? {} : { language: this.settings.language }),
810
- ...(this.foldHints === null ? {} : { hints: this.foldHints })
811
- });
818
+ const ranges = this.structureRanges();
812
819
  this.foldRanges = this.withCustomFolds(ranges);
813
820
  this.outline.setStructure(structuralEntries(ranges, this.model, (from, to) => this.model.slice(from, to), (offset) => this.model.matchBracket(offset, this.settings.language)?.mate));
814
821
  const anchors = new Set(this.foldRanges.map((range) => range.from));
815
822
  this.collapsed = new Set([...this.collapsed].filter((anchor) => anchors.has(anchor)));
816
823
  this.refoldLayout();
817
824
  }
825
+ /* The folding ranges the document has now, a server's included, whether or not the folds are drawn. */
826
+ structureRanges() {
827
+ if (this.model.getLength() > FOLD_LIMIT) {
828
+ return [];
829
+ }
830
+ return this.model.getFoldingRanges({
831
+ indentation: /^(python|py|yaml|yml)$/.test(this.settings.language ?? ''),
832
+ tabSize: this.settings.tabSize,
833
+ ...(this.settings.language === undefined ? {} : { language: this.settings.language }),
834
+ ...(this.foldHints === null ? {} : { hints: this.foldHints })
835
+ });
836
+ }
818
837
  /* The folds a person made of a selection, which the document's own structure does not know, among the ones it does. */
819
838
  withCustomFolds(ranges) {
820
839
  const custom = this.customFolds.flatMap((fold) => {
@@ -1023,6 +1042,49 @@ export class EditorView {
1023
1042
  this.applyFoldDefaults();
1024
1043
  this.refoldLayout();
1025
1044
  }
1045
+ /* The places Method Up and Down stop at; they follow their text through edits until set again, and null goes back to the editor's own reading. */
1046
+ setDeclarations(offsets) {
1047
+ this.declarations = offsets === null ? null : [...new Set(offsets)].sort((left, right) => left - right);
1048
+ }
1049
+ methodStops() {
1050
+ if (this.declarations !== null) {
1051
+ return this.declarations;
1052
+ }
1053
+ const sections = this.foldRanges.filter((range) => range.kind === 'section').map((range) => this.model.getLine(range.startLine).start);
1054
+ return [...new Set([...this.outline.declarationStops(this.model), ...sections])].sort((left, right) => left - right);
1055
+ }
1056
+ /* Moves the one caret to the first line of the next or previous change mark, of any layer. False when there is none. */
1057
+ goToChange(direction) {
1058
+ const line = this.model.positionAt(this.model.getPrimary().head).line;
1059
+ const { runs } = this.resolvedChanges();
1060
+ // A run the caret is in is where it already is; a deleted run is the line its mark sits on.
1061
+ const target = direction === 1 ? runs.find((run) => run.start > line) : runs.findLast((run) => (run.kind === 'deleted' ? run.start : run.end) < line);
1062
+ if (target === undefined) {
1063
+ return false;
1064
+ }
1065
+ const offset = this.model.getLine(Math.min(target.start, this.model.getLineCount() - 1)).start;
1066
+ this.model.setSelections([{ anchor: offset, head: offset }]);
1067
+ this.revealOffset(offset, 'center');
1068
+ return true;
1069
+ }
1070
+ /* Moves the one caret to the previous or next declaration on another line. False when there is none. */
1071
+ goToMethod(direction) {
1072
+ // The structure is read after a pause in typing, so a press right after an edit reads it now.
1073
+ if (this.declarations === null) {
1074
+ this.refreshFolds();
1075
+ }
1076
+ const line = this.model.positionAt(this.model.getPrimary().head).line;
1077
+ const stops = this.methodStops();
1078
+ const target = direction === 1
1079
+ ? stops.find((offset) => this.model.positionAt(offset).line > line)
1080
+ : stops.findLast((offset) => this.model.positionAt(offset).line < line);
1081
+ if (target === undefined) {
1082
+ return false;
1083
+ }
1084
+ this.model.setSelections([{ anchor: target, head: target }]);
1085
+ this.revealOffset(target, direction === 1 ? 'centerDown' : 'centerUp');
1086
+ return true;
1087
+ }
1026
1088
  /* Hands over what a language server knows about the folds; null forgets it. The defaults fold what the answer names, once. */
1027
1089
  setFoldHints(hints) {
1028
1090
  this.foldHints = hints;
@@ -1866,6 +1928,9 @@ export class EditorView {
1866
1928
  });
1867
1929
  }
1868
1930
  }
1931
+ if (this.hippieSource !== null) {
1932
+ marks.push({ className: 'se-hippie-source', rects: this.paintRectangles(this.hippieSource.from, this.hippieSource.to, rows) });
1933
+ }
1869
1934
  const marginX = this.settings.rightMargin === null ? null : this.settings.rightMargin * this.layout.metrics.charWidth;
1870
1935
  // A margin past the content would stretch the scroll extent to itself, so it shows only where the text or the view reaches it.
1871
1936
  if (marginX !== null && marginX < Math.max(this.layout.width, this.viewportWidth)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/editor",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -54,7 +54,7 @@
54
54
  "test": "bun test --conditions=source"
55
55
  },
56
56
  "dependencies": {
57
- "@adecore/editor-core": "0.42.0",
57
+ "@adecore/editor-core": "0.44.0",
58
58
  "linkedom": "^0.18.14",
59
59
  "shiki": "^4.5.0"
60
60
  },