@adecore/editor-react 0.0.1 → 0.17.0-beta.2
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/LICENSE +110 -0
- package/README.md +60 -0
- package/dist/AnchoredPopup.d.ts +12 -0
- package/dist/AnchoredPopup.js +27 -0
- package/dist/AttributionCard.d.ts +14 -0
- package/dist/AttributionCard.js +5 -0
- package/dist/BaselineStatus.d.ts +4 -0
- package/dist/BaselineStatus.js +13 -0
- package/dist/ChangeReview.d.ts +12 -0
- package/dist/ChangeReview.js +27 -0
- package/dist/CodeAuthorsCard.d.ts +6 -0
- package/dist/CodeAuthorsCard.js +22 -0
- package/dist/CompletionPopup.d.ts +8 -0
- package/dist/CompletionPopup.js +103 -0
- package/dist/EditorContextMenu.d.ts +8 -0
- package/dist/EditorContextMenu.js +23 -0
- package/dist/EditorView.d.ts +16 -0
- package/dist/EditorView.js +27 -0
- package/dist/FindReplace.d.ts +6 -0
- package/dist/FindReplace.js +44 -0
- package/dist/HoverCard.d.ts +11 -0
- package/dist/HoverCard.js +38 -0
- package/dist/HoverSections.d.ts +21 -0
- package/dist/HoverSections.js +82 -0
- package/dist/LanguagePopups.d.ts +6 -0
- package/dist/LanguagePopups.js +38 -0
- package/dist/Markdown.d.ts +3 -0
- package/dist/Markdown.js +7 -0
- package/dist/PathText.d.ts +4 -0
- package/dist/PathText.js +12 -0
- package/dist/PeekPanel.d.ts +6 -0
- package/dist/PeekPanel.js +111 -0
- package/dist/PickPopup.d.ts +8 -0
- package/dist/PickPopup.js +33 -0
- package/dist/RenameCard.d.ts +9 -0
- package/dist/RenameCard.js +108 -0
- package/dist/SignatureCard.d.ts +4 -0
- package/dist/SignatureCard.js +8 -0
- package/dist/SymbolPicker.d.ts +6 -0
- package/dist/SymbolPicker.js +108 -0
- package/dist/code-actions-model.d.ts +23 -0
- package/dist/code-actions-model.js +114 -0
- package/dist/code-actions.d.ts +30 -0
- package/dist/code-actions.js +326 -0
- package/dist/code-authors.d.ts +18 -0
- package/dist/code-authors.js +79 -0
- package/dist/code-vision-model.d.ts +21 -0
- package/dist/code-vision-model.js +183 -0
- package/dist/code-vision.d.ts +48 -0
- package/dist/code-vision.js +236 -0
- package/dist/completion-call.d.ts +24 -0
- package/dist/completion-call.js +110 -0
- package/dist/completion-model.d.ts +51 -0
- package/dist/completion-model.js +353 -0
- package/dist/completion.d.ts +43 -0
- package/dist/completion.js +463 -0
- package/dist/conflict-model.d.ts +15 -0
- package/dist/conflict-model.js +36 -0
- package/dist/context-menu.d.ts +10 -0
- package/dist/context-menu.js +55 -0
- package/dist/definition-link.d.ts +14 -0
- package/dist/definition-link.js +99 -0
- package/dist/diagnostics-model.d.ts +15 -0
- package/dist/diagnostics-model.js +83 -0
- package/dist/diagnostics.d.ts +23 -0
- package/dist/diagnostics.js +95 -0
- package/dist/editor-language.d.ts +65 -0
- package/dist/editor-language.js +170 -0
- package/dist/editor-react.css +43 -0
- package/dist/find-query.d.ts +26 -0
- package/dist/find-query.js +51 -0
- package/dist/folding.d.ts +16 -0
- package/dist/folding.js +95 -0
- package/dist/highlight-layers.d.ts +10 -0
- package/dist/highlight-layers.js +30 -0
- package/dist/highlights.d.ts +16 -0
- package/dist/highlights.js +102 -0
- package/dist/history.d.ts +8 -0
- package/dist/history.js +46 -0
- package/dist/holder.d.ts +5 -0
- package/dist/holder.js +19 -0
- package/dist/host-types.d.ts +45 -0
- package/dist/host-types.js +1 -0
- package/dist/hover-content.d.ts +45 -0
- package/dist/hover-content.js +198 -0
- package/dist/hover.d.ts +32 -0
- package/dist/hover.js +260 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +28 -0
- package/dist/inlay-hints.d.ts +13 -0
- package/dist/inlay-hints.js +71 -0
- package/dist/language-ids.d.ts +3 -0
- package/dist/language-ids.js +49 -0
- package/dist/line-edits.d.ts +8 -0
- package/dist/line-edits.js +39 -0
- package/dist/locales/en.json +171 -0
- package/dist/locales/nl.json +171 -0
- package/dist/models.d.ts +20 -0
- package/dist/models.js +20 -0
- package/dist/navigation-history.d.ts +16 -0
- package/dist/navigation-history.js +53 -0
- package/dist/navigation.d.ts +23 -0
- package/dist/navigation.js +155 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +3 -0
- package/dist/peek-model.d.ts +28 -0
- package/dist/peek-model.js +104 -0
- package/dist/peek.d.ts +30 -0
- package/dist/peek.js +215 -0
- package/dist/pick.d.ts +34 -0
- package/dist/pick.js +166 -0
- package/dist/popup-placement.d.ts +23 -0
- package/dist/popup-placement.js +34 -0
- package/dist/popups.d.ts +133 -0
- package/dist/popups.js +14 -0
- package/dist/project-language.d.ts +36 -0
- package/dist/project-language.js +190 -0
- package/dist/project-problems.d.ts +24 -0
- package/dist/project-problems.js +63 -0
- package/dist/proposal-model.d.ts +36 -0
- package/dist/proposal-model.js +197 -0
- package/dist/recent-choices.d.ts +10 -0
- package/dist/recent-choices.js +39 -0
- package/dist/refresher.d.ts +13 -0
- package/dist/refresher.js +50 -0
- package/dist/relative-time.d.ts +1 -0
- package/dist/relative-time.js +9 -0
- package/dist/rename-model.d.ts +25 -0
- package/dist/rename-model.js +93 -0
- package/dist/rename.d.ts +21 -0
- package/dist/rename.js +257 -0
- package/dist/rendering-context.d.ts +6 -0
- package/dist/rendering-context.js +5 -0
- package/dist/rendering.d.ts +6 -0
- package/dist/rendering.js +5 -0
- package/dist/review-group.d.ts +16 -0
- package/dist/review-group.js +54 -0
- package/dist/row-host.d.ts +42 -0
- package/dist/row-host.js +120 -0
- package/dist/selection-ranges.d.ts +7 -0
- package/dist/selection-ranges.js +26 -0
- package/dist/semantic-model.d.ts +4 -0
- package/dist/semantic-model.js +76 -0
- package/dist/semantic-tokens.d.ts +6 -0
- package/dist/semantic-tokens.js +38 -0
- package/dist/shortcut-keys.d.ts +91 -0
- package/dist/shortcut-keys.js +12 -0
- package/dist/signature-model.d.ts +18 -0
- package/dist/signature-model.js +33 -0
- package/dist/signature.d.ts +22 -0
- package/dist/signature.js +150 -0
- package/dist/snippet-session.d.ts +17 -0
- package/dist/snippet-session.js +110 -0
- package/dist/snippet.d.ts +11 -0
- package/dist/snippet.js +134 -0
- package/dist/symbol-links.d.ts +6 -0
- package/dist/symbol-links.js +50 -0
- package/dist/symbol-picker-model.d.ts +44 -0
- package/dist/symbol-picker-model.js +146 -0
- package/dist/symbol-picker.d.ts +12 -0
- package/dist/symbol-picker.js +76 -0
- package/dist/symbols.d.ts +11 -0
- package/dist/symbols.js +95 -0
- package/dist/testing.d.ts +57 -0
- package/dist/testing.js +156 -0
- package/dist/timers.d.ts +14 -0
- package/dist/timers.js +34 -0
- package/dist/use-problem-counts.d.ts +3 -0
- package/dist/use-problem-counts.js +20 -0
- package/dist/view-state.d.ts +21 -0
- package/dist/view-state.js +63 -0
- package/dist/word-diff.d.ts +1 -0
- package/dist/word-diff.js +32 -0
- package/dist/workspace-edit.d.ts +21 -0
- package/dist/workspace-edit.js +147 -0
- package/package.json +74 -2
- package/src/AnchoredPopup.tsx +51 -0
- package/src/AttributionCard.tsx +36 -0
- package/src/BaselineStatus.tsx +20 -0
- package/src/ChangeReview.tsx +43 -0
- package/src/CodeAuthorsCard.tsx +81 -0
- package/src/CompletionPopup.tsx +196 -0
- package/src/EditorContextMenu.tsx +67 -0
- package/src/EditorView.tsx +47 -0
- package/src/FindReplace.tsx +89 -0
- package/src/HoverCard.tsx +121 -0
- package/src/HoverSections.tsx +146 -0
- package/src/LanguagePopups.tsx +65 -0
- package/src/Markdown.tsx +11 -0
- package/src/PathText.tsx +18 -0
- package/src/PeekPanel.tsx +222 -0
- package/src/PickPopup.tsx +102 -0
- package/src/RenameCard.tsx +219 -0
- package/src/SignatureCard.tsx +42 -0
- package/src/SymbolPicker.tsx +219 -0
- package/src/code-actions-model.ts +146 -0
- package/src/code-actions.ts +384 -0
- package/src/code-authors.ts +104 -0
- package/src/code-vision-model.ts +230 -0
- package/src/code-vision.ts +269 -0
- package/src/completion-call.ts +139 -0
- package/src/completion-model.ts +440 -0
- package/src/completion.ts +500 -0
- package/src/conflict-model.ts +56 -0
- package/src/context-menu.ts +63 -0
- package/src/definition-link.ts +106 -0
- package/src/diagnostics-model.ts +102 -0
- package/src/diagnostics.ts +117 -0
- package/src/editor-language.ts +189 -0
- package/src/editor-react.css +43 -0
- package/src/find-query.ts +74 -0
- package/src/folding.ts +110 -0
- package/src/highlight-layers.ts +37 -0
- package/src/highlights.ts +123 -0
- package/src/history.ts +56 -0
- package/src/holder.ts +19 -0
- package/src/host-types.ts +52 -0
- package/src/hover-content.ts +264 -0
- package/src/hover.ts +280 -0
- package/src/index.ts +63 -0
- package/src/inlay-hints.ts +85 -0
- package/src/language-ids.ts +54 -0
- package/src/line-edits.ts +48 -0
- package/src/locales/en.json +171 -0
- package/src/locales/nl.json +171 -0
- package/src/models.ts +157 -0
- package/src/navigation-history.ts +69 -0
- package/src/navigation.ts +168 -0
- package/src/paths.ts +3 -0
- package/src/peek-model.ts +147 -0
- package/src/peek.ts +247 -0
- package/src/pick.ts +197 -0
- package/src/popup-placement.ts +64 -0
- package/src/popups.ts +204 -0
- package/src/project-language.ts +223 -0
- package/src/project-problems.ts +87 -0
- package/src/proposal-model.ts +259 -0
- package/src/recent-choices.ts +48 -0
- package/src/refresher.ts +56 -0
- package/src/relative-time.ts +10 -0
- package/src/rename-model.ts +130 -0
- package/src/rename.ts +290 -0
- package/src/rendering-context.ts +12 -0
- package/src/rendering.tsx +6 -0
- package/src/review-group.ts +70 -0
- package/src/row-host.ts +158 -0
- package/src/selection-ranges.ts +32 -0
- package/src/semantic-model.ts +80 -0
- package/src/semantic-tokens.ts +46 -0
- package/src/shortcut-keys.ts +21 -0
- package/src/signature-model.ts +49 -0
- package/src/signature.ts +164 -0
- package/src/snippet-session.ts +122 -0
- package/src/snippet.ts +147 -0
- package/src/symbol-links.ts +72 -0
- package/src/symbol-picker-model.ts +198 -0
- package/src/symbol-picker.ts +85 -0
- package/src/symbols.ts +110 -0
- package/src/testing.ts +240 -0
- package/src/timers.ts +45 -0
- package/src/use-problem-counts.ts +25 -0
- package/src/view-state.ts +88 -0
- package/src/word-diff.ts +35 -0
- package/src/workspace-edit.ts +194 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DocumentSymbolResult, WorkspaceSymbolResult } from '@adecore/lsp';
|
|
2
|
+
export type SymbolGroup = 'namespaces' | 'classes' | 'interfaces' | 'enums' | 'functions' | 'methods' | 'properties' | 'variables' | 'constants' | 'other';
|
|
3
|
+
export declare const SYMBOL_GROUPS: readonly SymbolGroup[];
|
|
4
|
+
export declare function groupOfKind(kind: number): SymbolGroup;
|
|
5
|
+
export declare function letterOfKind(kind: number): string;
|
|
6
|
+
export type SymbolTone = 'callable' | 'value' | 'type' | 'other';
|
|
7
|
+
export declare function toneOfKind(kind: number): SymbolTone;
|
|
8
|
+
export interface SymbolEntry {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly kind: number;
|
|
12
|
+
readonly container: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
readonly line: number;
|
|
15
|
+
readonly character: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function entriesOf(result: DocumentSymbolResult): SymbolEntry[];
|
|
18
|
+
export declare function scoreOf(name: string, query: string): number;
|
|
19
|
+
export declare function filterEntries(entries: readonly SymbolEntry[], query: string): SymbolEntry[];
|
|
20
|
+
export interface EntryGroup {
|
|
21
|
+
readonly group: SymbolGroup;
|
|
22
|
+
readonly entries: readonly SymbolEntry[];
|
|
23
|
+
}
|
|
24
|
+
export declare function groupEntries(entries: readonly SymbolEntry[]): EntryGroup[];
|
|
25
|
+
export type PickerMode = {
|
|
26
|
+
readonly mode: 'symbol';
|
|
27
|
+
readonly text: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly mode: 'workspace';
|
|
30
|
+
readonly text: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly mode: 'line';
|
|
33
|
+
readonly line: number | null;
|
|
34
|
+
};
|
|
35
|
+
export declare function modeOf(input: string): PickerMode;
|
|
36
|
+
export interface WorkspaceEntry {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly kind: number;
|
|
40
|
+
readonly container: string;
|
|
41
|
+
readonly uri: string;
|
|
42
|
+
readonly line: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function workspaceEntriesOf(result: WorkspaceSymbolResult): WorkspaceEntry[];
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/* The order the groups are listed in: what a file declares, then what those declare. */
|
|
2
|
+
export const SYMBOL_GROUPS = [
|
|
3
|
+
'namespaces',
|
|
4
|
+
'classes',
|
|
5
|
+
'interfaces',
|
|
6
|
+
'enums',
|
|
7
|
+
'functions',
|
|
8
|
+
'methods',
|
|
9
|
+
'properties',
|
|
10
|
+
'variables',
|
|
11
|
+
'constants',
|
|
12
|
+
'other'
|
|
13
|
+
];
|
|
14
|
+
const GROUP_OF_KIND = {
|
|
15
|
+
2: 'namespaces',
|
|
16
|
+
3: 'namespaces',
|
|
17
|
+
4: 'namespaces',
|
|
18
|
+
5: 'classes',
|
|
19
|
+
23: 'classes',
|
|
20
|
+
11: 'interfaces',
|
|
21
|
+
10: 'enums',
|
|
22
|
+
22: 'enums',
|
|
23
|
+
12: 'functions',
|
|
24
|
+
6: 'methods',
|
|
25
|
+
9: 'methods',
|
|
26
|
+
7: 'properties',
|
|
27
|
+
8: 'properties',
|
|
28
|
+
13: 'variables',
|
|
29
|
+
14: 'constants',
|
|
30
|
+
26: 'other'
|
|
31
|
+
};
|
|
32
|
+
export function groupOfKind(kind) {
|
|
33
|
+
return GROUP_OF_KIND[kind] ?? 'other';
|
|
34
|
+
}
|
|
35
|
+
const LETTERS = {
|
|
36
|
+
namespaces: 'N',
|
|
37
|
+
classes: 'C',
|
|
38
|
+
interfaces: 'I',
|
|
39
|
+
enums: 'E',
|
|
40
|
+
functions: 'f',
|
|
41
|
+
methods: 'm',
|
|
42
|
+
properties: 'p',
|
|
43
|
+
variables: 'v',
|
|
44
|
+
constants: 'c',
|
|
45
|
+
other: '·'
|
|
46
|
+
};
|
|
47
|
+
/* The letter in the badge in front of a symbol. */
|
|
48
|
+
export function letterOfKind(kind) {
|
|
49
|
+
return kind === 22 ? 'e' : LETTERS[groupOfKind(kind)];
|
|
50
|
+
}
|
|
51
|
+
export function toneOfKind(kind) {
|
|
52
|
+
const group = groupOfKind(kind);
|
|
53
|
+
return group === 'functions' || group === 'methods'
|
|
54
|
+
? 'callable'
|
|
55
|
+
: group === 'classes' || group === 'interfaces' || group === 'enums'
|
|
56
|
+
? 'type'
|
|
57
|
+
: group === 'properties' || group === 'variables' || group === 'constants'
|
|
58
|
+
? 'value'
|
|
59
|
+
: 'other';
|
|
60
|
+
}
|
|
61
|
+
function isHierarchical(symbols) {
|
|
62
|
+
return symbols.length > 0 && 'selectionRange' in symbols[0];
|
|
63
|
+
}
|
|
64
|
+
/* The symbols of a file, parents before their children, in the order of the file. */
|
|
65
|
+
export function entriesOf(result) {
|
|
66
|
+
const entries = [];
|
|
67
|
+
const add = (name, kind, container, detail, at) => {
|
|
68
|
+
entries.push({ id: String(entries.length), name, kind, container, detail, line: at.line, character: at.character });
|
|
69
|
+
};
|
|
70
|
+
const walk = (symbols, container) => {
|
|
71
|
+
for (const symbol of symbols) {
|
|
72
|
+
add(symbol.name, symbol.kind, container, symbol.detail ?? '', symbol.selectionRange.start);
|
|
73
|
+
walk(symbol.children ?? [], container === '' ? symbol.name : `${container}.${symbol.name}`);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
if (result === null || result.length === 0) {
|
|
77
|
+
return entries;
|
|
78
|
+
}
|
|
79
|
+
if (isHierarchical(result)) {
|
|
80
|
+
walk(result, '');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
for (const symbol of result) {
|
|
84
|
+
add(symbol.name, symbol.kind, symbol.containerName ?? '', '', symbol.location.range.start);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return entries;
|
|
88
|
+
}
|
|
89
|
+
/* How well a query matches a name: 3 for a prefix, 2 for a part of it, 1 for its letters in order, 0 for no match. Case is ignored. */
|
|
90
|
+
export function scoreOf(name, query) {
|
|
91
|
+
const haystack = name.toLowerCase();
|
|
92
|
+
const needle = query.toLowerCase();
|
|
93
|
+
if (needle === '') {
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
if (haystack.startsWith(needle)) {
|
|
97
|
+
return 3;
|
|
98
|
+
}
|
|
99
|
+
if (haystack.includes(needle)) {
|
|
100
|
+
return 2;
|
|
101
|
+
}
|
|
102
|
+
let from = 0;
|
|
103
|
+
for (const character of needle) {
|
|
104
|
+
from = haystack.indexOf(character, from) + 1;
|
|
105
|
+
if (from === 0) {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
/* The entries that match, best first within each group; without a query, in the order of the file. */
|
|
112
|
+
export function filterEntries(entries, query) {
|
|
113
|
+
const trimmed = query.trim();
|
|
114
|
+
const scored = entries.map((entry, index) => ({ entry, index, score: scoreOf(entry.name, trimmed) })).filter((candidate) => candidate.score > 0);
|
|
115
|
+
return scored
|
|
116
|
+
.sort((left, right) => (trimmed === '' ? left.index - right.index : right.score - left.score || left.index - right.index))
|
|
117
|
+
.map((candidate) => candidate.entry);
|
|
118
|
+
}
|
|
119
|
+
/* The entries in their groups, in the order of the groups, and so in the order the list is walked with the arrows. */
|
|
120
|
+
export function groupEntries(entries) {
|
|
121
|
+
return SYMBOL_GROUPS.flatMap((group) => {
|
|
122
|
+
const own = entries.filter((entry) => groupOfKind(entry.kind) === group);
|
|
123
|
+
return own.length === 0 ? [] : [{ group, entries: own }];
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/* `#name` searches the whole project, `:12` goes to a line, anything else filters the symbols of the file. */
|
|
127
|
+
export function modeOf(input) {
|
|
128
|
+
if (input.startsWith('#')) {
|
|
129
|
+
return { mode: 'workspace', text: input.slice(1).trim() };
|
|
130
|
+
}
|
|
131
|
+
if (input.startsWith(':')) {
|
|
132
|
+
const line = Number.parseInt(input.slice(1), 10);
|
|
133
|
+
return { mode: 'line', line: Number.isFinite(line) && line > 0 ? line : null };
|
|
134
|
+
}
|
|
135
|
+
return { mode: 'symbol', text: input };
|
|
136
|
+
}
|
|
137
|
+
export function workspaceEntriesOf(result) {
|
|
138
|
+
return (result ?? []).map((symbol, index) => ({
|
|
139
|
+
id: String(index),
|
|
140
|
+
name: symbol.name,
|
|
141
|
+
kind: symbol.kind,
|
|
142
|
+
container: symbol.containerName ?? '',
|
|
143
|
+
uri: symbol.location.uri,
|
|
144
|
+
line: 'range' in symbol.location ? symbol.location.range.start.line : 0
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
2
|
+
import { type SymbolEntry, type WorkspaceEntry } from './symbol-picker-model.ts';
|
|
3
|
+
export declare class SymbolPickerFeature {
|
|
4
|
+
private readonly language;
|
|
5
|
+
constructor(language: EditorLanguage);
|
|
6
|
+
open(): Promise<void>;
|
|
7
|
+
close(): void;
|
|
8
|
+
goTo(entry: SymbolEntry): void;
|
|
9
|
+
goToLine(line: number): void;
|
|
10
|
+
search(text: string, signal: AbortSignal): Promise<WorkspaceEntry[]>;
|
|
11
|
+
goToWorkspace(entry: WorkspaceEntry): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { StaleResultError } from '@adecore/lsp';
|
|
2
|
+
import { basenameOf } from './paths.js';
|
|
3
|
+
import { entriesOf, workspaceEntriesOf } from './symbol-picker-model.js';
|
|
4
|
+
import { isShortcut } from './shortcut-keys.js';
|
|
5
|
+
const TOAST_ID = 'language-symbols';
|
|
6
|
+
/*
|
|
7
|
+
* Go to symbol: a picker over the symbols the language servers know in the file, filtered as it is typed.
|
|
8
|
+
* A `#` in front searches the whole project and a `:` in front goes to a line. The picker answers to
|
|
9
|
+
* its own input, so the editor only has to open it.
|
|
10
|
+
*/
|
|
11
|
+
export class SymbolPickerFeature {
|
|
12
|
+
language;
|
|
13
|
+
constructor(language) {
|
|
14
|
+
this.language = language;
|
|
15
|
+
const off = language.editor.onKeyDown((event) => {
|
|
16
|
+
if (!isShortcut(this.language.shortcuts.goToSymbol, event)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
void this.open();
|
|
20
|
+
return true;
|
|
21
|
+
});
|
|
22
|
+
language.onDispose(() => {
|
|
23
|
+
off();
|
|
24
|
+
this.close();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async open() {
|
|
28
|
+
const { project, uri, popups } = this.language;
|
|
29
|
+
if (!project.service.supports('textDocument/documentSymbol', uri)) {
|
|
30
|
+
this.language.project.host.notify?.({ id: TOAST_ID, kind: 'error', title: this.language.project.i18n.t('editor:language.symbols.unavailable') });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const entries = entriesOf(await project.service.documentSymbols(uri));
|
|
35
|
+
popups.setState({ symbols: { file: basenameOf(this.language.uri), entries } });
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (!(error instanceof StaleResultError)) {
|
|
39
|
+
this.language.project.host.notify?.({
|
|
40
|
+
id: TOAST_ID,
|
|
41
|
+
kind: 'error',
|
|
42
|
+
title: this.language.project.i18n.t('editor:language.symbols.failed', { message: error instanceof Error ? error.message : String(error) })
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
close() {
|
|
48
|
+
if (this.language.popups.getState().symbols !== null) {
|
|
49
|
+
this.language.popups.setState({ symbols: null });
|
|
50
|
+
this.language.editor.focus();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/* Puts the caret on a symbol of the file. */
|
|
54
|
+
goTo(entry) {
|
|
55
|
+
this.close();
|
|
56
|
+
this.language.jump({ line: entry.line, character: entry.character });
|
|
57
|
+
}
|
|
58
|
+
/* Puts the caret on a one-based line. */
|
|
59
|
+
goToLine(line) {
|
|
60
|
+
this.close();
|
|
61
|
+
this.language.jump({ line: line - 1, character: 0 });
|
|
62
|
+
}
|
|
63
|
+
/* The symbols of the project that answer to a name, which is a request to the server and so waits for a pause in the typing. */
|
|
64
|
+
async search(text, signal) {
|
|
65
|
+
const { project, uri } = this.language;
|
|
66
|
+
if (text === '' || !project.service.supports('workspace/symbol', uri)) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return workspaceEntriesOf(await project.service.workspaceSymbols(uri, text, { signal }));
|
|
70
|
+
}
|
|
71
|
+
goToWorkspace(entry) {
|
|
72
|
+
this.close();
|
|
73
|
+
const location = { uri: entry.uri, range: { start: { line: entry.line, character: 0 }, end: { line: entry.line, character: 0 } } };
|
|
74
|
+
this.language.goTo(location);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EditorBlock } from '@adecore/editor';
|
|
2
|
+
import type { DocumentSymbolResult } from '@adecore/lsp';
|
|
3
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
4
|
+
import { type Timers } from './timers.ts';
|
|
5
|
+
export declare function blocksOf(result: DocumentSymbolResult): EditorBlock[];
|
|
6
|
+
export declare class SymbolsFeature {
|
|
7
|
+
private readonly refresher;
|
|
8
|
+
private readonly listeners;
|
|
9
|
+
constructor(language: EditorLanguage, timers?: Timers);
|
|
10
|
+
onResult(listener: (result: DocumentSymbolResult) => void): () => void;
|
|
11
|
+
}
|
package/dist/symbols.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Refresher } from './refresher.js';
|
|
2
|
+
import { realTimers } from './timers.js';
|
|
3
|
+
const METHOD = 'textDocument/documentSymbol';
|
|
4
|
+
const PAUSE_MS = 500;
|
|
5
|
+
/* The names a breadcrumb and a sticky header know a symbol kind by; a kind left out is not a block worth pinning. */
|
|
6
|
+
const KINDS = {
|
|
7
|
+
2: 'module',
|
|
8
|
+
3: 'namespace',
|
|
9
|
+
4: 'package',
|
|
10
|
+
5: 'class',
|
|
11
|
+
6: 'method',
|
|
12
|
+
9: 'constructor',
|
|
13
|
+
10: 'enum',
|
|
14
|
+
11: 'interface',
|
|
15
|
+
12: 'function',
|
|
16
|
+
13: 'variable',
|
|
17
|
+
14: 'constant',
|
|
18
|
+
23: 'struct'
|
|
19
|
+
};
|
|
20
|
+
function isHierarchical(symbols) {
|
|
21
|
+
return symbols.length > 0 && 'range' in symbols[0];
|
|
22
|
+
}
|
|
23
|
+
function blockOf(name, kind, startLine, endLine) {
|
|
24
|
+
const label = KINDS[kind];
|
|
25
|
+
// A one-line symbol has no body to pin, and a property or an enum member is part of the block around it.
|
|
26
|
+
return label === undefined || endLine <= startLine ? null : { startLine: startLine + 1, endLine: endLine + 1, name, kind: label };
|
|
27
|
+
}
|
|
28
|
+
function walk(symbols, blocks) {
|
|
29
|
+
for (const symbol of symbols) {
|
|
30
|
+
const block = blockOf(symbol.name, symbol.kind, symbol.range.start.line, symbol.range.end.line);
|
|
31
|
+
if (block !== null) {
|
|
32
|
+
blocks.push(block);
|
|
33
|
+
}
|
|
34
|
+
walk(symbol.children ?? [], blocks);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/* The multi-line symbols of a file as blocks, outermost first, in one-based lines. */
|
|
38
|
+
export function blocksOf(result) {
|
|
39
|
+
const blocks = [];
|
|
40
|
+
if (result === null || result.length === 0) {
|
|
41
|
+
return blocks;
|
|
42
|
+
}
|
|
43
|
+
if (isHierarchical(result)) {
|
|
44
|
+
walk(result, blocks);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
for (const symbol of result) {
|
|
48
|
+
const block = blockOf(symbol.name, symbol.kind, symbol.location.range.start.line, symbol.location.range.end.line);
|
|
49
|
+
if (block !== null) {
|
|
50
|
+
blocks.push(block);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return blocks.sort((left, right) => left.startLine - right.startLine || right.endLine - left.endLine);
|
|
55
|
+
}
|
|
56
|
+
/*
|
|
57
|
+
* The symbols of the file as the blocks sticky scroll and the breadcrumb go by, with their real names. Until
|
|
58
|
+
* the first answer, and wherever no server has symbols, the editor reads blocks off the text itself.
|
|
59
|
+
*/
|
|
60
|
+
export class SymbolsFeature {
|
|
61
|
+
refresher;
|
|
62
|
+
listeners = new Set();
|
|
63
|
+
constructor(language, timers = realTimers) {
|
|
64
|
+
const { editor, project, uri } = language;
|
|
65
|
+
this.refresher = new Refresher(async (signal) => {
|
|
66
|
+
if (!project.service.supports(METHOD, uri)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const result = await project.service.documentSymbols(uri, { signal });
|
|
70
|
+
if (!signal.aborted) {
|
|
71
|
+
editor.setBlocks(result === null ? null : blocksOf(result));
|
|
72
|
+
for (const listener of [...this.listeners]) {
|
|
73
|
+
listener(result);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}, PAUSE_MS, timers);
|
|
77
|
+
const edits = editor.onTextChange(() => this.refresher.later());
|
|
78
|
+
const providers = language.onProvidersChanged(() => {
|
|
79
|
+
this.refresher.now();
|
|
80
|
+
});
|
|
81
|
+
language.onDispose(() => {
|
|
82
|
+
edits();
|
|
83
|
+
providers();
|
|
84
|
+
this.refresher.dispose();
|
|
85
|
+
editor.setBlocks(null);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/* Called with every answer that was not replaced before it came. */
|
|
89
|
+
onResult(listener) {
|
|
90
|
+
this.listeners.add(listener);
|
|
91
|
+
return () => {
|
|
92
|
+
this.listeners.delete(listener);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, 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';
|
|
2
|
+
export { ManualTimers } from './timers.ts';
|
|
3
|
+
export interface LanguageCall {
|
|
4
|
+
method: string;
|
|
5
|
+
uri: string;
|
|
6
|
+
params: unknown;
|
|
7
|
+
options: LanguageRequestOptions;
|
|
8
|
+
}
|
|
9
|
+
export declare class FakeLanguageService implements LanguageService {
|
|
10
|
+
readonly documents: Map<string, LanguageDocument & {
|
|
11
|
+
version: number;
|
|
12
|
+
}>;
|
|
13
|
+
readonly calls: LanguageCall[];
|
|
14
|
+
readonly providers: Map<string, ProviderOptions>;
|
|
15
|
+
private readonly answers;
|
|
16
|
+
private readonly diagnostics;
|
|
17
|
+
private readonly changes;
|
|
18
|
+
respond(method: string, answer: (call: LanguageCall) => unknown, provider?: ProviderOptions): void;
|
|
19
|
+
openDocument(document: LanguageDocument): Promise<void>;
|
|
20
|
+
changeDocument(uri: string, changes: readonly ContentChange[]): Promise<void>;
|
|
21
|
+
closeDocument(uri: string): Promise<void>;
|
|
22
|
+
supports(method: string, _uri: string): boolean;
|
|
23
|
+
providerOptions(method: string, _uri: string): ProviderOptions | undefined;
|
|
24
|
+
onProvidersChanged(listener: (uri: string) => void): Disposable;
|
|
25
|
+
onDiagnostics(listener: (report: DiagnosticsReport) => void): Disposable;
|
|
26
|
+
report(report: DiagnosticsReport): void;
|
|
27
|
+
refresh(uri: string): void;
|
|
28
|
+
private request;
|
|
29
|
+
completion(uri: string, position: Position, context?: CompletionContext, options?: LanguageRequestOptions): Promise<CompletionResult>;
|
|
30
|
+
resolveCompletion(uri: string, item: CompletionItem, options?: LanguageRequestOptions): Promise<CompletionItem>;
|
|
31
|
+
hover(uri: string, position: Position, options?: LanguageRequestOptions): Promise<Hover | null>;
|
|
32
|
+
signatureHelp(uri: string, position: Position, context?: SignatureHelpContext, options?: LanguageRequestOptions): Promise<SignatureHelp | null>;
|
|
33
|
+
definition(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
|
|
34
|
+
declaration(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
|
|
35
|
+
typeDefinition(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
|
|
36
|
+
implementation(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
|
|
37
|
+
references(uri: string, position: Position, includeDeclaration?: boolean, options?: LanguageRequestOptions): Promise<Location[] | null>;
|
|
38
|
+
documentHighlights(uri: string, position: Position, options?: LanguageRequestOptions): Promise<DocumentHighlight[] | null>;
|
|
39
|
+
documentSymbols(uri: string, options?: LanguageRequestOptions): Promise<DocumentSymbolResult>;
|
|
40
|
+
prepareRename(uri: string, position: Position, options?: LanguageRequestOptions): Promise<PrepareRenameResult>;
|
|
41
|
+
rename(uri: string, position: Position, newName: string, options?: LanguageRequestOptions): Promise<WorkspaceEdit | null>;
|
|
42
|
+
codeActions(uri: string, range: Range, context?: CodeActionContext, options?: LanguageRequestOptions): Promise<(CodeAction | Command)[] | null>;
|
|
43
|
+
resolveCodeAction(uri: string, action: CodeAction, options?: LanguageRequestOptions): Promise<CodeAction>;
|
|
44
|
+
executeCommand(uri: string, command: Command, options?: LanguageRequestOptions): Promise<unknown>;
|
|
45
|
+
formatting(uri: string, formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
|
|
46
|
+
rangeFormatting(uri: string, range: Range, formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
|
|
47
|
+
semanticTokens(uri: string, options?: LanguageRequestOptions): Promise<SemanticTokens | null>;
|
|
48
|
+
semanticTokensDelta(uri: string, previousResultId: string, options?: LanguageRequestOptions): Promise<SemanticTokens | SemanticTokensDelta | null>;
|
|
49
|
+
semanticTokensRange(uri: string, range: Range, options?: LanguageRequestOptions): Promise<SemanticTokens | null>;
|
|
50
|
+
inlayHints(uri: string, range: Range, options?: LanguageRequestOptions): Promise<InlayHint[] | null>;
|
|
51
|
+
resolveInlayHint(uri: string, hint: InlayHint, options?: LanguageRequestOptions): Promise<InlayHint>;
|
|
52
|
+
workspaceSymbols(uri: string, query: string, options?: LanguageRequestOptions): Promise<WorkspaceSymbolResult>;
|
|
53
|
+
foldingRanges(uri: string, options?: LanguageRequestOptions): Promise<FoldingRange[] | null>;
|
|
54
|
+
selectionRanges(uri: string, positions: readonly Position[], options?: LanguageRequestOptions): Promise<SelectionRange[] | null>;
|
|
55
|
+
codeLenses(uri: string, options?: LanguageRequestOptions): Promise<CodeLens[] | null>;
|
|
56
|
+
resolveCodeLens(uri: string, lens: CodeLens, options?: LanguageRequestOptions): Promise<CodeLens>;
|
|
57
|
+
}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { applyContentChanges, LspError, StaleResultError } from '@adecore/lsp';
|
|
2
|
+
export { ManualTimers } from './timers.js';
|
|
3
|
+
export class FakeLanguageService {
|
|
4
|
+
documents = new Map();
|
|
5
|
+
calls = [];
|
|
6
|
+
providers = new Map();
|
|
7
|
+
answers = new Map();
|
|
8
|
+
diagnostics = new Set();
|
|
9
|
+
changes = new Set();
|
|
10
|
+
respond(method, answer, provider = {}) {
|
|
11
|
+
this.answers.set(method, answer);
|
|
12
|
+
this.providers.set(method, provider);
|
|
13
|
+
}
|
|
14
|
+
async openDocument(document) {
|
|
15
|
+
const held = this.documents.get(document.uri);
|
|
16
|
+
this.documents.set(document.uri, { ...document, version: held === undefined ? 1 : held.version + (held.text === document.text ? 0 : 1) });
|
|
17
|
+
this.calls.push({ method: 'open', uri: document.uri, params: document, options: {} });
|
|
18
|
+
await this.answers.get('open')?.(this.calls.at(-1));
|
|
19
|
+
}
|
|
20
|
+
async changeDocument(uri, changes) {
|
|
21
|
+
const held = this.documents.get(uri);
|
|
22
|
+
if (held === undefined) {
|
|
23
|
+
throw new LspError('Document is not open', -32002);
|
|
24
|
+
}
|
|
25
|
+
held.text = applyContentChanges(held.text, changes);
|
|
26
|
+
held.version++;
|
|
27
|
+
this.calls.push({ method: 'change', uri, params: changes, options: {} });
|
|
28
|
+
}
|
|
29
|
+
async closeDocument(uri) {
|
|
30
|
+
this.documents.delete(uri);
|
|
31
|
+
this.calls.push({ method: 'close', uri, params: null, options: {} });
|
|
32
|
+
}
|
|
33
|
+
supports(method, _uri) {
|
|
34
|
+
return this.providers.has(method);
|
|
35
|
+
}
|
|
36
|
+
providerOptions(method, _uri) {
|
|
37
|
+
return this.providers.get(method);
|
|
38
|
+
}
|
|
39
|
+
onProvidersChanged(listener) {
|
|
40
|
+
this.changes.add(listener);
|
|
41
|
+
return { dispose: () => this.changes.delete(listener) };
|
|
42
|
+
}
|
|
43
|
+
onDiagnostics(listener) {
|
|
44
|
+
this.diagnostics.add(listener);
|
|
45
|
+
return { dispose: () => this.diagnostics.delete(listener) };
|
|
46
|
+
}
|
|
47
|
+
report(report) {
|
|
48
|
+
for (const listener of this.diagnostics) {
|
|
49
|
+
listener(report);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
refresh(uri) {
|
|
53
|
+
for (const listener of this.changes) {
|
|
54
|
+
listener(uri);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async request(method, uri, params, options = {}) {
|
|
58
|
+
if (!this.supports(method, uri)) {
|
|
59
|
+
throw new LspError(`${method} is unavailable`, -32601);
|
|
60
|
+
}
|
|
61
|
+
options.signal?.throwIfAborted();
|
|
62
|
+
const version = this.documents.get(uri)?.version;
|
|
63
|
+
const call = { method, uri, params, options };
|
|
64
|
+
this.calls.push(call);
|
|
65
|
+
const result = await this.answers.get(method)?.(call);
|
|
66
|
+
options.signal?.throwIfAborted();
|
|
67
|
+
if (this.documents.get(uri)?.version !== version) {
|
|
68
|
+
throw new StaleResultError(uri);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
completion(uri, position, context, options = {}) {
|
|
73
|
+
return this.request('textDocument/completion', uri, { position, context }, options);
|
|
74
|
+
}
|
|
75
|
+
resolveCompletion(uri, item, options = {}) {
|
|
76
|
+
return this.request('completionItem/resolve', uri, item, options);
|
|
77
|
+
}
|
|
78
|
+
hover(uri, position, options = {}) {
|
|
79
|
+
return this.request('textDocument/hover', uri, { position }, options);
|
|
80
|
+
}
|
|
81
|
+
signatureHelp(uri, position, context, options = {}) {
|
|
82
|
+
return this.request('textDocument/signatureHelp', uri, { position, context }, options);
|
|
83
|
+
}
|
|
84
|
+
definition(uri, position, options = {}) {
|
|
85
|
+
return this.request('textDocument/definition', uri, { position }, options);
|
|
86
|
+
}
|
|
87
|
+
declaration(uri, position, options = {}) {
|
|
88
|
+
return this.request('textDocument/declaration', uri, { position }, options);
|
|
89
|
+
}
|
|
90
|
+
typeDefinition(uri, position, options = {}) {
|
|
91
|
+
return this.request('textDocument/typeDefinition', uri, { position }, options);
|
|
92
|
+
}
|
|
93
|
+
implementation(uri, position, options = {}) {
|
|
94
|
+
return this.request('textDocument/implementation', uri, { position }, options);
|
|
95
|
+
}
|
|
96
|
+
references(uri, position, includeDeclaration, options = {}) {
|
|
97
|
+
return this.request('textDocument/references', uri, { position, context: { includeDeclaration } }, options);
|
|
98
|
+
}
|
|
99
|
+
documentHighlights(uri, position, options = {}) {
|
|
100
|
+
return this.request('textDocument/documentHighlight', uri, { position }, options);
|
|
101
|
+
}
|
|
102
|
+
documentSymbols(uri, options = {}) {
|
|
103
|
+
return this.request('textDocument/documentSymbol', uri, {}, options);
|
|
104
|
+
}
|
|
105
|
+
prepareRename(uri, position, options = {}) {
|
|
106
|
+
return this.request('textDocument/prepareRename', uri, { position }, options);
|
|
107
|
+
}
|
|
108
|
+
rename(uri, position, newName, options = {}) {
|
|
109
|
+
return this.request('textDocument/rename', uri, { position, newName }, options);
|
|
110
|
+
}
|
|
111
|
+
codeActions(uri, range, context, options = {}) {
|
|
112
|
+
return this.request('textDocument/codeAction', uri, { range, context }, options);
|
|
113
|
+
}
|
|
114
|
+
resolveCodeAction(uri, action, options = {}) {
|
|
115
|
+
return this.request('codeAction/resolve', uri, action, options);
|
|
116
|
+
}
|
|
117
|
+
executeCommand(uri, command, options = {}) {
|
|
118
|
+
return this.request('workspace/executeCommand', uri, command, options);
|
|
119
|
+
}
|
|
120
|
+
formatting(uri, formatting, options = {}) {
|
|
121
|
+
return this.request('textDocument/formatting', uri, { options: formatting }, options);
|
|
122
|
+
}
|
|
123
|
+
rangeFormatting(uri, range, formatting, options = {}) {
|
|
124
|
+
return this.request('textDocument/rangeFormatting', uri, { range, options: formatting }, options);
|
|
125
|
+
}
|
|
126
|
+
semanticTokens(uri, options = {}) {
|
|
127
|
+
return this.request('textDocument/semanticTokens/full', uri, {}, options);
|
|
128
|
+
}
|
|
129
|
+
semanticTokensDelta(uri, previousResultId, options = {}) {
|
|
130
|
+
return this.request('textDocument/semanticTokens/full/delta', uri, { previousResultId }, options);
|
|
131
|
+
}
|
|
132
|
+
semanticTokensRange(uri, range, options = {}) {
|
|
133
|
+
return this.request('textDocument/semanticTokens/range', uri, { range }, options);
|
|
134
|
+
}
|
|
135
|
+
inlayHints(uri, range, options = {}) {
|
|
136
|
+
return this.request('textDocument/inlayHint', uri, { range }, options);
|
|
137
|
+
}
|
|
138
|
+
resolveInlayHint(uri, hint, options = {}) {
|
|
139
|
+
return this.request('inlayHint/resolve', uri, hint, options);
|
|
140
|
+
}
|
|
141
|
+
workspaceSymbols(uri, query, options = {}) {
|
|
142
|
+
return this.request('workspace/symbol', uri, { query }, options);
|
|
143
|
+
}
|
|
144
|
+
foldingRanges(uri, options = {}) {
|
|
145
|
+
return this.request('textDocument/foldingRange', uri, {}, options);
|
|
146
|
+
}
|
|
147
|
+
selectionRanges(uri, positions, options = {}) {
|
|
148
|
+
return this.request('textDocument/selectionRange', uri, { positions }, options);
|
|
149
|
+
}
|
|
150
|
+
codeLenses(uri, options = {}) {
|
|
151
|
+
return this.request('textDocument/codeLens', uri, {}, options);
|
|
152
|
+
}
|
|
153
|
+
resolveCodeLens(uri, lens, options = {}) {
|
|
154
|
+
return this.request('codeLens/resolve', uri, lens, options);
|
|
155
|
+
}
|
|
156
|
+
}
|
package/dist/timers.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Timers {
|
|
2
|
+
set(callback: () => void, delay: number): unknown;
|
|
3
|
+
clear(handle: unknown): void;
|
|
4
|
+
}
|
|
5
|
+
export declare const realTimers: Timers;
|
|
6
|
+
export declare class ManualTimers implements Timers {
|
|
7
|
+
private now;
|
|
8
|
+
private next;
|
|
9
|
+
private readonly pending;
|
|
10
|
+
set(callback: () => void, delay: number): unknown;
|
|
11
|
+
clear(handle: unknown): void;
|
|
12
|
+
advance(milliseconds: number): void;
|
|
13
|
+
get size(): number;
|
|
14
|
+
}
|
package/dist/timers.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const realTimers = {
|
|
2
|
+
set: (callback, delay) => setTimeout(callback, delay),
|
|
3
|
+
clear: (handle) => clearTimeout(handle)
|
|
4
|
+
};
|
|
5
|
+
/* Runs what was scheduled when a test says how much time has passed. */
|
|
6
|
+
export class ManualTimers {
|
|
7
|
+
now = 0;
|
|
8
|
+
next = 1;
|
|
9
|
+
pending = new Map();
|
|
10
|
+
set(callback, delay) {
|
|
11
|
+
const id = this.next++;
|
|
12
|
+
this.pending.set(id, { at: this.now + delay, callback });
|
|
13
|
+
return id;
|
|
14
|
+
}
|
|
15
|
+
clear(handle) {
|
|
16
|
+
this.pending.delete(handle);
|
|
17
|
+
}
|
|
18
|
+
advance(milliseconds) {
|
|
19
|
+
const until = this.now + milliseconds;
|
|
20
|
+
for (;;) {
|
|
21
|
+
const due = [...this.pending].filter(([, entry]) => entry.at <= until).sort((left, right) => left[1].at - right[1].at)[0];
|
|
22
|
+
if (due === undefined) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
this.now = due[1].at;
|
|
26
|
+
this.pending.delete(due[0]);
|
|
27
|
+
due[1].callback();
|
|
28
|
+
}
|
|
29
|
+
this.now = until;
|
|
30
|
+
}
|
|
31
|
+
get size() {
|
|
32
|
+
return this.pending.size;
|
|
33
|
+
}
|
|
34
|
+
}
|