@adecore/editor-react 0.42.0 → 0.43.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 +190 -70
- 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.js +10 -15
- 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 +19 -1
- package/dist/editor-language.js +77 -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 +6 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +8 -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 +8 -5
- package/dist/key-priority.js +9 -6
- package/dist/linked-editing.d.ts +26 -0
- package/dist/linked-editing.js +215 -0
- package/dist/locales/en.json +31 -2
- package/dist/locales/nl.json +31 -2
- package/dist/markup-context.d.ts +37 -0
- package/dist/markup-context.js +291 -0
- package/dist/markup.d.ts +36 -0
- package/dist/markup.js +453 -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 +20 -2
- package/dist/pick.js +141 -18
- package/dist/popups.d.ts +9 -0
- package/dist/popups.js +2 -1
- package/dist/project-language.d.ts +18 -1
- package/dist/project-language.js +129 -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 +19 -0
- package/dist/signature.js +3 -6
- package/dist/snippet-session.d.ts +2 -2
- package/dist/snippet-session.js +3 -3
- package/dist/symbol-picker.js +4 -6
- package/dist/symbols.js +22 -3
- 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 +40 -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 +15 -15
- 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 +80 -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 +10 -0
- package/src/index.ts +12 -3
- package/src/inlay-hints.ts +2 -2
- package/src/inline-completion.ts +221 -0
- package/src/key-priority.ts +9 -6
- package/src/linked-editing.ts +199 -0
- package/src/locales/en.json +31 -2
- package/src/locales/nl.json +31 -2
- package/src/markup-context.ts +262 -0
- package/src/markup.ts +367 -0
- package/src/navigation.ts +6 -6
- package/src/paste-history.ts +111 -0
- package/src/peek.ts +4 -4
- package/src/pick.ts +201 -19
- package/src/popups.ts +19 -1
- package/src/project-language.ts +136 -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-session.ts +9 -4
- package/src/symbol-picker.ts +6 -6
- package/src/symbols.ts +30 -4
- package/src/testing.ts +126 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { comparePositions } from './diagnostics-model.js';
|
|
2
|
+
import { KEY_PRIORITY } from './key-priority.js';
|
|
3
|
+
import { isShortcut } from './shortcut-keys.js';
|
|
4
|
+
import { parseSnippet } from './snippet.js';
|
|
5
|
+
import { realTimers } from './timers.js';
|
|
6
|
+
const METHOD = 'textDocument/inlineCompletion';
|
|
7
|
+
/* How long typing rests before a suggestion is asked. */
|
|
8
|
+
const DELAY_MS = 300;
|
|
9
|
+
export function itemsOf(result) {
|
|
10
|
+
if (result === null) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return Array.isArray(result) ? result : result.items;
|
|
14
|
+
}
|
|
15
|
+
/*
|
|
16
|
+
* A server's item as a suggestion at the caret, or null when it does not continue what the line holds: the text the
|
|
17
|
+
* item replaces up to the caret must start its insert text, so the rest can stand after the caret.
|
|
18
|
+
*/
|
|
19
|
+
export function suggestionOf(item, caret, textOf) {
|
|
20
|
+
const text = typeof item.insertText === 'string' ? item.insertText : parseSnippet(item.insertText.value).text;
|
|
21
|
+
const range = item.range ?? {
|
|
22
|
+
start: caret,
|
|
23
|
+
end: caret
|
|
24
|
+
};
|
|
25
|
+
if (comparePositions(range.start, caret) > 0 || comparePositions(range.end, caret) < 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const typed = textOf({
|
|
29
|
+
start: range.start,
|
|
30
|
+
end: caret
|
|
31
|
+
});
|
|
32
|
+
if (!text.startsWith(typed)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const rest = text.slice(typed.length);
|
|
36
|
+
return rest === '' ? null : {
|
|
37
|
+
item,
|
|
38
|
+
text,
|
|
39
|
+
range,
|
|
40
|
+
rest
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/*
|
|
44
|
+
* Suggestions a server writes ahead of the caret, drawn as ghost text: asked a moment after typing stops, or at
|
|
45
|
+
* once with the `suggestInline` key of the host. Tab takes the suggestion, Alt+] and Alt+[ step through the others,
|
|
46
|
+
* Escape and any key that moves away put it away. Off unless the host turns it on, since a host may draw
|
|
47
|
+
* suggestions of its own in the same place.
|
|
48
|
+
*/
|
|
49
|
+
export class InlineCompletionFeature {
|
|
50
|
+
language;
|
|
51
|
+
timers;
|
|
52
|
+
enabled = false;
|
|
53
|
+
suggestions = [];
|
|
54
|
+
index = 0;
|
|
55
|
+
timer = null;
|
|
56
|
+
controller = null;
|
|
57
|
+
revision = 0;
|
|
58
|
+
accepting = false;
|
|
59
|
+
constructor(language, timers = realTimers) {
|
|
60
|
+
this.language = language;
|
|
61
|
+
this.timers = timers;
|
|
62
|
+
const {
|
|
63
|
+
editor
|
|
64
|
+
} = language;
|
|
65
|
+
const offs = [language.onKey(event => this.key(event), KEY_PRIORITY.inlineCompletion), editor.onTextChange(change => this.edited(change)), editor.onCaret(() => queueMicrotask(() => this.moved()))];
|
|
66
|
+
language.onDispose(() => {
|
|
67
|
+
this.hide();
|
|
68
|
+
for (const off of offs) {
|
|
69
|
+
off();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
get supported() {
|
|
74
|
+
const {
|
|
75
|
+
service
|
|
76
|
+
} = this.language;
|
|
77
|
+
return service.inlineCompletions !== undefined && service.supports(METHOD, this.language.uri);
|
|
78
|
+
}
|
|
79
|
+
get isEnabled() {
|
|
80
|
+
return this.enabled;
|
|
81
|
+
}
|
|
82
|
+
/* The suggestion drawn now, or null. */
|
|
83
|
+
get current() {
|
|
84
|
+
return this.suggestions[this.index] ?? null;
|
|
85
|
+
}
|
|
86
|
+
configure(settings) {
|
|
87
|
+
this.enabled = settings.enabled === true;
|
|
88
|
+
if (!this.enabled) {
|
|
89
|
+
this.hide();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/* Asks for suggestions at the caret now, as a person asking for one does. */
|
|
93
|
+
trigger() {
|
|
94
|
+
this.timers.clear(this.timer);
|
|
95
|
+
void this.ask(1);
|
|
96
|
+
}
|
|
97
|
+
/* Takes the drawn suggestion into the text; false when there is none. */
|
|
98
|
+
accept() {
|
|
99
|
+
const suggestion = this.current;
|
|
100
|
+
if (suggestion === null) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const {
|
|
104
|
+
editor,
|
|
105
|
+
service,
|
|
106
|
+
uri
|
|
107
|
+
} = this.language;
|
|
108
|
+
this.accepting = true;
|
|
109
|
+
try {
|
|
110
|
+
editor.applyEdits([{
|
|
111
|
+
range: suggestion.range,
|
|
112
|
+
text: suggestion.text
|
|
113
|
+
}]);
|
|
114
|
+
} finally {
|
|
115
|
+
this.accepting = false;
|
|
116
|
+
}
|
|
117
|
+
this.hide();
|
|
118
|
+
const {
|
|
119
|
+
command
|
|
120
|
+
} = suggestion.item;
|
|
121
|
+
if (command !== undefined && service.supports('workspace/executeCommand', uri)) {
|
|
122
|
+
void service.executeCommand(uri, command).catch(() => undefined);
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/* Steps to the next or the previous suggestion. */
|
|
127
|
+
cycle(step) {
|
|
128
|
+
if (this.suggestions.length > 1) {
|
|
129
|
+
this.index = (this.index + step + this.suggestions.length) % this.suggestions.length;
|
|
130
|
+
this.draw();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
hide() {
|
|
134
|
+
this.revision++;
|
|
135
|
+
this.timers.clear(this.timer);
|
|
136
|
+
this.controller?.abort();
|
|
137
|
+
if (this.suggestions.length > 0) {
|
|
138
|
+
this.suggestions = [];
|
|
139
|
+
this.language.editor.setGhostText(null);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
key(event) {
|
|
143
|
+
if (isShortcut(this.language.shortcuts.suggestInline, event) && this.enabled) {
|
|
144
|
+
this.trigger();
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (this.current === null || event.isComposing) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (event.key === 'Tab' && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
|
151
|
+
return this.accept();
|
|
152
|
+
}
|
|
153
|
+
if (event.key === 'Escape') {
|
|
154
|
+
this.hide();
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
if (event.altKey && !event.ctrlKey && !event.metaKey && (event.code === 'BracketRight' || event.code === 'BracketLeft')) {
|
|
158
|
+
this.cycle(event.code === 'BracketRight' ? 1 : -1);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
edited(change) {
|
|
164
|
+
if (this.accepting) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.hide();
|
|
168
|
+
if (this.enabled && change.source === 'input' && this.supported) {
|
|
169
|
+
this.timer = this.timers.set(() => void this.ask(2), DELAY_MS);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/* A caret that left the place of the suggestion puts it away. */
|
|
173
|
+
moved() {
|
|
174
|
+
const suggestion = this.current;
|
|
175
|
+
if (suggestion === null) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const caret = this.language.editor.getCaret();
|
|
179
|
+
const selections = this.language.editor.getSelections();
|
|
180
|
+
if (selections.length !== 1 || suggestionOf(suggestion.item, caret, range => this.language.editor.textInRange(range)) === null) {
|
|
181
|
+
this.hide();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async ask(triggerKind) {
|
|
185
|
+
const {
|
|
186
|
+
editor,
|
|
187
|
+
service,
|
|
188
|
+
uri
|
|
189
|
+
} = this.language;
|
|
190
|
+
if (!this.enabled || !this.supported || editor.getSelections().length !== 1 || editor.isReadOnly?.() === true) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
this.controller?.abort();
|
|
194
|
+
const controller = new AbortController();
|
|
195
|
+
this.controller = controller;
|
|
196
|
+
const revision = this.revision;
|
|
197
|
+
const caret = editor.getCaret();
|
|
198
|
+
try {
|
|
199
|
+
const result = await service.inlineCompletions(uri, caret, {
|
|
200
|
+
triggerKind
|
|
201
|
+
}, {
|
|
202
|
+
signal: controller.signal
|
|
203
|
+
});
|
|
204
|
+
if (revision !== this.revision || comparePositions(editor.getCaret(), caret) !== 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.suggestions = itemsOf(result).flatMap(item => suggestionOf(item, caret, range => editor.textInRange(range)) ?? []);
|
|
208
|
+
this.index = 0;
|
|
209
|
+
this.draw();
|
|
210
|
+
} catch {
|
|
211
|
+
// A refusal or a failing server is no suggestion; the next pause asks again.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
draw() {
|
|
215
|
+
const suggestion = this.current;
|
|
216
|
+
this.language.editor.setGhostText(suggestion === null ? null : {
|
|
217
|
+
position: this.language.editor.getCaret(),
|
|
218
|
+
text: suggestion.rest
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
package/dist/key-priority.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { EditorKeyHandler } from '@adecore/editor';
|
|
2
2
|
export declare const KEY_PRIORITY: {
|
|
3
|
-
readonly pick:
|
|
4
|
-
readonly diagnostics:
|
|
5
|
-
readonly hover:
|
|
6
|
-
readonly completion:
|
|
7
|
-
readonly snippets:
|
|
3
|
+
readonly pick: 1600;
|
|
4
|
+
readonly diagnostics: 1500;
|
|
5
|
+
readonly hover: 1400;
|
|
6
|
+
readonly completion: 1300;
|
|
7
|
+
readonly snippets: 1200;
|
|
8
|
+
readonly markup: 1100;
|
|
8
9
|
readonly signature: 1000;
|
|
9
10
|
readonly highlights: 900;
|
|
10
11
|
readonly codeActions: 800;
|
|
@@ -15,6 +16,8 @@ export declare const KEY_PRIORITY: {
|
|
|
15
16
|
readonly symbolPicker: 300;
|
|
16
17
|
readonly history: 200;
|
|
17
18
|
readonly renderedDocs: 100;
|
|
19
|
+
readonly pasteHistory: 50;
|
|
20
|
+
readonly inlineCompletion: 25;
|
|
18
21
|
};
|
|
19
22
|
export declare class KeyDispatcher {
|
|
20
23
|
private readonly entries;
|
package/dist/key-priority.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* apart, so a handler of the app can stand between two features; 0, the default, comes after all of them.
|
|
4
4
|
*/
|
|
5
5
|
export const KEY_PRIORITY = {
|
|
6
|
-
pick:
|
|
7
|
-
diagnostics:
|
|
8
|
-
hover:
|
|
9
|
-
completion:
|
|
10
|
-
snippets:
|
|
6
|
+
pick: 1600,
|
|
7
|
+
diagnostics: 1500,
|
|
8
|
+
hover: 1400,
|
|
9
|
+
completion: 1300,
|
|
10
|
+
snippets: 1200,
|
|
11
|
+
markup: 1100,
|
|
11
12
|
signature: 1000,
|
|
12
13
|
highlights: 900,
|
|
13
14
|
codeActions: 800,
|
|
@@ -17,7 +18,9 @@ export const KEY_PRIORITY = {
|
|
|
17
18
|
callHierarchy: 400,
|
|
18
19
|
symbolPicker: 300,
|
|
19
20
|
history: 200,
|
|
20
|
-
renderedDocs: 100
|
|
21
|
+
renderedDocs: 100,
|
|
22
|
+
pasteHistory: 50,
|
|
23
|
+
inlineCompletion: 25
|
|
21
24
|
};
|
|
22
25
|
/* Hands a key to its handlers by priority until one takes it; of two of the same priority the one added first. */
|
|
23
26
|
export class KeyDispatcher {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LinkedEditingRanges } from '@adecore/lsp';
|
|
2
|
+
import type { EditorLanguage } from './editor-language.ts';
|
|
3
|
+
import { type Timers } from './timers.ts';
|
|
4
|
+
export declare function wordPatternOf(ranges: LinkedEditingRanges): RegExp | null;
|
|
5
|
+
export declare class LinkedEditingFeature {
|
|
6
|
+
private readonly language;
|
|
7
|
+
private readonly timers;
|
|
8
|
+
private session;
|
|
9
|
+
private timer;
|
|
10
|
+
private controller;
|
|
11
|
+
private revision;
|
|
12
|
+
private mirroring;
|
|
13
|
+
private enabled;
|
|
14
|
+
constructor(language: EditorLanguage, timers?: Timers);
|
|
15
|
+
get supported(): boolean;
|
|
16
|
+
get isActive(): boolean;
|
|
17
|
+
configure(settings: {
|
|
18
|
+
readonly enabled?: boolean;
|
|
19
|
+
}): void;
|
|
20
|
+
private stop;
|
|
21
|
+
private caretOffset;
|
|
22
|
+
private moved;
|
|
23
|
+
private ask;
|
|
24
|
+
private edited;
|
|
25
|
+
private mirror;
|
|
26
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { realTimers } from './timers.js';
|
|
2
|
+
const METHOD = 'textDocument/linkedEditingRange';
|
|
3
|
+
/* How long the caret rests before the ranges around it are asked. */
|
|
4
|
+
const DELAY_MS = 100;
|
|
5
|
+
/* The server's word pattern, anchored, or null when it gave none or one that does not compile. */
|
|
6
|
+
export function wordPatternOf(ranges) {
|
|
7
|
+
if (ranges.wordPattern === undefined) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
return new RegExp(`^(?:${ranges.wordPattern})$`, 'u');
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/*
|
|
17
|
+
* Ranges a server says are edited together, such as the names of a start tag and its end tag: a change inside
|
|
18
|
+
* one goes into every other, in the undo step of the key. The ranges are asked once the caret rests, and the link
|
|
19
|
+
* ends when an edit falls outside them, the text stops matching the server's word pattern, or there is more
|
|
20
|
+
* than one caret.
|
|
21
|
+
*/
|
|
22
|
+
export class LinkedEditingFeature {
|
|
23
|
+
language;
|
|
24
|
+
timers;
|
|
25
|
+
session = null;
|
|
26
|
+
timer = null;
|
|
27
|
+
controller = null;
|
|
28
|
+
revision = 0;
|
|
29
|
+
mirroring = false;
|
|
30
|
+
enabled = true;
|
|
31
|
+
constructor(language, timers = realTimers) {
|
|
32
|
+
this.language = language;
|
|
33
|
+
this.timers = timers;
|
|
34
|
+
const {
|
|
35
|
+
editor
|
|
36
|
+
} = language;
|
|
37
|
+
const offs = [editor.onTextChange(change => this.edited(change)), editor.onCaret(() => this.moved())];
|
|
38
|
+
language.onDispose(() => {
|
|
39
|
+
this.stop();
|
|
40
|
+
for (const off of offs) {
|
|
41
|
+
off();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
get supported() {
|
|
46
|
+
const {
|
|
47
|
+
service
|
|
48
|
+
} = this.language;
|
|
49
|
+
return this.enabled && service.linkedEditingRanges !== undefined && service.supports(METHOD, this.language.uri);
|
|
50
|
+
}
|
|
51
|
+
/* Whether ranges are linked now. */
|
|
52
|
+
get isActive() {
|
|
53
|
+
return this.session !== null;
|
|
54
|
+
}
|
|
55
|
+
/* Off, nothing is linked; on unless false. */
|
|
56
|
+
configure(settings) {
|
|
57
|
+
this.enabled = settings.enabled !== false;
|
|
58
|
+
if (!this.enabled) {
|
|
59
|
+
this.stop();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
stop() {
|
|
63
|
+
this.session = null;
|
|
64
|
+
this.timers.clear(this.timer);
|
|
65
|
+
this.controller?.abort();
|
|
66
|
+
}
|
|
67
|
+
caretOffset() {
|
|
68
|
+
const {
|
|
69
|
+
editor
|
|
70
|
+
} = this.language;
|
|
71
|
+
const selections = editor.getSelections();
|
|
72
|
+
if (selections.length !== 1) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const {
|
|
76
|
+
start,
|
|
77
|
+
end
|
|
78
|
+
} = selections[0];
|
|
79
|
+
return start.line === end.line && start.character === end.character ? editor.offsetAt(start) : null;
|
|
80
|
+
}
|
|
81
|
+
moved() {
|
|
82
|
+
if (this.mirroring) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// The caret moves before the text change of the same key is told, so the session is checked once both are in.
|
|
86
|
+
queueMicrotask(() => {
|
|
87
|
+
const offset = this.caretOffset();
|
|
88
|
+
if (offset !== null && this.session?.spans.some(span => span.from <= offset && offset <= span.to)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.stop();
|
|
92
|
+
if (offset !== null && this.supported) {
|
|
93
|
+
this.timer = this.timers.set(() => void this.ask(offset), DELAY_MS);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async ask(offset) {
|
|
98
|
+
const {
|
|
99
|
+
editor,
|
|
100
|
+
service,
|
|
101
|
+
uri
|
|
102
|
+
} = this.language;
|
|
103
|
+
const revision = this.revision;
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
this.controller = controller;
|
|
106
|
+
try {
|
|
107
|
+
const ranges = await service.linkedEditingRanges(uri, editor.positionAt(offset), {
|
|
108
|
+
signal: controller.signal
|
|
109
|
+
});
|
|
110
|
+
if (ranges === null || ranges.ranges.length < 2 || revision !== this.revision || this.caretOffset() !== offset) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.session = {
|
|
114
|
+
spans: ranges.ranges.map(range => ({
|
|
115
|
+
from: editor.offsetAt(range.start),
|
|
116
|
+
to: editor.offsetAt(range.end)
|
|
117
|
+
})),
|
|
118
|
+
pattern: wordPatternOf(ranges)
|
|
119
|
+
};
|
|
120
|
+
} catch {
|
|
121
|
+
// No ranges is no link; the next rest of the caret asks again.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
edited(change) {
|
|
125
|
+
const revision = ++this.revision;
|
|
126
|
+
const session = this.session;
|
|
127
|
+
if (this.mirroring || session === null) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const edit = change.changes[0];
|
|
131
|
+
if (change.source === 'external' || change.changes.length !== 1 || edit === undefined || edit.range.start.line !== edit.range.end.line) {
|
|
132
|
+
this.stop();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const from = this.language.editor.offsetAt(edit.range.start);
|
|
136
|
+
const removed = edit.range.end.character - edit.range.start.character;
|
|
137
|
+
const own = session.spans.findIndex(span => span.from <= from && from + removed <= span.to);
|
|
138
|
+
if (own === -1) {
|
|
139
|
+
this.stop();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const delta = edit.text.length - removed;
|
|
143
|
+
const edited = session.spans[own];
|
|
144
|
+
session.spans = session.spans.map((span, index) => index === own ? {
|
|
145
|
+
from: span.from,
|
|
146
|
+
to: span.to + delta
|
|
147
|
+
} : span.from > edited.from ? {
|
|
148
|
+
from: span.from + delta,
|
|
149
|
+
to: span.to + delta
|
|
150
|
+
} : span);
|
|
151
|
+
queueMicrotask(() => this.mirror(own, revision));
|
|
152
|
+
}
|
|
153
|
+
mirror(own, revision) {
|
|
154
|
+
const session = this.session;
|
|
155
|
+
if (session === null || revision !== this.revision) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const {
|
|
159
|
+
editor
|
|
160
|
+
} = this.language;
|
|
161
|
+
const text = editor.getText();
|
|
162
|
+
const source = session.spans[own];
|
|
163
|
+
const value = text.slice(source.from, source.to);
|
|
164
|
+
if (session.pattern !== null && value !== '' && !session.pattern.test(value)) {
|
|
165
|
+
this.stop();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const others = session.spans.map((span, index) => ({
|
|
169
|
+
span,
|
|
170
|
+
index
|
|
171
|
+
})).filter(({
|
|
172
|
+
span,
|
|
173
|
+
index
|
|
174
|
+
}) => index !== own && text.slice(span.from, span.to) !== value);
|
|
175
|
+
if (others.length === 0) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this.mirroring = true;
|
|
179
|
+
try {
|
|
180
|
+
const applied = editor.applyEdits(others.map(({
|
|
181
|
+
span
|
|
182
|
+
}) => ({
|
|
183
|
+
range: {
|
|
184
|
+
start: editor.positionAt(span.from),
|
|
185
|
+
end: editor.positionAt(span.to)
|
|
186
|
+
},
|
|
187
|
+
text: value
|
|
188
|
+
})), {
|
|
189
|
+
mergeUndo: true
|
|
190
|
+
});
|
|
191
|
+
if (!applied) {
|
|
192
|
+
this.stop();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
} finally {
|
|
196
|
+
this.mirroring = false;
|
|
197
|
+
}
|
|
198
|
+
// Each replaced span moves every span after it by what its length changed.
|
|
199
|
+
const changed = others.map(({
|
|
200
|
+
span
|
|
201
|
+
}) => ({
|
|
202
|
+
at: span.from,
|
|
203
|
+
delta: value.length - (span.to - span.from)
|
|
204
|
+
})).sort((left, right) => left.at - right.at);
|
|
205
|
+
const shift = offset => offset + changed.filter(entry => entry.at < offset).reduce((total, entry) => total + entry.delta, 0);
|
|
206
|
+
session.spans = session.spans.map((span, index) => {
|
|
207
|
+
const from = shift(span.from);
|
|
208
|
+
const replaced = others.some(other => other.index === index);
|
|
209
|
+
return {
|
|
210
|
+
from,
|
|
211
|
+
to: replaced ? from + value.length : shift(span.to)
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
package/dist/locales/en.json
CHANGED
|
@@ -70,6 +70,12 @@
|
|
|
70
70
|
"delete": "Deletes"
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
+
"markup": {
|
|
74
|
+
"wrap": "Wrap with abbreviation",
|
|
75
|
+
"placeholder": "Abbreviation, such as div.card>p",
|
|
76
|
+
"apply": "Wrap",
|
|
77
|
+
"invalid": "Not an abbreviation"
|
|
78
|
+
},
|
|
73
79
|
"navigation": {
|
|
74
80
|
"unavailable": "Not available here",
|
|
75
81
|
"none": "Nothing found",
|
|
@@ -119,13 +125,30 @@
|
|
|
119
125
|
"site": "Call {{current}} of {{total}}",
|
|
120
126
|
"previousSite": "Previous call",
|
|
121
127
|
"nextSite": "Next call",
|
|
128
|
+
"typesUnavailable": "Type hierarchy unavailable in this file",
|
|
129
|
+
"typesNone": "No type here",
|
|
130
|
+
"typesFailed": "Could not look up types: {{message}}",
|
|
131
|
+
"typesLabel": "Type hierarchy",
|
|
132
|
+
"supertypes": "Supertypes",
|
|
133
|
+
"subtypes": "Subtypes",
|
|
134
|
+
"noSupertypes": "No supertypes",
|
|
135
|
+
"noSubtypes": "No subtypes",
|
|
122
136
|
"announce": {
|
|
123
137
|
"incoming_one": "{{count}} caller of {{name}}",
|
|
124
138
|
"incoming_other": "{{count}} callers of {{name}}",
|
|
125
139
|
"outgoing_one": "{{count}} callee of {{name}}",
|
|
126
|
-
"outgoing_other": "{{count}} callees of {{name}}"
|
|
140
|
+
"outgoing_other": "{{count}} callees of {{name}}",
|
|
141
|
+
"supertypes_one": "{{count}} supertype of {{name}}",
|
|
142
|
+
"supertypes_other": "{{count}} supertypes of {{name}}",
|
|
143
|
+
"subtypes_one": "{{count}} subtype of {{name}}",
|
|
144
|
+
"subtypes_other": "{{count}} subtypes of {{name}}"
|
|
127
145
|
}
|
|
128
146
|
},
|
|
147
|
+
"colors": {
|
|
148
|
+
"swatch": "Color {{color}}",
|
|
149
|
+
"title": "Color",
|
|
150
|
+
"other": "Pick another color…"
|
|
151
|
+
},
|
|
129
152
|
"codeVision": {
|
|
130
153
|
"usagesNone": "No usages",
|
|
131
154
|
"usages_one": "{{formatted}} usage",
|
|
@@ -192,6 +215,10 @@
|
|
|
192
215
|
"history": {
|
|
193
216
|
"recent": "Recent locations"
|
|
194
217
|
},
|
|
218
|
+
"pasteHistory": {
|
|
219
|
+
"title": "Choose Content to Paste",
|
|
220
|
+
"empty": "Nothing was copied yet"
|
|
221
|
+
},
|
|
195
222
|
"announce": {
|
|
196
223
|
"problem": "{{severity}}: {{message}}",
|
|
197
224
|
"severity": {
|
|
@@ -236,10 +263,12 @@
|
|
|
236
263
|
"goToTypeDefinition": "Go to type definition",
|
|
237
264
|
"goToImplementation": "Go to implementation",
|
|
238
265
|
"callHierarchy": "Call hierarchy",
|
|
266
|
+
"typeHierarchy": "Type hierarchy",
|
|
239
267
|
"peekReferences": "Peek references",
|
|
240
268
|
"renameSymbol": "Rename symbol",
|
|
241
269
|
"codeActions": "Code actions",
|
|
242
|
-
"formatDocument": "Format document"
|
|
270
|
+
"formatDocument": "Format document",
|
|
271
|
+
"formatSelection": "Format selection"
|
|
243
272
|
},
|
|
244
273
|
"find": {
|
|
245
274
|
"label": "Find",
|
package/dist/locales/nl.json
CHANGED
|
@@ -70,6 +70,12 @@
|
|
|
70
70
|
"delete": "Verwijdert"
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
+
"markup": {
|
|
74
|
+
"wrap": "Omwikkelen met afkorting",
|
|
75
|
+
"placeholder": "Afkorting, zoals div.card>p",
|
|
76
|
+
"apply": "Omwikkelen",
|
|
77
|
+
"invalid": "Geen geldige afkorting"
|
|
78
|
+
},
|
|
73
79
|
"navigation": {
|
|
74
80
|
"unavailable": "Hier niet beschikbaar",
|
|
75
81
|
"none": "Niets gevonden",
|
|
@@ -119,13 +125,30 @@
|
|
|
119
125
|
"site": "Aanroep {{current}} van {{total}}",
|
|
120
126
|
"previousSite": "Vorige aanroep",
|
|
121
127
|
"nextSite": "Volgende aanroep",
|
|
128
|
+
"typesUnavailable": "Typehiërarchie niet beschikbaar in dit bestand",
|
|
129
|
+
"typesNone": "Hier geen type",
|
|
130
|
+
"typesFailed": "Kon de types niet opzoeken: {{message}}",
|
|
131
|
+
"typesLabel": "Typehiërarchie",
|
|
132
|
+
"supertypes": "Supertypes",
|
|
133
|
+
"subtypes": "Subtypes",
|
|
134
|
+
"noSupertypes": "Geen supertypes",
|
|
135
|
+
"noSubtypes": "Geen subtypes",
|
|
122
136
|
"announce": {
|
|
123
137
|
"incoming_one": "{{count}} aanroeper van {{name}}",
|
|
124
138
|
"incoming_other": "{{count}} aanroepers van {{name}}",
|
|
125
139
|
"outgoing_one": "{{count}} aangeroepen door {{name}}",
|
|
126
|
-
"outgoing_other": "{{count}} aangeroepen door {{name}}"
|
|
140
|
+
"outgoing_other": "{{count}} aangeroepen door {{name}}",
|
|
141
|
+
"supertypes_one": "{{count}} supertype van {{name}}",
|
|
142
|
+
"supertypes_other": "{{count}} supertypes van {{name}}",
|
|
143
|
+
"subtypes_one": "{{count}} subtype van {{name}}",
|
|
144
|
+
"subtypes_other": "{{count}} subtypes van {{name}}"
|
|
127
145
|
}
|
|
128
146
|
},
|
|
147
|
+
"colors": {
|
|
148
|
+
"swatch": "Kleur {{color}}",
|
|
149
|
+
"title": "Kleur",
|
|
150
|
+
"other": "Andere kleur kiezen…"
|
|
151
|
+
},
|
|
129
152
|
"codeVision": {
|
|
130
153
|
"usagesNone": "Niet gebruikt",
|
|
131
154
|
"usages_one": "{{formatted}} keer gebruikt",
|
|
@@ -192,6 +215,10 @@
|
|
|
192
215
|
"history": {
|
|
193
216
|
"recent": "Recente plekken"
|
|
194
217
|
},
|
|
218
|
+
"pasteHistory": {
|
|
219
|
+
"title": "Kies wat je plakt",
|
|
220
|
+
"empty": "Er is nog niets gekopieerd"
|
|
221
|
+
},
|
|
195
222
|
"announce": {
|
|
196
223
|
"problem": "{{severity}}: {{message}}",
|
|
197
224
|
"severity": {
|
|
@@ -236,10 +263,12 @@
|
|
|
236
263
|
"goToTypeDefinition": "Ga naar typedefinitie",
|
|
237
264
|
"goToImplementation": "Ga naar implementatie",
|
|
238
265
|
"callHierarchy": "Aanroephiërarchie",
|
|
266
|
+
"typeHierarchy": "Typehiërarchie",
|
|
239
267
|
"peekReferences": "Bekijk referenties",
|
|
240
268
|
"renameSymbol": "Symbool hernoemen",
|
|
241
269
|
"codeActions": "Codeacties",
|
|
242
|
-
"formatDocument": "Document formatteren"
|
|
270
|
+
"formatDocument": "Document formatteren",
|
|
271
|
+
"formatSelection": "Selectie formatteren"
|
|
243
272
|
},
|
|
244
273
|
"find": {
|
|
245
274
|
"label": "Zoeken",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type MarkupSyntax = 'html' | 'xml' | 'vue' | 'php';
|
|
2
|
+
export interface OpenElement {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly nameFrom: number;
|
|
5
|
+
}
|
|
6
|
+
export interface TagAt {
|
|
7
|
+
readonly start: number;
|
|
8
|
+
readonly closing: boolean;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly nameFrom: number;
|
|
11
|
+
readonly nameTo: number;
|
|
12
|
+
readonly inValue: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type MarkupPlace = {
|
|
15
|
+
readonly kind: 'text';
|
|
16
|
+
readonly open: readonly OpenElement[];
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'tag';
|
|
19
|
+
readonly tag: TagAt;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'other';
|
|
22
|
+
};
|
|
23
|
+
export interface TagPair {
|
|
24
|
+
readonly open: {
|
|
25
|
+
readonly from: number;
|
|
26
|
+
readonly to: number;
|
|
27
|
+
};
|
|
28
|
+
readonly close: {
|
|
29
|
+
readonly from: number;
|
|
30
|
+
readonly to: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare const MAX_MARKUP_SCAN = 1000000;
|
|
34
|
+
export declare function markupSyntaxOf(languageId: string): MarkupSyntax | null;
|
|
35
|
+
export declare function isVoidElement(name: string, syntax: MarkupSyntax): boolean;
|
|
36
|
+
export declare function markupPlaceAt(text: string, offset: number, syntax: MarkupSyntax): MarkupPlace | null;
|
|
37
|
+
export declare function tagPairAt(text: string, offset: number, syntax: MarkupSyntax): TagPair | null;
|