@adecore/editor-react 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.
- package/dist/CallHierarchyPanel.js +177 -181
- package/dist/EditorContextMenu.js +118 -105
- package/dist/LanguagePopups.js +7 -0
- package/dist/PickPopup.js +245 -104
- package/dist/WrapCard.d.ts +8 -0
- package/dist/WrapCard.js +172 -0
- package/dist/call-hierarchy-model.d.ts +4 -2
- package/dist/call-hierarchy-model.js +20 -0
- package/dist/call-hierarchy.d.ts +3 -0
- package/dist/call-hierarchy.js +54 -22
- package/dist/clipboard-history.d.ts +31 -0
- package/dist/clipboard-history.js +70 -0
- package/dist/code-actions.d.ts +3 -1
- package/dist/code-actions.js +50 -10
- package/dist/code-vision.js +2 -3
- package/dist/completion.d.ts +28 -0
- package/dist/completion.js +83 -27
- package/dist/context-menu-rows.d.ts +1 -1
- package/dist/context-menu-rows.js +4 -0
- package/dist/definition-link.js +15 -5
- package/dist/document-colors.d.ts +24 -0
- package/dist/document-colors.js +194 -0
- package/dist/document-links.d.ts +22 -0
- package/dist/document-links.js +133 -0
- package/dist/document-query.d.ts +15 -0
- package/dist/document-query.js +54 -0
- package/dist/edit-locations.d.ts +20 -0
- package/dist/edit-locations.js +136 -0
- package/dist/editor-language.d.ts +23 -1
- package/dist/editor-language.js +83 -2
- package/dist/emmet.d.ts +13 -0
- package/dist/emmet.js +62 -0
- package/dist/folding.js +2 -3
- package/dist/format-on-type.js +3 -5
- package/dist/highlights.js +2 -3
- package/dist/history.d.ts +7 -0
- package/dist/history.js +79 -1
- package/dist/host-types.d.ts +10 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +10 -0
- package/dist/inlay-hints.js +2 -2
- package/dist/inline-completion.d.ts +39 -0
- package/dist/inline-completion.js +221 -0
- package/dist/key-priority.d.ts +9 -5
- package/dist/key-priority.js +11 -6
- package/dist/linked-editing.d.ts +26 -0
- package/dist/linked-editing.js +215 -0
- package/dist/live-templates.d.ts +16 -0
- package/dist/live-templates.js +289 -0
- package/dist/locales/en.json +61 -2
- package/dist/locales/nl.json +61 -2
- package/dist/markup-context.d.ts +55 -0
- package/dist/markup-context.js +408 -0
- package/dist/markup.d.ts +39 -0
- package/dist/markup.js +465 -0
- package/dist/navigation.js +4 -6
- package/dist/paste-history.d.ts +7 -0
- package/dist/paste-history.js +104 -0
- package/dist/peek.js +3 -4
- package/dist/pick.d.ts +21 -2
- package/dist/pick.js +152 -19
- package/dist/popups.d.ts +10 -0
- package/dist/popups.js +2 -1
- package/dist/postfix.d.ts +21 -0
- package/dist/postfix.js +212 -0
- package/dist/project-language.d.ts +20 -1
- package/dist/project-language.js +132 -18
- package/dist/rename.js +8 -11
- package/dist/rendered-docs.js +1 -2
- package/dist/selection-ranges.js +2 -3
- package/dist/shortcut-keys.d.ts +24 -0
- package/dist/signature.js +3 -6
- package/dist/snippet-insert.d.ts +10 -0
- package/dist/snippet-insert.js +217 -0
- package/dist/snippet-session.d.ts +2 -2
- package/dist/snippet-session.js +3 -3
- package/dist/snippet.d.ts +7 -1
- package/dist/snippet.js +65 -8
- package/dist/surround-constructs.d.ts +10 -0
- package/dist/surround-constructs.js +100 -0
- package/dist/surround.d.ts +18 -0
- package/dist/surround.js +399 -0
- package/dist/symbol-picker.js +4 -6
- package/dist/symbols.js +22 -3
- package/dist/template-builtins.d.ts +3 -0
- package/dist/template-builtins.js +46 -0
- package/dist/template-context.d.ts +12 -0
- package/dist/template-context.js +56 -0
- package/dist/templates.d.ts +59 -0
- package/dist/templates.js +103 -0
- package/dist/testing.d.ts +23 -1
- package/dist/testing.js +114 -0
- package/package.json +7 -6
- package/src/CallHierarchyPanel.tsx +26 -11
- package/src/EditorContextMenu.tsx +7 -0
- package/src/LanguagePopups.tsx +4 -0
- package/src/PickPopup.tsx +47 -5
- package/src/WrapCard.tsx +64 -0
- package/src/call-hierarchy-model.ts +25 -3
- package/src/call-hierarchy.ts +62 -20
- package/src/clipboard-history.ts +102 -0
- package/src/code-actions.ts +48 -11
- package/src/code-vision.ts +3 -3
- package/src/completion.ts +123 -25
- package/src/context-menu-rows.ts +3 -2
- package/src/definition-link.ts +17 -5
- package/src/document-colors.ts +189 -0
- package/src/document-links.ts +129 -0
- package/src/document-query.ts +71 -0
- package/src/edit-locations.ts +162 -0
- package/src/editor-language.ts +86 -3
- package/src/emmet-module.d.ts +44 -0
- package/src/emmet.ts +73 -0
- package/src/folding.ts +3 -3
- package/src/format-on-type.ts +5 -5
- package/src/highlights.ts +3 -3
- package/src/history.ts +81 -1
- package/src/host-types.ts +17 -0
- package/src/index.ts +28 -3
- package/src/inlay-hints.ts +2 -2
- package/src/inline-completion.ts +221 -0
- package/src/key-priority.ts +11 -6
- package/src/linked-editing.ts +199 -0
- package/src/live-templates.ts +315 -0
- package/src/locales/en.json +61 -2
- package/src/locales/nl.json +61 -2
- package/src/markup-context.ts +387 -0
- package/src/markup.ts +377 -0
- package/src/navigation.ts +6 -6
- package/src/paste-history.ts +111 -0
- package/src/peek.ts +4 -4
- package/src/pick.ts +218 -20
- package/src/popups.ts +21 -1
- package/src/postfix.ts +224 -0
- package/src/project-language.ts +139 -19
- package/src/rename.ts +11 -11
- package/src/rendered-docs.ts +2 -2
- package/src/selection-ranges.ts +3 -3
- package/src/signature.ts +6 -6
- package/src/snippet-insert.ts +258 -0
- package/src/snippet-session.ts +9 -4
- package/src/snippet.ts +82 -7
- package/src/surround-constructs.ts +85 -0
- package/src/surround.ts +430 -0
- package/src/symbol-picker.ts +6 -6
- package/src/symbols.ts +30 -4
- package/src/template-builtins.ts +94 -0
- package/src/template-context.ts +65 -0
- package/src/templates.ts +197 -0
- package/src/testing.ts +126 -1
package/src/snippet-session.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { acquireSnippetTransforms, type SnippetTransformLease } from './snippet-
|
|
|
5
5
|
import { realTimers, type Timers } from './timers.ts';
|
|
6
6
|
import { KEY_PRIORITY } from './key-priority.ts';
|
|
7
7
|
import { MAX_TRANSFORM_CHARACTERS, type SnippetTransformJob } from './snippet-transform-protocol.ts';
|
|
8
|
-
import type { EditorContentChange, EditorPosition, EditorRange, EditorTextChange } from '@adecore/editor';
|
|
8
|
+
import type { EditorApplyEditsOptions, EditorContentChange, EditorPosition, EditorRange, EditorTextChange } from '@adecore/editor';
|
|
9
9
|
import { comparePositions, endOfInsertion, rangeHolds, sameRange, shiftPosition } from './diagnostics-model.ts';
|
|
10
10
|
import type { EditorLanguage } from './editor-language.ts';
|
|
11
11
|
import { textBeforeOf } from './line-text.ts';
|
|
@@ -367,7 +367,12 @@ export class SnippetFeature {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/* Imports can touch a hull's edge; their affinity must differ from edits inside its active field. */
|
|
370
|
-
applyEdits(
|
|
370
|
+
applyEdits(
|
|
371
|
+
changes: readonly EditorContentChange[],
|
|
372
|
+
token: number,
|
|
373
|
+
extras: readonly EditorContentChange[] = [],
|
|
374
|
+
options?: EditorApplyEditsOptions
|
|
375
|
+
): boolean {
|
|
371
376
|
const frame = this.frames.at(-1);
|
|
372
377
|
|
|
373
378
|
const fits =
|
|
@@ -385,14 +390,14 @@ export class SnippetFeature {
|
|
|
385
390
|
if (token !== this.generation || !fits) {
|
|
386
391
|
this.end();
|
|
387
392
|
|
|
388
|
-
return this.language.editor.applyEdits(changes);
|
|
393
|
+
return this.language.editor.applyEdits(changes, options);
|
|
389
394
|
}
|
|
390
395
|
|
|
391
396
|
this.applying = true;
|
|
392
397
|
this.extras = extras;
|
|
393
398
|
|
|
394
399
|
try {
|
|
395
|
-
return this.language.editor.applyEdits(changes);
|
|
400
|
+
return this.language.editor.applyEdits(changes, options);
|
|
396
401
|
} finally {
|
|
397
402
|
this.applying = false;
|
|
398
403
|
this.extras = [];
|
package/src/snippet.ts
CHANGED
|
@@ -40,15 +40,21 @@ export interface ParsedSnippet {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
type Node =
|
|
43
|
-
|
|
43
|
+
// `indent` marks the value of a variable whose lines after the first take the indentation of the line it starts on.
|
|
44
|
+
| { kind: 'text'; text: string; transform?: SnippetTransform; indent?: true }
|
|
44
45
|
| { kind: 'field'; index: number; body?: Node[]; choices?: string[]; transform?: SnippetTransform }
|
|
45
46
|
| { kind: 'variable'; name: string; body?: Node[]; transform?: SnippetTransform };
|
|
46
47
|
|
|
47
48
|
export type SnippetVariableResolver = (name: string) => string | undefined;
|
|
48
49
|
|
|
50
|
+
export interface SnippetRenderOptions {
|
|
51
|
+
/* The way VS Code treats `TM_SELECTED_TEXT`: the lines of a value after its first take the leading whitespace of the output line the variable starts on. */
|
|
52
|
+
readonly indentVariables?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
49
55
|
export interface SnippetTemplate {
|
|
50
56
|
readonly source: string;
|
|
51
|
-
render(resolve?: SnippetVariableResolver, variableTransforms?: readonly string[]): ParsedSnippet;
|
|
57
|
+
render(resolve?: SnippetVariableResolver, variableTransforms?: readonly string[], options?: SnippetRenderOptions): ParsedSnippet;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
type FieldNode = Extract<Node, { kind: 'field' }>;
|
|
@@ -235,6 +241,13 @@ class SnippetParser {
|
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
/* The lines of a value after its first, behind the whitespace the last line of `before` starts with; blank lines stay blank. */
|
|
245
|
+
function indentLines(value: string, before: string): string {
|
|
246
|
+
const prefix = /^[\t ]*/.exec(before.slice(before.lastIndexOf('\n') + 1))![0];
|
|
247
|
+
|
|
248
|
+
return prefix === '' ? value : value.replace(/(\r\n|\r|\n)(?=[^\r\n])/g, `$1${prefix}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
238
251
|
function renderSnippet(nodes: readonly Node[], variableTransforms?: readonly string[]): ParsedSnippet {
|
|
239
252
|
const definitions = new Map<number, FieldNode>();
|
|
240
253
|
|
|
@@ -304,7 +317,8 @@ function renderSnippet(nodes: readonly Node[], variableTransforms?: readonly str
|
|
|
304
317
|
|
|
305
318
|
if (node.kind === 'text') {
|
|
306
319
|
const start = text.length;
|
|
307
|
-
const
|
|
320
|
+
const given = node.transform === undefined || variableTransforms === undefined ? node.text : variableTransforms[transforms.length];
|
|
321
|
+
const value = node.indent === true && typeof given === 'string' ? indentLines(given, text) : given;
|
|
308
322
|
|
|
309
323
|
if (typeof value !== 'string') {
|
|
310
324
|
throw new RangeError('The variable transform count does not match the snippet.');
|
|
@@ -453,7 +467,7 @@ function variableIndices(nodes: readonly Node[]): ReadonlyMap<string, number> {
|
|
|
453
467
|
);
|
|
454
468
|
}
|
|
455
469
|
|
|
456
|
-
function resolveVariables(nodes: readonly Node[], indices: ReadonlyMap<string, number>, resolve?: SnippetVariableResolver): Node[] {
|
|
470
|
+
function resolveVariables(nodes: readonly Node[], indices: ReadonlyMap<string, number>, resolve?: SnippetVariableResolver, indentVariables = false): Node[] {
|
|
457
471
|
const unknown = new Map<string, { index: number; body: Node[]; defaulted: boolean; definition: Node }>();
|
|
458
472
|
const values = new Map<string, string | undefined>();
|
|
459
473
|
|
|
@@ -513,7 +527,11 @@ function resolveVariables(nodes: readonly Node[], indices: ReadonlyMap<string, n
|
|
|
513
527
|
return [{ kind: 'text', text: value ?? '', transform: node.transform }];
|
|
514
528
|
}
|
|
515
529
|
|
|
516
|
-
|
|
530
|
+
if (value !== undefined && value !== '') {
|
|
531
|
+
return [{ kind: 'text', text: value, ...(indentVariables ? { indent: true as const } : {}) }];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return normalize(node.body ?? [], visiting, depth + 1);
|
|
517
535
|
}
|
|
518
536
|
|
|
519
537
|
if (node.kind === 'field') {
|
|
@@ -565,13 +583,13 @@ export function compileSnippet(source: string): SnippetTemplate {
|
|
|
565
583
|
|
|
566
584
|
return {
|
|
567
585
|
source,
|
|
568
|
-
render: (resolve, variableTransforms) => {
|
|
586
|
+
render: (resolve, variableTransforms, options) => {
|
|
569
587
|
if (nodes === null) {
|
|
570
588
|
return { text: source, stops: [], limited: true };
|
|
571
589
|
}
|
|
572
590
|
|
|
573
591
|
try {
|
|
574
|
-
return renderSnippet(resolveVariables(nodes, indices, resolve), variableTransforms);
|
|
592
|
+
return renderSnippet(resolveVariables(nodes, indices, resolve, options?.indentVariables === true), variableTransforms);
|
|
575
593
|
} catch (error) {
|
|
576
594
|
if (!(error instanceof SnippetLimitError)) {
|
|
577
595
|
throw error;
|
|
@@ -615,3 +633,60 @@ export function tabOrder(parsed: ParsedSnippet): SnippetStop[] {
|
|
|
615
633
|
|
|
616
634
|
return [...visited, first.get(0) ?? { index: 0, start: parsed.text.length, end: parsed.text.length }];
|
|
617
635
|
}
|
|
636
|
+
|
|
637
|
+
/* The names of the variables a snippet uses, the editable ones that are no built-in too. A source that does not parse names none. */
|
|
638
|
+
export function snippetVariables(source: string): ReadonlySet<string> {
|
|
639
|
+
const names = new Set<string>();
|
|
640
|
+
|
|
641
|
+
const scan = (nodes: readonly Node[]): void => {
|
|
642
|
+
for (const node of nodes) {
|
|
643
|
+
if (node.kind === 'variable') {
|
|
644
|
+
names.add(node.name);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (node.kind !== 'text' && node.body !== undefined) {
|
|
648
|
+
scan(node.body);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
if (source.length > MAX_SNIPPET_TEXT) {
|
|
654
|
+
return names;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
try {
|
|
658
|
+
scan(new SnippetParser(source).parse());
|
|
659
|
+
} catch {
|
|
660
|
+
return new Set();
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return names;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/* Why a snippet cannot be used, in English for a host to show, or null: it does not parse, or it is over the limits an insertion keeps. */
|
|
667
|
+
export function checkSnippet(source: string): string | null {
|
|
668
|
+
if (source.length > MAX_SNIPPET_TEXT) {
|
|
669
|
+
return `The text is longer than ${MAX_SNIPPET_TEXT} characters.`;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
new SnippetParser(source).parse();
|
|
674
|
+
} catch (error) {
|
|
675
|
+
if (error instanceof MalformedSnippetError) {
|
|
676
|
+
return 'A transform in the text is not valid.';
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (error instanceof SnippetLimitError) {
|
|
680
|
+
return 'The text has too many parts or is nested too deeply.';
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
throw error;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return compileSnippet(source).render().limited === true ? 'The text expands to more than a snippet can hold.' : null;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* Escapes `\`, `$` and `}`, so the text reads back as itself in a snippet. */
|
|
690
|
+
export function escapeSnippetText(text: string): string {
|
|
691
|
+
return text.replace(/[\\$}]/g, '\\$&');
|
|
692
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { CommentSyntax } from '@adecore/editor';
|
|
2
|
+
import { escapeSnippetText } from './snippet.ts';
|
|
3
|
+
import type { TemplateLanguage } from './templates.ts';
|
|
4
|
+
|
|
5
|
+
/* A row of Surround With that wraps the selection in a snippet. */
|
|
6
|
+
export interface Construct {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
|
|
9
|
+
/* Code, so it is not translated. */
|
|
10
|
+
readonly label: string;
|
|
11
|
+
|
|
12
|
+
/* Snippet syntax; a tab is one indentation unit. */
|
|
13
|
+
readonly body: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SELECTED = '$TM_SELECTED_TEXT';
|
|
17
|
+
|
|
18
|
+
function block(head: string, tail = ''): string {
|
|
19
|
+
return `${head} {\n\t${SELECTED}\n}${tail}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const JS_STATEMENTS: readonly Construct[] = [
|
|
23
|
+
{ id: 'if', label: 'if', body: block('if ($1)') },
|
|
24
|
+
{ id: 'ifElse', label: 'if / else', body: block('if ($1)', ' else {\n\t$2\n}') },
|
|
25
|
+
{ id: 'while', label: 'while', body: block('while ($1)') },
|
|
26
|
+
{ id: 'doWhile', label: 'do / while', body: `do {\n\t${SELECTED}\n} while ($1);` },
|
|
27
|
+
{ id: 'for', label: 'for', body: block('for (let ${1:i} = 0; $1 < $2; $1++)') },
|
|
28
|
+
{ id: 'tryCatch', label: 'try / catch', body: block('try', ' catch (${1:e}) {\n\t$0\n}') },
|
|
29
|
+
{ id: 'tryFinally', label: 'try / finally', body: block('try', ' finally {\n\t$0\n}') },
|
|
30
|
+
{ id: 'tryCatchFinally', label: 'try / catch / finally', body: block('try', ' catch (${1:e}) {\n\t$2\n} finally {\n\t$0\n}') },
|
|
31
|
+
{ id: 'braces', label: '{ }', body: `{\n\t${SELECTED}\n}` },
|
|
32
|
+
{ id: 'function', label: 'function', body: block('function ${1:name}()') }
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const PHP_STATEMENTS: readonly Construct[] = [
|
|
36
|
+
JS_STATEMENTS[0]!,
|
|
37
|
+
JS_STATEMENTS[1]!,
|
|
38
|
+
JS_STATEMENTS[2]!,
|
|
39
|
+
JS_STATEMENTS[3]!,
|
|
40
|
+
{ id: 'for', label: 'for', body: block('for (\\$${1:i} = 0; \\$$1 < $2; \\$$1++)') },
|
|
41
|
+
{ id: 'foreach', label: 'foreach', body: block('foreach (\\$${1:array} as \\$${2:value})') },
|
|
42
|
+
{ id: 'tryCatch', label: 'try / catch', body: block('try', ' catch (${1:\\\\Exception} \\$${2:e}) {\n\t$0\n}') },
|
|
43
|
+
JS_STATEMENTS[6]!,
|
|
44
|
+
JS_STATEMENTS[7]!,
|
|
45
|
+
JS_STATEMENTS[9]!
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const EXPRESSIONS: readonly Construct[] = [
|
|
49
|
+
{ id: 'parentheses', label: '(expr)', body: `(${SELECTED})` },
|
|
50
|
+
{ id: 'not', label: '!(expr)', body: `!(${SELECTED})` }
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
/* The statement surrounders of the first of the families that has any; JavaScript and TypeScript share theirs. */
|
|
54
|
+
export function statementConstructs(families: readonly TemplateLanguage[]): readonly Construct[] {
|
|
55
|
+
if (families.includes('javascript')) {
|
|
56
|
+
return JS_STATEMENTS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return families.includes('php') ? PHP_STATEMENTS : [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* The surrounders of one expression, which JavaScript, TypeScript and PHP share. */
|
|
63
|
+
export function expressionConstructs(families: readonly TemplateLanguage[]): readonly Construct[] {
|
|
64
|
+
return families.includes('javascript') || families.includes('php') ? EXPRESSIONS : [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* The two comment pairs that fold a region, written with the language's own comments: the line comment, or the block
|
|
69
|
+
* pair where there is none. There is no space after the marker, so the output reads `//region Description`.
|
|
70
|
+
*/
|
|
71
|
+
export function regionConstructs(syntax: CommentSyntax): readonly Construct[] {
|
|
72
|
+
const marker = syntax.line ?? syntax.block?.open;
|
|
73
|
+
|
|
74
|
+
if (marker === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const start = escapeSnippetText(marker);
|
|
79
|
+
const end = syntax.line === null ? escapeSnippetText(syntax.block?.close ?? '') : '';
|
|
80
|
+
|
|
81
|
+
return [
|
|
82
|
+
{ id: 'region', label: 'region...endregion', body: `${start}region \${1:Description}${end}\n${SELECTED}\n${start}endregion${end}` },
|
|
83
|
+
{ id: 'editorFold', label: '<editor-fold...>', body: `${start}<editor-fold desc="\${1:Description}">${end}\n${SELECTED}\n${start}</editor-fold>${end}` }
|
|
84
|
+
];
|
|
85
|
+
}
|