@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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BUILT_IN_LIVE_TEMPLATES, BUILT_IN_POSTFIX_TEMPLATES } from './template-builtins.js';
|
|
2
|
+
import { checkSnippet } from './snippet.js';
|
|
3
|
+
export { BUILT_IN_LIVE_TEMPLATES, BUILT_IN_POSTFIX_TEMPLATES };
|
|
4
|
+
export {} from './live-templates.js';
|
|
5
|
+
const MAX_KEY_LENGTH = 40;
|
|
6
|
+
const MAX_BODY_LENGTH = 20_000;
|
|
7
|
+
const POSTFIX_LANGUAGES = ['javascript', 'typescript', 'php'];
|
|
8
|
+
const LANGUAGES = ['javascript', 'typescript', 'php', 'css', 'html', 'xml', 'other'];
|
|
9
|
+
const PLACES = ['code', 'comment', 'string'];
|
|
10
|
+
/*
|
|
11
|
+
* The templates the editors of a project know: the built-in ones and the ones a host sets. A host that keeps
|
|
12
|
+
* templates feeds them through `set`, and the editors read the registry on every use.
|
|
13
|
+
*/
|
|
14
|
+
export class TemplateRegistry {
|
|
15
|
+
builtins;
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
own = {
|
|
18
|
+
live: [],
|
|
19
|
+
postfix: []
|
|
20
|
+
};
|
|
21
|
+
off = new Set();
|
|
22
|
+
current = {
|
|
23
|
+
liveInCompletion: true,
|
|
24
|
+
postfix: true,
|
|
25
|
+
postfixInCompletion: true
|
|
26
|
+
};
|
|
27
|
+
liveList;
|
|
28
|
+
postfixList;
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
this.builtins = options.builtins !== false;
|
|
31
|
+
this.liveList = this.listed(BUILT_IN_LIVE_TEMPLATES, []);
|
|
32
|
+
this.postfixList = this.listed(BUILT_IN_POSTFIX_TEMPLATES, []);
|
|
33
|
+
}
|
|
34
|
+
/* The built-in templates that are not disabled, then the host's. */
|
|
35
|
+
get live() {
|
|
36
|
+
return this.liveList;
|
|
37
|
+
}
|
|
38
|
+
get postfix() {
|
|
39
|
+
return this.postfixList;
|
|
40
|
+
}
|
|
41
|
+
get settings() {
|
|
42
|
+
return this.current;
|
|
43
|
+
}
|
|
44
|
+
/* Replaces what the update names. A template that fails `checkTemplate` is dropped; this never throws. */
|
|
45
|
+
set(update) {
|
|
46
|
+
this.own = {
|
|
47
|
+
live: update.live === undefined ? this.own.live : update.live.filter(template => checkTemplate(template, 'live') === null),
|
|
48
|
+
postfix: update.postfix === undefined ? this.own.postfix : update.postfix.filter(template => checkTemplate(template, 'postfix') === null)
|
|
49
|
+
};
|
|
50
|
+
this.off = update.disabled === undefined ? this.off : new Set(update.disabled);
|
|
51
|
+
if (update.settings !== undefined) {
|
|
52
|
+
this.current = {
|
|
53
|
+
liveInCompletion: update.settings.liveInCompletion !== false,
|
|
54
|
+
postfix: update.settings.postfix !== false,
|
|
55
|
+
postfixInCompletion: update.settings.postfixInCompletion !== false
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
this.liveList = this.listed(BUILT_IN_LIVE_TEMPLATES, this.own.live);
|
|
59
|
+
this.postfixList = this.listed(BUILT_IN_POSTFIX_TEMPLATES, this.own.postfix);
|
|
60
|
+
for (const listener of [...this.listeners]) {
|
|
61
|
+
listener();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/* Calls `listener` after every `set`. Returns the function that stops it. */
|
|
65
|
+
onChange(listener) {
|
|
66
|
+
this.listeners.add(listener);
|
|
67
|
+
return () => {
|
|
68
|
+
this.listeners.delete(listener);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
listed(builtins, own) {
|
|
72
|
+
return [...(this.builtins ? builtins.filter(template => !this.off.has(template.id)) : []), ...own];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/*
|
|
76
|
+
* Why a template cannot be used, in English for a host to show, or null. `kind` says which rules apply; without it a
|
|
77
|
+
* template with an `expression` or an `example` counts as a postfix one.
|
|
78
|
+
*/
|
|
79
|
+
export function checkTemplate(template, kind) {
|
|
80
|
+
const postfix = kind === undefined ? 'expression' in template || 'example' in template : kind === 'postfix';
|
|
81
|
+
if (template.id === '') {
|
|
82
|
+
return 'The template has no id.';
|
|
83
|
+
}
|
|
84
|
+
if (template.key === '' || template.key.length > MAX_KEY_LENGTH) {
|
|
85
|
+
return `The key is one to ${MAX_KEY_LENGTH} characters.`;
|
|
86
|
+
}
|
|
87
|
+
if (postfix ? !/^[\p{L}\p{N}_$]+$/u.test(template.key) : /\s/.test(template.key)) {
|
|
88
|
+
return postfix ? 'The key holds letters, digits, underscores and dollar signs only.' : 'The key holds no whitespace.';
|
|
89
|
+
}
|
|
90
|
+
if (template.body.trim() === '') {
|
|
91
|
+
return 'The text is empty.';
|
|
92
|
+
}
|
|
93
|
+
if (template.body.length > MAX_BODY_LENGTH) {
|
|
94
|
+
return `The text is longer than ${MAX_BODY_LENGTH} characters.`;
|
|
95
|
+
}
|
|
96
|
+
if (template.languages.length === 0 || template.languages.some(language => !(postfix ? POSTFIX_LANGUAGES : LANGUAGES).includes(language))) {
|
|
97
|
+
return 'The languages are empty or not known.';
|
|
98
|
+
}
|
|
99
|
+
if (!postfix && template.places?.some(place => !PLACES.includes(place)) === true) {
|
|
100
|
+
return 'The places are not known.';
|
|
101
|
+
}
|
|
102
|
+
return checkSnippet(template.body);
|
|
103
|
+
}
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, type CallHierarchyIncomingCall, type CallHierarchyItem, type CallHierarchyOutgoingCall, type CodeAction, type CodeActionContext, type CodeLens, type Command, type CompletionContext, type CompletionItem, type CompletionResult, type DocumentHighlight, type DocumentSymbolResult, type FoldingRange, type FormattingOptions, type Hover, type InlayHint, type Location, type NavigationResult, type Position, type PrepareRenameResult, type Range, type SelectionRange, type SemanticTokens, type SemanticTokensDelta, type SignatureHelp, type SignatureHelpContext, type TextEdit, type WorkspaceEdit, type WorkspaceSymbolResult } from '@adecore/lsp';
|
|
1
|
+
import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, type CallHierarchyIncomingCall, type CallHierarchyItem, type CallHierarchyOutgoingCall, type CodeAction, type CodeActionContext, type CodeLens, type Command, type CompletionContext, type CompletionItem, type CompletionResult, type DocumentHighlight, type DocumentSymbolResult, type FoldingRange, type FormattingOptions, type Hover, type InlayHint, type Location, type NavigationResult, type Position, type PrepareRenameResult, type Range, type SelectionRange, type SemanticTokens, type SemanticTokensDelta, type SignatureHelp, type SignatureHelpContext, type TextEdit, type WorkspaceEdit, type WorkspaceSymbolResult, type Color, type ColorInformation, type ColorPresentation, type DocumentLink, type InlineCompletionContext, type InlineCompletionResult, type InlineValue, type InlineValueContext, type LinkedEditingRanges, type Moniker, type PreviousResultId, type ServerMessage, type ShowDocumentParams, type ShowDocumentResult, type TextDocumentSaveReason, type TypeHierarchyItem, type WorkspaceDiagnosticReport } from '@adecore/lsp';
|
|
2
2
|
export { ManualTimers } from './timers.ts';
|
|
3
3
|
export interface LanguageCall {
|
|
4
4
|
method: string;
|
|
@@ -15,6 +15,8 @@ export declare class FakeLanguageService implements LanguageService {
|
|
|
15
15
|
private readonly answers;
|
|
16
16
|
private readonly diagnostics;
|
|
17
17
|
private readonly changes;
|
|
18
|
+
private readonly messages;
|
|
19
|
+
private showDocument;
|
|
18
20
|
respond(method: string, answer: (call: LanguageCall) => unknown, provider?: ProviderOptions): void;
|
|
19
21
|
openDocument(document: LanguageDocument): Promise<void>;
|
|
20
22
|
changeDocument(uri: string, changes: readonly ContentChange[]): Promise<void>;
|
|
@@ -58,4 +60,24 @@ export declare class FakeLanguageService implements LanguageService {
|
|
|
58
60
|
outgoingCalls(uri: string, item: CallHierarchyItem, options?: LanguageRequestOptions): Promise<CallHierarchyOutgoingCall[] | null>;
|
|
59
61
|
codeLenses(uri: string, options?: LanguageRequestOptions): Promise<CodeLens[] | null>;
|
|
60
62
|
resolveCodeLens(uri: string, lens: CodeLens, options?: LanguageRequestOptions): Promise<CodeLens>;
|
|
63
|
+
documentLinks(uri: string, options?: LanguageRequestOptions): Promise<DocumentLink[] | null>;
|
|
64
|
+
resolveDocumentLink(uri: string, link: DocumentLink, options?: LanguageRequestOptions): Promise<DocumentLink>;
|
|
65
|
+
documentColors(uri: string, options?: LanguageRequestOptions): Promise<ColorInformation[] | null>;
|
|
66
|
+
colorPresentations(uri: string, color: Color, range: Range, options?: LanguageRequestOptions): Promise<ColorPresentation[] | null>;
|
|
67
|
+
linkedEditingRanges(uri: string, position: Position, options?: LanguageRequestOptions): Promise<LinkedEditingRanges | null>;
|
|
68
|
+
monikers(uri: string, position: Position, options?: LanguageRequestOptions): Promise<Moniker[] | null>;
|
|
69
|
+
prepareTypeHierarchy(uri: string, position: Position, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
|
|
70
|
+
supertypes(uri: string, item: TypeHierarchyItem, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
|
|
71
|
+
subtypes(uri: string, item: TypeHierarchyItem, options?: LanguageRequestOptions): Promise<TypeHierarchyItem[] | null>;
|
|
72
|
+
inlineValues(uri: string, range: Range, context: InlineValueContext, options?: LanguageRequestOptions): Promise<InlineValue[] | null>;
|
|
73
|
+
inlineCompletions(uri: string, position: Position, context: InlineCompletionContext, options?: LanguageRequestOptions): Promise<InlineCompletionResult>;
|
|
74
|
+
rangesFormatting(uri: string, ranges: readonly Range[], formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
|
|
75
|
+
willSave(uri: string, reason?: TextDocumentSaveReason): Promise<void>;
|
|
76
|
+
willSaveWaitUntil(uri: string, reason?: TextDocumentSaveReason, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
|
|
77
|
+
workspaceDiagnostics(uri: string, previousResultIds?: readonly PreviousResultId[], options?: LanguageRequestOptions): Promise<WorkspaceDiagnosticReport>;
|
|
78
|
+
textDocumentContent(uri: string, options?: LanguageRequestOptions): Promise<string | null>;
|
|
79
|
+
onServerMessage(listener: (message: ServerMessage) => void): Disposable;
|
|
80
|
+
onShowDocument(handler: (params: ShowDocumentParams) => Promise<ShowDocumentResult>): Disposable;
|
|
81
|
+
message(message: ServerMessage): void;
|
|
82
|
+
show(params: ShowDocumentParams): Promise<boolean>;
|
|
61
83
|
}
|
package/dist/testing.js
CHANGED
|
@@ -7,6 +7,8 @@ export class FakeLanguageService {
|
|
|
7
7
|
answers = new Map();
|
|
8
8
|
diagnostics = new Set();
|
|
9
9
|
changes = new Set();
|
|
10
|
+
messages = new Set();
|
|
11
|
+
showDocument = null;
|
|
10
12
|
respond(method, answer, provider = {}) {
|
|
11
13
|
this.answers.set(method, answer);
|
|
12
14
|
this.providers.set(method, provider);
|
|
@@ -248,4 +250,116 @@ export class FakeLanguageService {
|
|
|
248
250
|
resolveCodeLens(uri, lens, options = {}) {
|
|
249
251
|
return this.request('codeLens/resolve', uri, lens, options);
|
|
250
252
|
}
|
|
253
|
+
documentLinks(uri, options = {}) {
|
|
254
|
+
return this.request('textDocument/documentLink', uri, {}, options);
|
|
255
|
+
}
|
|
256
|
+
resolveDocumentLink(uri, link, options = {}) {
|
|
257
|
+
return this.request('documentLink/resolve', uri, link, options);
|
|
258
|
+
}
|
|
259
|
+
documentColors(uri, options = {}) {
|
|
260
|
+
return this.request('textDocument/documentColor', uri, {}, options);
|
|
261
|
+
}
|
|
262
|
+
colorPresentations(uri, color, range, options = {}) {
|
|
263
|
+
return this.request('textDocument/colorPresentation', uri, {
|
|
264
|
+
color,
|
|
265
|
+
range
|
|
266
|
+
}, options);
|
|
267
|
+
}
|
|
268
|
+
linkedEditingRanges(uri, position, options = {}) {
|
|
269
|
+
return this.request('textDocument/linkedEditingRange', uri, {
|
|
270
|
+
position
|
|
271
|
+
}, options);
|
|
272
|
+
}
|
|
273
|
+
monikers(uri, position, options = {}) {
|
|
274
|
+
return this.request('textDocument/moniker', uri, {
|
|
275
|
+
position
|
|
276
|
+
}, options);
|
|
277
|
+
}
|
|
278
|
+
prepareTypeHierarchy(uri, position, options = {}) {
|
|
279
|
+
return this.request('textDocument/prepareTypeHierarchy', uri, {
|
|
280
|
+
position
|
|
281
|
+
}, options);
|
|
282
|
+
}
|
|
283
|
+
supertypes(uri, item, options = {}) {
|
|
284
|
+
return this.request('typeHierarchy/supertypes', uri, {
|
|
285
|
+
item
|
|
286
|
+
}, options);
|
|
287
|
+
}
|
|
288
|
+
subtypes(uri, item, options = {}) {
|
|
289
|
+
return this.request('typeHierarchy/subtypes', uri, {
|
|
290
|
+
item
|
|
291
|
+
}, options);
|
|
292
|
+
}
|
|
293
|
+
inlineValues(uri, range, context, options = {}) {
|
|
294
|
+
return this.request('textDocument/inlineValue', uri, {
|
|
295
|
+
range,
|
|
296
|
+
context
|
|
297
|
+
}, options);
|
|
298
|
+
}
|
|
299
|
+
inlineCompletions(uri, position, context, options = {}) {
|
|
300
|
+
return this.request('textDocument/inlineCompletion', uri, {
|
|
301
|
+
position,
|
|
302
|
+
context
|
|
303
|
+
}, options);
|
|
304
|
+
}
|
|
305
|
+
rangesFormatting(uri, ranges, formatting, options = {}) {
|
|
306
|
+
return this.request('textDocument/rangesFormatting', uri, {
|
|
307
|
+
ranges,
|
|
308
|
+
options: formatting
|
|
309
|
+
}, options);
|
|
310
|
+
}
|
|
311
|
+
async willSave(uri, reason = 1) {
|
|
312
|
+
this.calls.push({
|
|
313
|
+
method: 'textDocument/willSave',
|
|
314
|
+
uri,
|
|
315
|
+
params: {
|
|
316
|
+
reason
|
|
317
|
+
},
|
|
318
|
+
options: {}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async willSaveWaitUntil(uri, reason = 1, options = {}) {
|
|
322
|
+
return this.supports('textDocument/willSaveWaitUntil', uri) ? this.request('textDocument/willSaveWaitUntil', uri, {
|
|
323
|
+
reason
|
|
324
|
+
}, options) : null;
|
|
325
|
+
}
|
|
326
|
+
workspaceDiagnostics(uri, previousResultIds = [], options = {}) {
|
|
327
|
+
return this.request('workspace/diagnostic', uri, {
|
|
328
|
+
previousResultIds
|
|
329
|
+
}, options);
|
|
330
|
+
}
|
|
331
|
+
async textDocumentContent(uri, options = {}) {
|
|
332
|
+
if (!this.supports('workspace/textDocumentContent', uri)) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
return (await this.request('workspace/textDocumentContent', uri, {
|
|
336
|
+
uri
|
|
337
|
+
}, options))?.text ?? null;
|
|
338
|
+
}
|
|
339
|
+
onServerMessage(listener) {
|
|
340
|
+
this.messages.add(listener);
|
|
341
|
+
return {
|
|
342
|
+
dispose: () => this.messages.delete(listener)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
onShowDocument(handler) {
|
|
346
|
+
this.showDocument = handler;
|
|
347
|
+
return {
|
|
348
|
+
dispose: () => {
|
|
349
|
+
if (this.showDocument === handler) {
|
|
350
|
+
this.showDocument = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/* Sends a message as a server would. */
|
|
356
|
+
message(message) {
|
|
357
|
+
for (const listener of this.messages) {
|
|
358
|
+
listener(message);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/* Asks the host to show a document as a server would; false when nothing handles it. */
|
|
362
|
+
async show(params) {
|
|
363
|
+
return (await this.showDocument?.(params))?.success ?? false;
|
|
364
|
+
}
|
|
251
365
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/editor-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Language-enabled editors, feature coordination and React popups over injected language services.",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -62,9 +62,10 @@
|
|
|
62
62
|
"test": "bun test --conditions=source"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@adecore/editor": "0.
|
|
66
|
-
"@adecore/lsp": "0.
|
|
67
|
-
"@adecore/merge": "0.
|
|
65
|
+
"@adecore/editor": "0.44.0",
|
|
66
|
+
"@adecore/lsp": "0.44.0",
|
|
67
|
+
"@adecore/merge": "0.44.0",
|
|
68
|
+
"emmet": "^2.4.11",
|
|
68
69
|
"lucide-react": "^1.61.0",
|
|
69
70
|
"react-markdown": "^10.1.0",
|
|
70
71
|
"remark-breaks": "^4.0.0",
|
|
@@ -72,14 +73,14 @@
|
|
|
72
73
|
"zustand": "^5.0.16"
|
|
73
74
|
},
|
|
74
75
|
"peerDependencies": {
|
|
75
|
-
"@adecore/ui": "^0.
|
|
76
|
+
"@adecore/ui": "^0.44.0",
|
|
76
77
|
"i18next": "^26.4.2",
|
|
77
78
|
"react": "^19.3.0",
|
|
78
79
|
"react-dom": "^19.3.0",
|
|
79
80
|
"react-i18next": "^17.0.15"
|
|
80
81
|
},
|
|
81
82
|
"devDependencies": {
|
|
82
|
-
"@adecore/ui": "0.
|
|
83
|
+
"@adecore/ui": "0.44.0",
|
|
83
84
|
"@types/bun": "^1.4.4",
|
|
84
85
|
"@types/react": "^19.3.0",
|
|
85
86
|
"@types/react-dom": "^19.3.0",
|
|
@@ -9,7 +9,7 @@ import { CALL_HIERARCHY_HEIGHT } from './call-hierarchy.ts';
|
|
|
9
9
|
import { PanelResizeHandle } from './PanelResizeHandle.tsx';
|
|
10
10
|
import { usePanelBody } from './use-panel-body.ts';
|
|
11
11
|
import { PANEL_BODY_HEIGHT } from './panel-size.ts';
|
|
12
|
-
import { CALL_LEVEL_LIMIT, type CallDirection, type CallRow } from './call-hierarchy-model.ts';
|
|
12
|
+
import { CALL_LEVEL_LIMIT, isTypeDirection, type CallDirection, type CallRow } from './call-hierarchy-model.ts';
|
|
13
13
|
import type { EditorLanguage } from './editor-language.ts';
|
|
14
14
|
import { basenameOf, relativeTo } from './paths.ts';
|
|
15
15
|
import { PathText } from './PathText.tsx';
|
|
@@ -125,7 +125,12 @@ function Tree({ language, view }: { language: EditorLanguage; view: CallHierarch
|
|
|
125
125
|
useInputPopup(language, treeId, activeId);
|
|
126
126
|
|
|
127
127
|
return (
|
|
128
|
-
<div
|
|
128
|
+
<div
|
|
129
|
+
id={treeId}
|
|
130
|
+
role="tree"
|
|
131
|
+
aria-label={t(isTypeDirection(view.direction) ? 'language.callHierarchy.typesLabel' : 'language.callHierarchy.label')}
|
|
132
|
+
className="w-1/2 shrink-0 overflow-y-auto border-r border-border py-1"
|
|
133
|
+
>
|
|
129
134
|
{view.rows.map((row) => {
|
|
130
135
|
const active = row.id === view.active;
|
|
131
136
|
const ref = active ? activeRow : undefined;
|
|
@@ -174,11 +179,14 @@ function StatusText({ row, direction }: { row: Extract<CallRow, { kind: 'status'
|
|
|
174
179
|
);
|
|
175
180
|
}
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
+
const empty = {
|
|
183
|
+
incoming: 'noIncoming',
|
|
184
|
+
outgoing: 'noOutgoing',
|
|
185
|
+
supertypes: 'noSupertypes',
|
|
186
|
+
subtypes: 'noSubtypes'
|
|
187
|
+
} as const;
|
|
188
|
+
|
|
189
|
+
return <span className="font-sans text-xs text-text-muted">{t(`language.callHierarchy.${empty[direction]}`)}</span>;
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
/*
|
|
@@ -210,10 +218,17 @@ export function CallHierarchyPanel({ language, view }: { language: EditorLanguag
|
|
|
210
218
|
value={view.direction}
|
|
211
219
|
onValueChange={(direction) => language.callHierarchy.setDirection(direction)}
|
|
212
220
|
label={t('language.callHierarchy.direction')}
|
|
213
|
-
options={
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
221
|
+
options={
|
|
222
|
+
isTypeDirection(view.direction)
|
|
223
|
+
? [
|
|
224
|
+
{ id: 'supertypes', label: t('language.callHierarchy.supertypes') },
|
|
225
|
+
{ id: 'subtypes', label: t('language.callHierarchy.subtypes') }
|
|
226
|
+
]
|
|
227
|
+
: [
|
|
228
|
+
{ id: 'incoming', label: t('language.callHierarchy.incoming') },
|
|
229
|
+
{ id: 'outgoing', label: t('language.callHierarchy.outgoing') }
|
|
230
|
+
]
|
|
231
|
+
}
|
|
217
232
|
/>
|
|
218
233
|
<span className="shrink-0 font-medium">{view.name}</span>
|
|
219
234
|
<span className="shrink-0 font-medium text-text-muted">{path === null ? '' : basenameOf(path)}</span>
|
|
@@ -28,6 +28,7 @@ export function EditorContextMenu({ language, view, children }: { language: Edit
|
|
|
28
28
|
implementation: () => void language.navigation.go('implementation'),
|
|
29
29
|
peekReferences: () => void language.peek.open(),
|
|
30
30
|
callHierarchy: () => void language.callHierarchy.open(),
|
|
31
|
+
typeHierarchy: () => void language.callHierarchy.open(undefined, 'supertypes'),
|
|
31
32
|
renameSymbol: () => void language.rename.start(),
|
|
32
33
|
codeActions: () => void language.codeActions.open()
|
|
33
34
|
};
|
|
@@ -100,6 +101,12 @@ export function EditorContextMenu({ language, view, children }: { language: Edit
|
|
|
100
101
|
{t('commands.formatDocument')}
|
|
101
102
|
{language.shortcuts.formatDocument !== null && <Kbd shortcut={language.shortcuts.formatDocument} />}
|
|
102
103
|
</ContextMenu.Item>
|
|
104
|
+
{language.codeActions.formatsSelection &&
|
|
105
|
+
language.editor
|
|
106
|
+
.getSelections()
|
|
107
|
+
.some((range) => range.start.line !== range.end.line || range.start.character !== range.end.character) && (
|
|
108
|
+
<ContextMenu.Item onClick={() => void language.codeActions.formatSelection()}>{t('commands.formatSelection')}</ContextMenu.Item>
|
|
109
|
+
)}
|
|
103
110
|
</>
|
|
104
111
|
)}
|
|
105
112
|
</ContextMenu.Popup>
|
package/src/LanguagePopups.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import { RenameCard } from './RenameCard.tsx';
|
|
|
13
13
|
import { RenderedDocs } from './RenderedDocs.tsx';
|
|
14
14
|
import { SymbolPicker } from './SymbolPicker.tsx';
|
|
15
15
|
import { SignatureCard } from './SignatureCard.tsx';
|
|
16
|
+
import { WrapCard } from './WrapCard.tsx';
|
|
16
17
|
import type { HoverView } from './popups.ts';
|
|
17
18
|
|
|
18
19
|
export interface LanguagePopupsProps<Language extends EditorLanguage = EditorLanguage> {
|
|
@@ -44,6 +45,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
|
|
|
44
45
|
const symbols = useStore(language.popups, (state) => state.symbols);
|
|
45
46
|
const menu = useStore(language.popups, (state) => state.menu);
|
|
46
47
|
const authors = useStore(language.popups, (state) => state.authors);
|
|
48
|
+
const wrap = useStore(language.popups, (state) => state.wrap);
|
|
47
49
|
|
|
48
50
|
useEffect(() => language.editor.onViewChange(redraw), [language]);
|
|
49
51
|
|
|
@@ -53,6 +55,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
|
|
|
53
55
|
const pickRect = pick === null ? null : language.editor.rectAt(pick.anchor);
|
|
54
56
|
const renameRect = rename === null ? null : language.editor.rectAt(rename.range.start);
|
|
55
57
|
const renameEnd = rename === null ? null : language.editor.rectAt(rename.range.end);
|
|
58
|
+
const wrapRect = wrap === null ? null : language.editor.rectAt(wrap.anchor);
|
|
56
59
|
|
|
57
60
|
return (
|
|
58
61
|
<>
|
|
@@ -85,6 +88,7 @@ export function LanguagePopups<Language extends EditorLanguage>({ language, cont
|
|
|
85
88
|
{peek !== null && <PeekPanel language={language} view={peek} />}
|
|
86
89
|
{callHierarchy !== null && <CallHierarchyPanel language={language} view={callHierarchy} />}
|
|
87
90
|
{authors !== null && <CodeAuthorsCard language={language} view={authors} />}
|
|
91
|
+
{wrap !== null && wrapRect !== null && <WrapCard language={language} view={wrap} rect={wrapRect} />}
|
|
88
92
|
{children}
|
|
89
93
|
{rename !== null && renameRect !== null && <RenameCard language={language} view={rename} rect={renameRect} endRect={renameEnd} />}
|
|
90
94
|
</>
|
package/src/PickPopup.tsx
CHANGED
|
@@ -10,7 +10,34 @@ import type { PickPreview, PickView } from './popups.ts';
|
|
|
10
10
|
const CARD =
|
|
11
11
|
'fixed top-0 left-0 z-(--z-popup) flex w-[420px] flex-col overflow-hidden rounded-lg border border-border bg-surface-raised text-text shadow-(--float-shadow)';
|
|
12
12
|
|
|
13
|
+
/* The label with the part a query matched marked, whatever the case. */
|
|
14
|
+
function MatchedLabel({ label, query }: { label: string; query: string | null }) {
|
|
15
|
+
const start = query === null || query === '' ? -1 : label.toLowerCase().indexOf(query.toLowerCase());
|
|
16
|
+
|
|
17
|
+
if (query === null || start < 0) {
|
|
18
|
+
return label;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
{label.slice(0, start)}
|
|
24
|
+
<mark className="bg-transparent font-semibold text-accent">{label.slice(start, start + query.length)}</mark>
|
|
25
|
+
{label.slice(start + query.length)}
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
function PreviewLines({ preview }: { preview: PickPreview }) {
|
|
31
|
+
if (preview.kind === 'text') {
|
|
32
|
+
return (
|
|
33
|
+
<div className="max-h-48 overflow-auto border-t border-border p-2 font-mono text-code whitespace-pre">
|
|
34
|
+
{preview.added.map((line, index) => (
|
|
35
|
+
<div key={index}>{line === '' ? ' ' : line}</div>
|
|
36
|
+
))}
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
14
41
|
return (
|
|
15
42
|
<div className="flex flex-col gap-px border-t border-border p-1 font-mono text-code">
|
|
16
43
|
{preview.removed.map((line, index) => (
|
|
@@ -78,6 +105,7 @@ export function PickPopup({ language, view, rect }: { language: EditorLanguage;
|
|
|
78
105
|
}
|
|
79
106
|
|
|
80
107
|
const activeId = optionIds.get(view.active);
|
|
108
|
+
const mnemonics = view.groups.some((group) => group.rows.some((row) => row.mnemonic !== undefined));
|
|
81
109
|
|
|
82
110
|
useInputPopup(language, listId, activeId);
|
|
83
111
|
|
|
@@ -89,7 +117,12 @@ export function PickPopup({ language, view, rect }: { language: EditorLanguage;
|
|
|
89
117
|
onPointerDown={(event) => event.preventDefault()}
|
|
90
118
|
{...keepFocusOutOfList(() => language.editor.focus())}
|
|
91
119
|
>
|
|
92
|
-
{view.title !== null
|
|
120
|
+
{(view.title !== null || view.query !== null) && (
|
|
121
|
+
<div className="flex items-baseline gap-2 border-b border-border px-3 py-1.5 text-xs text-text-muted">
|
|
122
|
+
<span className="min-w-0 truncate">{view.title}</span>
|
|
123
|
+
{view.query !== null && view.query !== '' && <span className="ml-auto shrink-0 font-mono text-text">{view.query}</span>}
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
93
126
|
<div id={listId} role="listbox" className="min-h-0 flex-1 overflow-y-auto p-1">
|
|
94
127
|
{view.groups.map((group, groupIndex) => (
|
|
95
128
|
<div key={group.title ?? groupIndex} role="group" className="flex flex-col">
|
|
@@ -102,12 +135,21 @@ export function PickPopup({ language, view, rect }: { language: EditorLanguage;
|
|
|
102
135
|
type="button"
|
|
103
136
|
role="option"
|
|
104
137
|
tabIndex={-1}
|
|
105
|
-
aria-selected={row.id === view.active}
|
|
138
|
+
aria-selected={row.id === view.active || view.chosen.includes(row.id)}
|
|
139
|
+
aria-keyshortcuts={row.mnemonic}
|
|
106
140
|
data-active={row.id === view.active}
|
|
107
|
-
|
|
108
|
-
|
|
141
|
+
data-chosen={view.chosen.includes(row.id)}
|
|
142
|
+
className="flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs cursor-row data-[chosen=true]:bg-accent/15"
|
|
143
|
+
onClick={(event) => language.pick.choose(row.id, { toggle: event.metaKey || event.ctrlKey, range: event.shiftKey })}
|
|
109
144
|
>
|
|
110
|
-
|
|
145
|
+
{mnemonics && (
|
|
146
|
+
<span aria-hidden="true" className="w-3 shrink-0 text-center font-mono text-text-faint">
|
|
147
|
+
{row.mnemonic}
|
|
148
|
+
</span>
|
|
149
|
+
)}
|
|
150
|
+
<span className={row.path ? 'shrink-0' : 'min-w-0 truncate'}>
|
|
151
|
+
<MatchedLabel label={row.label} query={view.query} />
|
|
152
|
+
</span>
|
|
111
153
|
{row.path ? (
|
|
112
154
|
<PathText path={row.detail} className="ml-auto font-mono text-text-faint" />
|
|
113
155
|
) : (
|
package/src/WrapCard.tsx
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef, useState, type KeyboardEvent } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import type { EditorRect } from '@adecore/editor';
|
|
4
|
+
import { Input } from '@adecore/ui';
|
|
5
|
+
import { AnchoredPopup } from './AnchoredPopup.tsx';
|
|
6
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
7
|
+
import type { WrapView } from './popups.ts';
|
|
8
|
+
|
|
9
|
+
/* The field for the abbreviation a selection is wrapped in: Enter wraps, Escape or leaving the field cancels. */
|
|
10
|
+
export function WrapCard({ language, view, rect }: { language: EditorLanguage; view: WrapView; rect: EditorRect }) {
|
|
11
|
+
const { t } = useTranslation('editor');
|
|
12
|
+
const input = useRef<HTMLInputElement>(null);
|
|
13
|
+
const [value, setValue] = useState('');
|
|
14
|
+
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
input.current?.focus();
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
function onKeyDown(event: KeyboardEvent<HTMLInputElement>): void {
|
|
20
|
+
if (event.nativeEvent.isComposing) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (event.key === 'Escape') {
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
event.stopPropagation();
|
|
26
|
+
language.markup.cancelWrap();
|
|
27
|
+
language.editor.focus();
|
|
28
|
+
} else if (event.key === 'Enter') {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
event.stopPropagation();
|
|
31
|
+
if (value.trim() !== '') {
|
|
32
|
+
language.markup.submitWrap(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<AnchoredPopup rect={rect} placement={{ prefer: 'above' }}>
|
|
39
|
+
<div className="flex w-[280px] flex-col gap-1.5 p-2">
|
|
40
|
+
<Input
|
|
41
|
+
ref={input}
|
|
42
|
+
size="sm"
|
|
43
|
+
mono
|
|
44
|
+
value={value}
|
|
45
|
+
spellCheck={false}
|
|
46
|
+
autoComplete="off"
|
|
47
|
+
aria-label={t('language.markup.wrap')}
|
|
48
|
+
aria-invalid={view.error !== null}
|
|
49
|
+
placeholder={t('language.markup.placeholder')}
|
|
50
|
+
onChange={(event) => setValue(event.target.value)}
|
|
51
|
+
onKeyDown={onKeyDown}
|
|
52
|
+
onBlur={() => language.markup.cancelWrap()}
|
|
53
|
+
/>
|
|
54
|
+
{view.error !== null ? (
|
|
55
|
+
<div role="alert" className="text-xs text-status-error">
|
|
56
|
+
{view.error}
|
|
57
|
+
</div>
|
|
58
|
+
) : (
|
|
59
|
+
<div className="text-xs text-text-muted">↵ {t('language.markup.apply')}</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</AnchoredPopup>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, Range } from '@adecore/lsp';
|
|
1
|
+
import type { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, Range, TypeHierarchyItem } from '@adecore/lsp';
|
|
2
2
|
|
|
3
|
-
/* Callers of a symbol
|
|
4
|
-
export type CallDirection = 'incoming' | 'outgoing';
|
|
3
|
+
/* Callers of a symbol or the symbols it calls; or the super and sub types of a type, which the same tree draws. */
|
|
4
|
+
export type CallDirection = 'incoming' | 'outgoing' | 'supertypes' | 'subtypes';
|
|
5
|
+
|
|
6
|
+
/* Whether a direction walks a type hierarchy and not the calls. */
|
|
7
|
+
export function isTypeDirection(direction: CallDirection): direction is 'supertypes' | 'subtypes' {
|
|
8
|
+
return direction === 'supertypes' || direction === 'subtypes';
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
/* The most calls of one level that are drawn at once; the rest wait behind a row that shows more. */
|
|
7
12
|
export const CALL_LEVEL_LIMIT = 50;
|
|
@@ -103,6 +108,23 @@ export function childrenOf(direction: CallDirection, parent: CallNode, calls: re
|
|
|
103
108
|
}));
|
|
104
109
|
}
|
|
105
110
|
|
|
111
|
+
/* The children of a node made from the types a server returned; a type has no call sites, so a row goes to the type itself. */
|
|
112
|
+
export function typeChildrenOf(parent: CallNode, items: readonly TypeHierarchyItem[]): CallNode[] {
|
|
113
|
+
return items.map((item, index) => ({
|
|
114
|
+
id: `${parent.id}.${index}`,
|
|
115
|
+
parent: parent.id,
|
|
116
|
+
level: parent.level + 1,
|
|
117
|
+
item,
|
|
118
|
+
sites: [],
|
|
119
|
+
siteUri: null,
|
|
120
|
+
expanded: false,
|
|
121
|
+
load: 'idle',
|
|
122
|
+
error: null,
|
|
123
|
+
children: [],
|
|
124
|
+
shown: 0
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
|
|
106
128
|
/* The lines the tree draws: every root, and under an expanded node its status, its calls and the row that shows more. */
|
|
107
129
|
export function rowsOf(nodes: ReadonlyMap<string, CallNode>, roots: readonly string[]): CallRow[] {
|
|
108
130
|
const rows: CallRow[] = [];
|