@adecore/editor 0.43.0 → 0.44.1

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.
@@ -336,6 +336,7 @@ export class InputController {
336
336
  indentGuides: settings.guides,
337
337
  smartSemicolon: keys.smartSemicolon,
338
338
  smartArrow: keys.smartArrow,
339
+ templateLiterals: keys.templateLiterals,
339
340
  language: settings.language ?? 'plaintext',
340
341
  camelCase: keys.camelHumps,
341
342
  visualLine: (offset) => this.view.rowOf(offset),
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';
@@ -784,6 +785,19 @@ class SmartEditor {
784
785
  isReadOnly() {
785
786
  return this.settings.readOnly;
786
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
+ }
787
801
  runCommand(command) {
788
802
  return this.controller.command(command);
789
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, EditorCopy, EditorPasteOptions, 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;
@@ -257,6 +257,10 @@ export declare class FakeEditor implements Editor {
257
257
  zoomTo(size: number | null): void;
258
258
  setReadOnly(readOnly: boolean, reason?: string): void;
259
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[];
260
264
  readonly commands: EditorRunCommand[];
261
265
  runCommand(command: EditorRunCommand): boolean;
262
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. */
@@ -918,6 +919,19 @@ export class FakeEditor {
918
919
  isReadOnly() {
919
920
  return this.readOnly;
920
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
+ }
921
935
  /* The commands run, in order; of its own the fake carries out only undo and redo. */
922
936
  commands = [];
923
937
  runCommand(command) {
package/dist/index.d.ts CHANGED
@@ -3,8 +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';
7
8
  export { rememberCopy } from './controller.ts';
8
9
  export { DEFAULT_SMART_KEYS } from './smart-keys.ts';
9
10
  export { shikiScopeColors, shikiTokenizers } from './shiki.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';
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,7 @@ 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';
7
8
  export { rememberCopy } from './controller.js';
8
9
  export { DEFAULT_SMART_KEYS } from './smart-keys.js';
@@ -97,6 +97,11 @@ export declare const KEYMAP: {
97
97
  readonly renameSymbol: KeyBinding;
98
98
  readonly organizeImports: KeyBinding;
99
99
  readonly formatDocument: KeyBinding;
100
+ readonly insertLiveTemplate: KeyBinding;
101
+ readonly surroundWith: KeyBinding;
102
+ readonly surroundWithLiveTemplate: KeyBinding;
103
+ readonly unwrap: KeyBinding;
104
+ readonly saveAsLiveTemplate: KeyBinding;
100
105
  readonly wrapWithAbbreviation: KeyBinding;
101
106
  readonly selectionToChat: KeyBinding;
102
107
  readonly inlineEdit: KeyBinding;
@@ -123,7 +128,7 @@ export declare const KEYMAP: {
123
128
  readonly replace: KeyBinding;
124
129
  };
125
130
  export type KeymapId = keyof typeof KEYMAP;
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';
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';
127
132
  export type Keymap = Readonly<Record<Exclude<KeymapId, OptionalKeymapId>, KeyBinding> & Partial<Record<OptionalKeymapId, KeyBinding>>>;
128
133
  export type KeymapOverrides = Partial<Record<KeymapId, Partial<KeyBinding>>>;
129
134
  export declare const KEYMAP_IDS: KeymapId[];
@@ -100,7 +100,13 @@ export const KEYMAP = {
100
100
  renameSymbol: bind('Shift+F6'),
101
101
  organizeImports: bind('Ctrl+Alt+O'),
102
102
  formatDocument: bind('Mod+Alt+L'),
103
- wrapWithAbbreviation: bind('Mod+Alt+J'),
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),
104
110
  selectionToChat: bind(null),
105
111
  inlineEdit: bind(null),
106
112
  suggestInline: bind(null),
@@ -7,6 +7,7 @@ export const DEFAULT_SMART_KEYS = {
7
7
  indentOnPaste: true,
8
8
  smartSemicolon: true,
9
9
  smartArrow: true,
10
+ templateLiterals: true,
10
11
  camelHumps: false
11
12
  };
12
13
  /* The keys with what a host left out filled in from the defaults. */
@@ -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,6 +1,6 @@
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';
@@ -66,6 +66,7 @@ export interface EditorSmartKeys {
66
66
  readonly indentOnPaste: boolean;
67
67
  readonly smartSemicolon: boolean;
68
68
  readonly smartArrow: boolean;
69
+ readonly templateLiterals: boolean;
69
70
  readonly camelHumps: boolean;
70
71
  }
71
72
  export interface EditorFoldSymbol {
@@ -139,6 +140,24 @@ export interface EditorContentChange {
139
140
  readonly range: EditorRange;
140
141
  readonly text: string;
141
142
  }
143
+ export interface EditorTextPlace {
144
+ readonly language: string;
145
+ readonly kind: TextPlaceKind;
146
+ }
147
+ export interface EditorPostfixOperand {
148
+ readonly range: EditorRange;
149
+ readonly statement: boolean;
150
+ }
151
+ export interface EditorSurroundShape {
152
+ readonly range: EditorRange;
153
+ readonly statements: boolean;
154
+ readonly expression: boolean;
155
+ }
156
+ export interface EditorUnwrapTarget {
157
+ readonly kind: UnwrapKind;
158
+ readonly range: EditorRange;
159
+ readonly edits: readonly EditorContentChange[];
160
+ }
142
161
  export interface EditorCopy {
143
162
  readonly text: string;
144
163
  readonly wholeLines: boolean;
@@ -420,6 +439,10 @@ export interface Editor {
420
439
  onFontSizeChange?(listener: (size: number | null) => void): () => void;
421
440
  setReadOnly(readOnly: boolean, reason?: string): void;
422
441
  isReadOnly?(): boolean;
442
+ placeAt?(position: EditorPosition): EditorTextPlace;
443
+ postfixOperands?(dot: EditorPosition): readonly EditorPostfixOperand[];
444
+ surroundShape?(range: EditorRange): EditorSurroundShape | null;
445
+ unwrapTargets?(position: EditorPosition): readonly EditorUnwrapTarget[];
423
446
  runCommand(command: EditorRunCommand): boolean;
424
447
  focus(): void;
425
448
  dispose(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/editor",
3
- "version": "0.43.0",
3
+ "version": "0.44.1",
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.43.0",
57
+ "@adecore/editor-core": "0.44.1",
58
58
  "linkedom": "^0.18.14",
59
59
  "shiki": "^4.5.0"
60
60
  },
package/src/controller.ts CHANGED
@@ -425,6 +425,7 @@ export class InputController {
425
425
  indentGuides: settings.guides,
426
426
  smartSemicolon: keys.smartSemicolon,
427
427
  smartArrow: keys.smartArrow,
428
+ templateLiterals: keys.templateLiterals,
428
429
  language: settings.language ?? 'plaintext',
429
430
  camelCase: keys.camelHumps,
430
431
  visualLine: (offset) => this.view.rowOf(offset),
package/src/engine.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { changedSpans, DocumentModel, type EditorSnapshot, type FoldHints, replacementText } from '@adecore/editor-core';
2
2
  import { MAX_REPLACEMENT_CHARACTERS } from './search-protocol.ts';
3
+ import { placeAt, postfixOperands, surroundShape, unwrapTargets } from './text-queries.ts';
3
4
  import { GrammarPreparation } from './grammar-preparation.ts';
4
5
  import { InputController } from './controller.ts';
5
6
  import { resolveSmartKeys } from './smart-keys.ts';
@@ -43,6 +44,10 @@ import type {
43
44
  EditorMessages,
44
45
  EditorOptions,
45
46
  EditorPosition,
47
+ EditorPostfixOperand,
48
+ EditorSurroundShape,
49
+ EditorTextPlace,
50
+ EditorUnwrapTarget,
46
51
  EditorRemoteCursor,
47
52
  EditorRange,
48
53
  EditorReveal,
@@ -1067,6 +1072,24 @@ class SmartEditor implements Editor {
1067
1072
  return this.settings.readOnly;
1068
1073
  }
1069
1074
 
1075
+ placeAt(position: EditorPosition): EditorTextPlace {
1076
+ return placeAt(this.model, this, position, this.settings.language ?? 'plaintext');
1077
+ }
1078
+
1079
+ postfixOperands(dot: EditorPosition): readonly EditorPostfixOperand[] {
1080
+ return postfixOperands(this.model, this, dot, this.settings.language ?? 'plaintext');
1081
+ }
1082
+
1083
+ surroundShape(range: EditorRange): EditorSurroundShape | null {
1084
+ return surroundShape(this.model, this, range, this.settings.language ?? 'plaintext');
1085
+ }
1086
+
1087
+ unwrapTargets(position: EditorPosition): readonly EditorUnwrapTarget[] {
1088
+ const unit = this.settings.insertSpaces ? ' '.repeat(this.settings.tabSize) : '\t';
1089
+
1090
+ return unwrapTargets(this.model, this, position, this.settings.language ?? 'plaintext', unit);
1091
+ }
1092
+
1070
1093
  runCommand(command: EditorRunCommand): boolean {
1071
1094
  return this.controller.command(command);
1072
1095
  }
package/src/fake.ts CHANGED
@@ -34,6 +34,10 @@ import type {
34
34
  EditorMessages,
35
35
  EditorOptions,
36
36
  EditorPosition,
37
+ EditorPostfixOperand,
38
+ EditorSurroundShape,
39
+ EditorTextPlace,
40
+ EditorUnwrapTarget,
37
41
  EditorRemoteCursor,
38
42
  EditorRange,
39
43
  EditorReveal,
@@ -55,7 +59,8 @@ import type {
55
59
  EditorWidget
56
60
  } from './types.ts';
57
61
  import { emit, type Listener, subscribe } from './listeners.ts';
58
- import { changedSpan, changedSpans } from '@adecore/editor-core';
62
+ import { changedSpan, changedSpans, DocumentModel } from '@adecore/editor-core';
63
+ import { placeAt, postfixOperands, surroundShape, unwrapTargets } from './text-queries.ts';
59
64
  import { mapTrackedRange } from './tracked-range.ts';
60
65
 
61
66
  interface OffsetSelection {
@@ -1270,6 +1275,24 @@ export class FakeEditor implements Editor {
1270
1275
  return this.readOnly;
1271
1276
  }
1272
1277
 
1278
+ placeAt(position: EditorPosition): EditorTextPlace {
1279
+ return placeAt(new DocumentModel(this.text), this, position, this.language ?? 'plaintext');
1280
+ }
1281
+
1282
+ postfixOperands(dot: EditorPosition): readonly EditorPostfixOperand[] {
1283
+ return postfixOperands(new DocumentModel(this.text), this, dot, this.language ?? 'plaintext');
1284
+ }
1285
+
1286
+ surroundShape(range: EditorRange): EditorSurroundShape | null {
1287
+ return surroundShape(new DocumentModel(this.text), this, range, this.language ?? 'plaintext');
1288
+ }
1289
+
1290
+ unwrapTargets(position: EditorPosition): readonly EditorUnwrapTarget[] {
1291
+ const unit = this.indentation.insertSpaces ? ' '.repeat(this.indentation.tabSize) : '\t';
1292
+
1293
+ return unwrapTargets(new DocumentModel(this.text), this, position, this.language ?? 'plaintext', unit);
1294
+ }
1295
+
1273
1296
  /* The commands run, in order; of its own the fake carries out only undo and redo. */
1274
1297
  readonly commands: EditorRunCommand[] = [];
1275
1298
 
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ 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';
7
8
  export { rememberCopy } from './controller.ts';
8
9
  export { DEFAULT_SMART_KEYS } from './smart-keys.ts';
@@ -64,6 +65,10 @@ export type {
64
65
  EditorApplyEditsOptions,
65
66
  EditorCopy,
66
67
  EditorPasteOptions,
68
+ EditorPostfixOperand,
69
+ EditorSurroundShape,
70
+ EditorTextPlace,
71
+ EditorUnwrapTarget,
67
72
  EditorTextChange,
68
73
  EditorTrackedRange,
69
74
  EditorRunCommand,
@@ -123,7 +123,13 @@ export const KEYMAP = {
123
123
  renameSymbol: bind('Shift+F6'),
124
124
  organizeImports: bind('Ctrl+Alt+O'),
125
125
  formatDocument: bind('Mod+Alt+L'),
126
- wrapWithAbbreviation: bind('Mod+Alt+J'),
126
+ insertLiveTemplate: bind('Mod+J'),
127
+ surroundWith: bind('Mod+Alt+T'),
128
+ surroundWithLiveTemplate: bind('Mod+Alt+J'),
129
+ unwrap: bind('Mod+Shift+Delete'),
130
+ saveAsLiveTemplate: bind(null),
131
+ // Surround with Emmet has no chord of its own: it is a row of Surround with Live Template, which has this one.
132
+ wrapWithAbbreviation: bind(null),
127
133
  selectionToChat: bind(null),
128
134
  inlineEdit: bind(null),
129
135
  suggestInline: bind(null),
@@ -182,6 +188,11 @@ export type OptionalKeymapId =
182
188
  | 'codeBlockStartWithSelection'
183
189
  | 'codeBlockEndWithSelection'
184
190
  | 'wrapWithAbbreviation'
191
+ | 'insertLiveTemplate'
192
+ | 'surroundWith'
193
+ | 'surroundWithLiveTemplate'
194
+ | 'unwrap'
195
+ | 'saveAsLiveTemplate'
185
196
  | 'typeHierarchy';
186
197
 
187
198
  export type Keymap = Readonly<Record<Exclude<KeymapId, OptionalKeymapId>, KeyBinding> & Partial<Record<OptionalKeymapId, KeyBinding>>>;
package/src/smart-keys.ts CHANGED
@@ -9,6 +9,7 @@ export const DEFAULT_SMART_KEYS: EditorSmartKeys = {
9
9
  indentOnPaste: true,
10
10
  smartSemicolon: true,
11
11
  smartArrow: true,
12
+ templateLiterals: true,
12
13
  camelHumps: false
13
14
  };
14
15
 
@@ -0,0 +1,45 @@
1
+ import type { DocumentModel } from '@adecore/editor-core';
2
+ import type { EditorPosition, EditorPostfixOperand, EditorRange, EditorSurroundShape, EditorTextPlace, EditorUnwrapTarget } from './types.ts';
3
+
4
+ /* How an editor turns the offsets of the text model into positions and back. */
5
+ export interface Coordinates {
6
+ positionAt(offset: number): EditorPosition;
7
+ offsetAt(position: EditorPosition): number;
8
+ }
9
+
10
+ function rangeOf(coordinates: Coordinates, from: number, to: number): EditorRange {
11
+ return { start: coordinates.positionAt(from), end: coordinates.positionAt(to) };
12
+ }
13
+
14
+ export function placeAt(model: DocumentModel, coordinates: Coordinates, position: EditorPosition, language: string): EditorTextPlace {
15
+ return model.placeAt(coordinates.offsetAt(position), language);
16
+ }
17
+
18
+ export function postfixOperands(model: DocumentModel, coordinates: Coordinates, dot: EditorPosition, language: string): EditorPostfixOperand[] {
19
+ const at = coordinates.offsetAt(dot);
20
+
21
+ return model.postfixOperands(at, language).map((operand) => ({
22
+ range: rangeOf(coordinates, operand.from, operand.from + model.slice(operand.from, at).trimEnd().length),
23
+ statement: operand.statement
24
+ }));
25
+ }
26
+
27
+ export function surroundShape(model: DocumentModel, coordinates: Coordinates, range: EditorRange, language: string): EditorSurroundShape | null {
28
+ const shape = model.surroundShape(coordinates.offsetAt(range.start), coordinates.offsetAt(range.end), language);
29
+
30
+ return shape === null ? null : { range: rangeOf(coordinates, shape.from, shape.to), statements: shape.statements, expression: shape.expression };
31
+ }
32
+
33
+ export function unwrapTargets(
34
+ model: DocumentModel,
35
+ coordinates: Coordinates,
36
+ position: EditorPosition,
37
+ language: string,
38
+ indentUnit: string
39
+ ): EditorUnwrapTarget[] {
40
+ return model.unwrapTargets(coordinates.offsetAt(position), language, indentUnit).map((target) => ({
41
+ kind: target.kind,
42
+ range: rangeOf(coordinates, target.range.from, target.range.to),
43
+ edits: target.edits.map((edit) => ({ range: rangeOf(coordinates, edit.from, edit.to), text: edit.text }))
44
+ }));
45
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
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
 
5
5
  export type { EditorCommand, FoldRole };
6
6
 
@@ -150,6 +150,8 @@ export interface EditorSmartKeys {
150
150
  readonly smartSemicolon: boolean;
151
151
  /* In PHP a `-` typed after a variable, a member or a `)` or `]` becomes `->`. */
152
152
  readonly smartArrow: boolean;
153
+ /* In JavaScript and TypeScript a `${` typed in a quoted string makes it a template literal. */
154
+ readonly templateLiterals: boolean;
153
155
  /* Moving by word also stops inside `camelCase` and `snake_case` words. Off. */
154
156
  readonly camelHumps: boolean;
155
157
  }
@@ -288,6 +290,37 @@ export interface EditorContentChange {
288
290
  readonly text: string;
289
291
  }
290
292
 
293
+ /* What the text at a position is written in, with embedded regions resolved, and whether it is code, a comment or a string. */
294
+ export interface EditorTextPlace {
295
+ readonly language: string;
296
+ readonly kind: TextPlaceKind;
297
+ }
298
+
299
+ /* An expression that ends right before a dot. */
300
+ export interface EditorPostfixOperand {
301
+ /* The expression, without the whitespace before the dot. */
302
+ readonly range: EditorRange;
303
+ /* Whether it starts a statement; only the widest operand can. */
304
+ readonly statement: boolean;
305
+ }
306
+
307
+ /* What a range can be surrounded with: whole statements, one expression, both or neither. */
308
+ export interface EditorSurroundShape {
309
+ /* The range after trimming whitespace, widened past a trailing line comment for statements. */
310
+ readonly range: EditorRange;
311
+ readonly statements: boolean;
312
+ readonly expression: boolean;
313
+ }
314
+
315
+ /* A construct around a position that can be unwrapped or removed. */
316
+ export interface EditorUnwrapTarget {
317
+ readonly kind: UnwrapKind;
318
+ /* The whole construct, for a preview. */
319
+ readonly range: EditorRange;
320
+ /* Small edits in the coordinates of the text before them, which `applyEdits` takes as they are. */
321
+ readonly edits: readonly EditorContentChange[];
322
+ }
323
+
291
324
  /* What a copy or cut put on the clipboard. */
292
325
  export interface EditorCopy {
293
326
  readonly text: string;
@@ -843,6 +876,14 @@ export interface Editor {
843
876
  setReadOnly(readOnly: boolean, reason?: string): void;
844
877
  /* Whether the editor refuses edits. Optional so an editor from before it still fits. */
845
878
  isReadOnly?(): boolean;
879
+ /* What the text at a position is written in and whether it is code, a comment or a string. Optional so an editor from before it still fits. */
880
+ placeAt?(position: EditorPosition): EditorTextPlace;
881
+ /* Every expression that ends right before the `.` at `dot`, innermost first. Optional so an editor from before it still fits. */
882
+ postfixOperands?(dot: EditorPosition): readonly EditorPostfixOperand[];
883
+ /* What a range can be surrounded with; null for a blank range. Optional so an editor from before it still fits. */
884
+ surroundShape?(range: EditorRange): EditorSurroundShape | null;
885
+ /* What can be unwrapped or removed around a position, innermost first. Optional so an editor from before it still fits. */
886
+ unwrapTargets?(position: EditorPosition): readonly EditorUnwrapTarget[];
846
887
  /* Runs an editing command on every caret, as its key would. False when it changed nothing. */
847
888
  runCommand(command: EditorRunCommand): boolean;
848
889
  focus(): void;