@adecore/editor-react 0.22.1 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CallHierarchyPanel.d.ts +6 -0
- package/dist/CallHierarchyPanel.js +92 -0
- package/dist/CompletionPopup.js +11 -3
- package/dist/DiffView.d.ts +22 -0
- package/dist/DiffView.js +203 -0
- package/dist/EditorContextMenu.js +1 -0
- package/dist/EditorView.js +18 -1
- package/dist/FindReplace.d.ts +1 -1
- package/dist/FindReplace.js +41 -11
- package/dist/HoverCard.js +2 -2
- package/dist/LanguagePopups.js +4 -1
- package/dist/Markdown.d.ts +2 -1
- package/dist/Markdown.js +8 -4
- package/dist/PanelResizeHandle.d.ts +8 -0
- package/dist/PanelResizeHandle.js +71 -0
- package/dist/PeekPanel.d.ts +3 -0
- package/dist/PeekPanel.js +7 -4
- package/dist/PickPopup.js +15 -2
- package/dist/RenameCard.js +10 -5
- package/dist/RenderedDocs.d.ts +9 -0
- package/dist/RenderedDocs.js +84 -0
- package/dist/SplitDiffView.d.ts +9 -0
- package/dist/SplitDiffView.js +276 -0
- package/dist/SymbolPicker.d.ts +3 -0
- package/dist/SymbolPicker.js +4 -3
- package/dist/call-hierarchy-model.d.ts +52 -0
- package/dist/call-hierarchy-model.js +93 -0
- package/dist/call-hierarchy.d.ts +42 -0
- package/dist/call-hierarchy.js +398 -0
- package/dist/code-actions-model.d.ts +2 -0
- package/dist/code-actions-model.js +12 -10
- package/dist/code-actions.js +3 -1
- package/dist/completion-model.d.ts +14 -4
- package/dist/completion-model.js +40 -33
- package/dist/completion.d.ts +4 -0
- package/dist/completion.js +257 -42
- package/dist/diagnostics.js +8 -6
- package/dist/diff-expansion.d.ts +9 -0
- package/dist/diff-expansion.js +41 -0
- package/dist/diff-overlays.d.ts +15 -0
- package/dist/diff-overlays.js +28 -0
- package/dist/diff-syntax.d.ts +16 -0
- package/dist/diff-syntax.js +49 -0
- package/dist/diff-wordmarks.d.ts +5 -0
- package/dist/diff-wordmarks.js +26 -0
- package/dist/doc-comment-model.d.ts +23 -0
- package/dist/doc-comment-model.js +291 -0
- package/dist/editor-language.d.ts +13 -1
- package/dist/editor-language.js +15 -5
- package/dist/editor-react.css +79 -0
- package/dist/format-on-type.d.ts +14 -0
- package/dist/format-on-type.js +96 -0
- package/dist/host-types.d.ts +19 -0
- package/dist/hover.d.ts +4 -0
- package/dist/hover.js +15 -2
- package/dist/index.d.ts +19 -5
- package/dist/index.js +9 -1
- package/dist/inlay-hints.d.ts +47 -2
- package/dist/inlay-hints.js +353 -16
- package/dist/language-ids.js +4 -2
- package/dist/locales/en.json +117 -3
- package/dist/locales/nl.json +117 -3
- package/dist/models.d.ts +6 -2
- package/dist/models.js +5 -1
- package/dist/panel-size.d.ts +6 -0
- package/dist/panel-size.js +25 -0
- package/dist/patch-decorations.d.ts +27 -0
- package/dist/patch-decorations.js +182 -0
- package/dist/patch-layout.d.ts +40 -0
- package/dist/patch-layout.js +304 -0
- package/dist/peek.d.ts +3 -1
- package/dist/peek.js +6 -1
- package/dist/pick.d.ts +1 -0
- package/dist/pick.js +11 -1
- package/dist/popups.d.ts +17 -0
- package/dist/popups.js +1 -0
- package/dist/project-language.d.ts +12 -0
- package/dist/project-language.js +52 -2
- package/dist/refresher.d.ts +1 -0
- package/dist/refresher.js +5 -0
- package/dist/rename-model.d.ts +1 -1
- package/dist/rename-model.js +3 -7
- package/dist/rename.js +5 -8
- package/dist/rendered-docs.d.ts +64 -0
- package/dist/rendered-docs.js +374 -0
- package/dist/row-host.d.ts +2 -0
- package/dist/row-host.js +11 -3
- package/dist/semantic-delta.d.ts +2 -0
- package/dist/semantic-delta.js +27 -0
- package/dist/semantic-model.js +16 -0
- package/dist/semantic-tokens.d.ts +26 -1
- package/dist/semantic-tokens.js +174 -14
- package/dist/shortcut-keys.d.ts +2 -0
- package/dist/shortcut-keys.js +6 -2
- package/dist/signature.js +1 -1
- package/dist/snippet-field-transforms.d.ts +30 -0
- package/dist/snippet-field-transforms.js +116 -0
- package/dist/snippet-indent.d.ts +2 -0
- package/dist/snippet-indent.js +49 -0
- package/dist/snippet-range-order.d.ts +3 -0
- package/dist/snippet-range-order.js +19 -0
- package/dist/snippet-session.d.ts +42 -6
- package/dist/snippet-session.js +650 -53
- package/dist/snippet-transform-client.d.ts +19 -0
- package/dist/snippet-transform-client.js +66 -0
- package/dist/snippet-transform-format.d.ts +15 -0
- package/dist/snippet-transform-format.js +302 -0
- package/dist/snippet-transform-protocol.d.ts +45 -0
- package/dist/snippet-transform-protocol.js +8 -0
- package/dist/snippet-transform-queue.d.ts +32 -0
- package/dist/snippet-transform-queue.js +173 -0
- package/dist/snippet-transform-worker.d.ts +10 -0
- package/dist/snippet-transform-worker.js +41 -0
- package/dist/snippet-variable-names.d.ts +1 -0
- package/dist/snippet-variable-names.js +36 -0
- package/dist/snippet-variables.d.ts +7 -0
- package/dist/snippet-variables.js +66 -0
- package/dist/snippet.d.ts +30 -3
- package/dist/snippet.js +402 -87
- package/dist/split-alignment.d.ts +6 -0
- package/dist/split-alignment.js +38 -0
- package/dist/split-scroll.d.ts +14 -0
- package/dist/split-scroll.js +45 -0
- package/dist/symbols.d.ts +2 -1
- package/dist/symbols.js +39 -7
- package/dist/testing.d.ts +5 -1
- package/dist/testing.js +12 -0
- package/dist/timers.d.ts +1 -0
- package/dist/timers.js +4 -0
- package/dist/use-panel-body.d.ts +6 -0
- package/dist/use-panel-body.js +13 -0
- package/dist/workspace-edit.d.ts +25 -3
- package/dist/workspace-edit.js +282 -143
- package/package.json +10 -5
- package/src/CallHierarchyPanel.tsx +244 -0
- package/src/CompletionPopup.tsx +12 -1
- package/src/DiffView.tsx +262 -0
- package/src/EditorContextMenu.tsx +1 -0
- package/src/EditorView.tsx +21 -2
- package/src/FindReplace.tsx +51 -13
- package/src/HoverCard.tsx +23 -21
- package/src/LanguagePopups.tsx +5 -0
- package/src/Markdown.tsx +10 -3
- package/src/PanelResizeHandle.tsx +104 -0
- package/src/PeekPanel.tsx +11 -6
- package/src/PickPopup.tsx +18 -2
- package/src/RenameCard.tsx +18 -0
- package/src/RenderedDocs.tsx +155 -0
- package/src/SplitDiffView.tsx +337 -0
- package/src/SymbolPicker.tsx +4 -3
- package/src/call-hierarchy-model.ts +151 -0
- package/src/call-hierarchy.ts +440 -0
- package/src/code-actions-model.ts +17 -11
- package/src/code-actions.ts +3 -1
- package/src/completion-model.ts +61 -35
- package/src/completion.ts +302 -42
- package/src/diagnostics.ts +8 -6
- package/src/diff-expansion.ts +49 -0
- package/src/diff-overlays.tsx +69 -0
- package/src/diff-syntax.ts +67 -0
- package/src/diff-wordmarks.ts +31 -0
- package/src/doc-comment-model.ts +336 -0
- package/src/editor-language.ts +22 -6
- package/src/editor-react.css +79 -0
- package/src/format-on-type.ts +111 -0
- package/src/host-types.ts +23 -0
- package/src/hover.ts +18 -3
- package/src/index.ts +27 -4
- package/src/inlay-hints.ts +394 -18
- package/src/language-ids.ts +4 -2
- package/src/locales/en.json +117 -3
- package/src/locales/nl.json +117 -3
- package/src/models.ts +40 -1
- package/src/panel-size.ts +28 -0
- package/src/patch-decorations.ts +224 -0
- package/src/patch-layout.ts +388 -0
- package/src/peek.ts +6 -1
- package/src/pick.ts +12 -1
- package/src/popups.ts +24 -0
- package/src/project-language.ts +63 -2
- package/src/refresher.ts +6 -0
- package/src/rename-model.ts +4 -8
- package/src/rename.ts +5 -8
- package/src/rendered-docs.ts +430 -0
- package/src/row-host.ts +12 -3
- package/src/semantic-delta.ts +29 -0
- package/src/semantic-model.ts +17 -0
- package/src/semantic-tokens.ts +202 -19
- package/src/shortcut-keys.ts +6 -2
- package/src/signature.ts +1 -1
- package/src/snippet-field-transforms.ts +144 -0
- package/src/snippet-indent.ts +53 -0
- package/src/snippet-range-order.ts +19 -0
- package/src/snippet-session.ts +797 -56
- package/src/snippet-transform-client.ts +84 -0
- package/src/snippet-transform-format.ts +325 -0
- package/src/snippet-transform-protocol.ts +24 -0
- package/src/snippet-transform-queue.ts +199 -0
- package/src/snippet-transform-worker.ts +58 -0
- package/src/snippet-variable-names.ts +36 -0
- package/src/snippet-variables.ts +74 -0
- package/src/snippet.ts +452 -89
- package/src/split-alignment.ts +45 -0
- package/src/split-scroll.ts +54 -0
- package/src/symbols.ts +43 -7
- package/src/testing.ts +25 -0
- package/src/timers.ts +5 -0
- package/src/use-panel-body.ts +17 -0
- package/src/workspace-edit.ts +347 -169
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { SnippetTransformJob } from './snippet-transform-protocol.ts';
|
|
1
2
|
import type { EditorContentChange, EditorPosition, EditorRange } from '@adecore/editor';
|
|
2
|
-
import type
|
|
3
|
+
import { type CompletionItem, type CompletionResult, type MarkupContent } from '@adecore/lsp';
|
|
3
4
|
import type { CompletionDocs } from './popups.ts';
|
|
4
|
-
import { type SnippetStop } from './snippet.ts';
|
|
5
|
+
import { type SnippetField, type SnippetStop, type SnippetTemplate, type SnippetVariableResolver } from './snippet.ts';
|
|
5
6
|
export declare function isIdentifierCharacter(character: string): boolean;
|
|
6
7
|
export declare function identifierPrefix(before: string): string;
|
|
7
8
|
export declare function opensCompletion(before: string, text: string): boolean;
|
|
@@ -34,12 +35,21 @@ export declare const CLASS_KIND = 7;
|
|
|
34
35
|
export declare const SNIPPET_FORMAT = 2;
|
|
35
36
|
export declare function isCallableKind(kind: number | undefined): boolean;
|
|
36
37
|
export interface Insertion {
|
|
38
|
+
readonly limited?: true;
|
|
37
39
|
readonly range: EditorRange;
|
|
38
40
|
readonly text: string;
|
|
39
41
|
readonly stops: readonly SnippetStop[];
|
|
42
|
+
readonly fields?: readonly SnippetField[];
|
|
43
|
+
readonly variableTransforms?: readonly SnippetTransformJob[];
|
|
40
44
|
}
|
|
41
|
-
export
|
|
42
|
-
|
|
45
|
+
export interface SnippetInsertionOptions {
|
|
46
|
+
readonly resolve?: SnippetVariableResolver;
|
|
47
|
+
readonly variableTransforms?: readonly string[];
|
|
48
|
+
readonly template?: SnippetTemplate;
|
|
49
|
+
readonly indentationBefore?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function insertionOf(item: CompletionItem, caret: EditorPosition, lineBefore: string, replace: boolean, lineAfter?: string, options?: SnippetInsertionOptions): Insertion;
|
|
52
|
+
export declare function mirroredInsertions(carets: readonly EditorRange[], primary: EditorRange, caret: EditorPosition, text: (range: EditorRange) => string, insertion: Insertion, includeSelections?: boolean): EditorContentChange[];
|
|
43
53
|
export declare function documentationText(documentation: string | MarkupContent | undefined): string;
|
|
44
54
|
export declare const SOURCE_LINE: RegExp;
|
|
45
55
|
export declare function completionDocsOf(item: CompletionItem, highlightLanguage: string): CompletionDocs | null;
|
package/dist/completion-model.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { applyCompletionDefaults } from '@adecore/lsp';
|
|
1
2
|
import { comparePositions } from './diagnostics-model.js';
|
|
2
3
|
import { escapeMarkdown, isEmptyHover, splitSignatures } from './hover-content.js';
|
|
3
|
-
import { parseSnippet, tabOrder } from './snippet.js';
|
|
4
|
+
import { compileSnippet, parseSnippet, tabOrder } from './snippet.js';
|
|
5
|
+
import { indentSnippet } from './snippet-indent.js';
|
|
4
6
|
const IDENTIFIER_CHARACTER = /[\p{L}\p{N}\p{M}_$]/u;
|
|
5
7
|
export function isIdentifierCharacter(character) {
|
|
6
8
|
return IDENTIFIER_CHARACTER.test(character);
|
|
@@ -117,35 +119,23 @@ export function editRangeOf(item, replace) {
|
|
|
117
119
|
}
|
|
118
120
|
/* Items from a result, with the list's defaults filled in so each carries its own edit range and format. */
|
|
119
121
|
export function itemsOf(result) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
if (Array.isArray(result)) {
|
|
124
|
-
return { items: result, incomplete: false };
|
|
125
|
-
}
|
|
126
|
-
const list = result;
|
|
127
|
-
const defaults = list.itemDefaults;
|
|
128
|
-
if (defaults === undefined) {
|
|
129
|
-
return { items: list.items, incomplete: list.isIncomplete };
|
|
130
|
-
}
|
|
122
|
+
const list = result === null || Array.isArray(result) ? undefined : result;
|
|
123
|
+
const items = result === null ? [] : Array.isArray(result) ? result : result.items;
|
|
131
124
|
return {
|
|
132
|
-
incomplete: list
|
|
133
|
-
items:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
next.insertTextFormat = defaults.insertTextFormat;
|
|
125
|
+
incomplete: list?.isIncomplete ?? false,
|
|
126
|
+
items: items.flatMap((item) => {
|
|
127
|
+
if (typeof item?.label !== 'string') {
|
|
128
|
+
return [];
|
|
137
129
|
}
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
try {
|
|
131
|
+
return [applyCompletionDefaults(item, list?.itemDefaults, { onInvalidRange: () => undefined })];
|
|
140
132
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
: { insert: defaults.editRange.insert, replace: defaults.editRange.replace, newText: text };
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (error instanceof TypeError || error instanceof RangeError) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
throw error;
|
|
147
138
|
}
|
|
148
|
-
return next;
|
|
149
139
|
})
|
|
150
140
|
};
|
|
151
141
|
}
|
|
@@ -236,7 +226,7 @@ function withoutCall(snippet) {
|
|
|
236
226
|
* What goes into the text for an item and the range it takes the place of, given the caret. `lineAfter` is
|
|
237
227
|
* the rest of the line after the caret: an item that would add a call does not when a parenthesis follows.
|
|
238
228
|
*/
|
|
239
|
-
export function insertionOf(item, caret, lineBefore, replace, lineAfter = '') {
|
|
229
|
+
export function insertionOf(item, caret, lineBefore, replace, lineAfter = '', options = {}) {
|
|
240
230
|
const raw = item.textEdit?.newText ?? item.insertText ?? item.label;
|
|
241
231
|
const edit = editRangeOf(item, replace);
|
|
242
232
|
const range = edit === null
|
|
@@ -244,17 +234,34 @@ export function insertionOf(item, caret, lineBefore, replace, lineAfter = '') {
|
|
|
244
234
|
: // The word grew since the answer came, so what was typed since belongs to what is replaced.
|
|
245
235
|
{ start: edit.start, end: comparePositions(edit.end, caret) < 0 && edit.end.line === caret.line ? caret : edit.end };
|
|
246
236
|
if (item.insertTextFormat !== SNIPPET_FORMAT) {
|
|
247
|
-
|
|
237
|
+
const adjusted = item.insertTextMode === 2
|
|
238
|
+
? indentSnippet({ text: raw, stops: [] }, options.indentationBefore ?? lineBefore.slice(0, range.start.character))
|
|
239
|
+
: { text: raw, stops: [] };
|
|
240
|
+
return { range, text: adjusted.text, stops: [], ...(adjusted.limited ? { limited: true } : {}) };
|
|
248
241
|
}
|
|
249
242
|
const followed = range.end.line === caret.line && lineAfter.slice(Math.max(0, range.end.character - caret.character)).startsWith('(');
|
|
250
|
-
const
|
|
251
|
-
|
|
243
|
+
const source = followed && isCallableKind(item.kind) ? withoutCall(raw) : raw;
|
|
244
|
+
const template = options.template?.source === source ? options.template : compileSnippet(source);
|
|
245
|
+
const rendered = template.render(options.resolve, options.variableTransforms);
|
|
246
|
+
const parsed = item.insertTextMode === 2 ? indentSnippet(rendered, options.indentationBefore ?? lineBefore.slice(0, range.start.character)) : rendered;
|
|
247
|
+
return {
|
|
248
|
+
range,
|
|
249
|
+
text: parsed.text,
|
|
250
|
+
stops: tabOrder(parsed),
|
|
251
|
+
fields: parsed.fields,
|
|
252
|
+
...(options.variableTransforms !== undefined || rendered.transforms === undefined
|
|
253
|
+
? {}
|
|
254
|
+
: {
|
|
255
|
+
variableTransforms: rendered.transforms.map((range) => ({ ...range.transform, value: rendered.text.slice(range.start, range.end) }))
|
|
256
|
+
}),
|
|
257
|
+
...(parsed.limited ? { limited: true } : {})
|
|
258
|
+
};
|
|
252
259
|
}
|
|
253
260
|
/*
|
|
254
261
|
* The same insertion at every other caret that has the word the primary caret has before it, so a name typed at
|
|
255
262
|
* several carets is completed at all of them. A caret that is inside a selection, or after other text, is left alone.
|
|
256
263
|
*/
|
|
257
|
-
export function mirroredInsertions(carets, primary, caret, text, insertion) {
|
|
264
|
+
export function mirroredInsertions(carets, primary, caret, text, insertion, includeSelections = false) {
|
|
258
265
|
const before = insertion.range.start.line === caret.line ? caret.character - insertion.range.start.character : -1;
|
|
259
266
|
const after = insertion.range.end.line === caret.line ? insertion.range.end.character - caret.character : -1;
|
|
260
267
|
if (before < 0 || after < 0) {
|
|
@@ -263,11 +270,11 @@ export function mirroredInsertions(carets, primary, caret, text, insertion) {
|
|
|
263
270
|
const typed = text({ start: insertion.range.start, end: caret });
|
|
264
271
|
const replaced = text({ start: caret, end: insertion.range.end });
|
|
265
272
|
return carets.flatMap((other) => {
|
|
266
|
-
const at = other.start;
|
|
273
|
+
const at = includeSelections ? other.end : other.start;
|
|
267
274
|
if (comparePositions(other.start, primary.start) === 0 && comparePositions(other.end, primary.end) === 0) {
|
|
268
275
|
return [];
|
|
269
276
|
}
|
|
270
|
-
if (comparePositions(other.start, other.end) !== 0 || at.character < before) {
|
|
277
|
+
if ((!includeSelections && comparePositions(other.start, other.end) !== 0) || at.character < before) {
|
|
271
278
|
return [];
|
|
272
279
|
}
|
|
273
280
|
const range = { start: { line: at.line, character: at.character - before }, end: { line: at.line, character: at.character + after } };
|
package/dist/completion.d.ts
CHANGED
|
@@ -18,12 +18,16 @@ export declare class CompletionFeature {
|
|
|
18
18
|
private requestTimer;
|
|
19
19
|
private refreshTimer;
|
|
20
20
|
private resolveTimer;
|
|
21
|
+
private textRevision;
|
|
22
|
+
private acceptSequence;
|
|
23
|
+
private disposed;
|
|
21
24
|
private readonly resolved;
|
|
22
25
|
constructor(language: EditorLanguage, timers?: Timers);
|
|
23
26
|
get isOpen(): boolean;
|
|
24
27
|
invoke(): void;
|
|
25
28
|
move(delta: number): void;
|
|
26
29
|
accept(replace: boolean, index?: number, commit?: string): Promise<void>;
|
|
30
|
+
private refuseLargeCompletion;
|
|
27
31
|
private placeMirroredCarets;
|
|
28
32
|
private surroundings;
|
|
29
33
|
private afterInsert;
|
package/dist/completion.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SnippetTransformQueueError } from './snippet-transform-queue.js';
|
|
2
|
+
import { SnippetTransformError } from './snippet-transform-client.js';
|
|
3
|
+
import { MAX_TRANSFORM_JOBS, MAX_TRANSFORM_WORK } from './snippet-transform-protocol.js';
|
|
4
|
+
import { fileUriToPath, StaleResultError } from '@adecore/lsp';
|
|
2
5
|
import { comparePositions, shiftPosition } from './diagnostics-model.js';
|
|
3
|
-
import { completionDocsOf, identifierPrefix, insertionOf, isIdentifierCharacter, itemsOf, matchedCharacters, mirroredInsertions, opensCompletion, prefixFor, qualifiersOf, rankCompletions } from './completion-model.js';
|
|
6
|
+
import { completionDocsOf, editRangeOf, identifierPrefix, insertionOf, isIdentifierCharacter, itemsOf, matchedCharacters, mirroredInsertions, opensCompletion, prefixFor, qualifiersOf, rankCompletions } from './completion-model.js';
|
|
4
7
|
import { PARAMETER_HINTS_COMMAND, isCallItem, planCall, withParentheses } from './completion-call.js';
|
|
8
|
+
import { compileSnippet } from './snippet.js';
|
|
9
|
+
import { createSnippetResolver } from './snippet-variables.js';
|
|
10
|
+
import { wordRangeAt } from './rename-model.js';
|
|
5
11
|
import { RecentChoices, recentChoicesOf } from './recent-choices.js';
|
|
6
12
|
import { shikiLanguageOf } from './language-ids.js';
|
|
7
13
|
import { textBeforeOf } from './line-text.js';
|
|
@@ -16,6 +22,40 @@ const RESOLVE_DELAY_MS = 120;
|
|
|
16
22
|
const PAGE = 8;
|
|
17
23
|
const LINES_BACK = 60;
|
|
18
24
|
const TOAST_ID = 'language-completion';
|
|
25
|
+
const MAX_COMPLETION_CHARACTERS = 2_000_000;
|
|
26
|
+
const MAX_COMPLETION_RANGES = 1_000;
|
|
27
|
+
class CompletionLimitError extends Error {
|
|
28
|
+
}
|
|
29
|
+
function mapCompletionEdits(item, changes, mapRange) {
|
|
30
|
+
// Changes apply one after another, so each is checked against the range as the earlier ones left it.
|
|
31
|
+
const overlapsChange = (range) => {
|
|
32
|
+
let { start, end } = range;
|
|
33
|
+
for (const change of changes) {
|
|
34
|
+
if (comparePositions(start, change.range.end) < 0 && comparePositions(change.range.start, end) < 0) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
start = shiftPosition(start, change);
|
|
38
|
+
end = shiftPosition(end, change);
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
let valid = true;
|
|
43
|
+
const map = (range) => {
|
|
44
|
+
valid &&= !overlapsChange(range);
|
|
45
|
+
return mapRange(range);
|
|
46
|
+
};
|
|
47
|
+
const edit = item.textEdit;
|
|
48
|
+
const mapped = {
|
|
49
|
+
...item,
|
|
50
|
+
...(edit === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { textEdit: 'range' in edit ? { ...edit, range: map(edit.range) } : { ...edit, insert: map(edit.insert), replace: map(edit.replace) } }),
|
|
53
|
+
...(item.additionalTextEdits === undefined
|
|
54
|
+
? {}
|
|
55
|
+
: { additionalTextEdits: item.additionalTextEdits.map((edit) => ({ ...edit, range: map(edit.range) })) })
|
|
56
|
+
};
|
|
57
|
+
return valid ? mapped : null;
|
|
58
|
+
}
|
|
19
59
|
/*
|
|
20
60
|
* The suggestions that open while a word is typed. A single typed identifier character or a trigger
|
|
21
61
|
* character of the server asks for the list; every character after that only filters the list that
|
|
@@ -41,6 +81,9 @@ export class CompletionFeature {
|
|
|
41
81
|
requestTimer;
|
|
42
82
|
refreshTimer;
|
|
43
83
|
resolveTimer;
|
|
84
|
+
textRevision = 0;
|
|
85
|
+
acceptSequence = 0;
|
|
86
|
+
disposed = false;
|
|
44
87
|
resolved = new Map();
|
|
45
88
|
constructor(language, timers = realTimers) {
|
|
46
89
|
this.language = language;
|
|
@@ -50,9 +93,13 @@ export class CompletionFeature {
|
|
|
50
93
|
editor.onKeyDown((event) => this.key(event)),
|
|
51
94
|
editor.onTextChange((change) => this.edited(change)),
|
|
52
95
|
editor.onCaret((position) => this.caretMoved(position)),
|
|
53
|
-
editor.onBlur(() =>
|
|
96
|
+
editor.onBlur(() => {
|
|
97
|
+
this.acceptSequence++;
|
|
98
|
+
this.close();
|
|
99
|
+
})
|
|
54
100
|
];
|
|
55
101
|
language.onDispose(() => {
|
|
102
|
+
this.disposed = true;
|
|
56
103
|
for (const off of offs) {
|
|
57
104
|
off();
|
|
58
105
|
}
|
|
@@ -64,6 +111,7 @@ export class CompletionFeature {
|
|
|
64
111
|
}
|
|
65
112
|
/* Asks for suggestions at the caret now, as Ctrl+Space does. */
|
|
66
113
|
invoke() {
|
|
114
|
+
this.language.pick.close();
|
|
67
115
|
this.explicit = true;
|
|
68
116
|
this.beginRequest({ triggerKind: 1 }, 0);
|
|
69
117
|
}
|
|
@@ -84,7 +132,28 @@ export class CompletionFeature {
|
|
|
84
132
|
if (entry === undefined) {
|
|
85
133
|
return;
|
|
86
134
|
}
|
|
87
|
-
const { editor, project, uri } = this.language;
|
|
135
|
+
const { editor, project, uri, snippets } = this.language;
|
|
136
|
+
const acceptSequence = ++this.acceptSequence;
|
|
137
|
+
const snippetToken = snippets.token;
|
|
138
|
+
const inSnippet = snippets.isActive;
|
|
139
|
+
const exitChanges = [];
|
|
140
|
+
if (snippets.hasPendingTransforms) {
|
|
141
|
+
this.close();
|
|
142
|
+
if (!(await snippets.settleTransforms(() => !this.disposed && acceptSequence === this.acceptSequence && snippets.token === snippetToken, (changes, map) => exitChanges.push({ changes, map })))) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const acceptedSelections = editor.getSelections();
|
|
147
|
+
const revision = this.textRevision;
|
|
148
|
+
const current = () => {
|
|
149
|
+
if (this.disposed || acceptSequence !== this.acceptSequence || revision !== this.textRevision || (inSnippet && snippets.token !== snippetToken)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const selections = editor.getSelections();
|
|
153
|
+
return (selections.length === acceptedSelections.length &&
|
|
154
|
+
selections.every((range, index) => comparePositions(range.start, acceptedSelections[index].start) === 0 &&
|
|
155
|
+
comparePositions(range.end, acceptedSelections[index].end) === 0));
|
|
156
|
+
};
|
|
88
157
|
recentChoicesOf(this.language.languageId).record(entry.item);
|
|
89
158
|
let item = this.resolved.get(entry.item) ?? entry.item;
|
|
90
159
|
const needsResolve = !this.resolved.has(entry.item) && project.service.providerOptions('completionItem/resolve', uri) !== undefined;
|
|
@@ -93,44 +162,185 @@ export class CompletionFeature {
|
|
|
93
162
|
// Resolving can add the import an item needs; an item that cannot be resolved is still inserted as it is.
|
|
94
163
|
item = await this.resolveNow(entry.item);
|
|
95
164
|
}
|
|
165
|
+
if (!current()) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (const { changes, map } of exitChanges) {
|
|
169
|
+
const mapped = mapCompletionEdits(item, changes, map);
|
|
170
|
+
if (mapped === null) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
item = mapped;
|
|
174
|
+
}
|
|
96
175
|
const caret = editor.getCaret();
|
|
97
176
|
const lineBefore = textBeforeOf(editor, caret);
|
|
98
177
|
const lineAfter = editor.textInRange({ start: caret, end: { line: caret.line, character: Number.MAX_SAFE_INTEGER } });
|
|
99
|
-
// A character that commits is typed after the name, so a call is not added before it.
|
|
100
|
-
const planned = insertionOf(item, caret, lineBefore, replace, commit === undefined ? lineAfter : '(');
|
|
101
|
-
const { before, rest } = this.surroundings(planned, caret, lineAfter);
|
|
102
|
-
const plan = commit === undefined ? planCall({ item, languageId: this.language.languageId, insertion: planned, before, rest, replace }) : { action: 'none' };
|
|
103
|
-
const main = plan.action === 'add' ? withParentheses(planned, plan.inside) : planned;
|
|
104
|
-
const extras = (item.additionalTextEdits ?? []).map((edit) => ({ range: edit.range, text: edit.newText }));
|
|
105
|
-
// An import above the insertion moves it, and the tab stops are measured from where it ends up.
|
|
106
|
-
const start = extras
|
|
107
|
-
.filter((extra) => comparePositions(extra.range.end, main.range.start) <= 0)
|
|
108
|
-
.sort((left, right) => comparePositions(right.range.start, left.range.start))
|
|
109
|
-
.reduce((place, extra) => shiftPosition(place, extra), main.range.start);
|
|
110
|
-
// A name typed at several carets is completed at all of them, and the other carets end behind what was put in.
|
|
111
178
|
const carets = editor.getSelections();
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
179
|
+
const source = item.textEdit?.newText ?? item.insertText ?? item.label;
|
|
180
|
+
if (source.length * carets.length > MAX_COMPLETION_CHARACTERS) {
|
|
181
|
+
this.refuseLargeCompletion();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
let characters = 0;
|
|
185
|
+
let ranges = 0;
|
|
186
|
+
const now = new Date();
|
|
187
|
+
const template = item.insertTextFormat === 2 ? compileSnippet(item.textEdit?.newText ?? item.insertText ?? item.label) : undefined;
|
|
188
|
+
const finishes = new Map();
|
|
189
|
+
const renderAt = (position, selection) => {
|
|
190
|
+
const line = editor.textInRange({ start: { line: position.line, character: 0 }, end: { line: position.line, character: Number.MAX_SAFE_INTEGER } });
|
|
191
|
+
const word = wordRangeAt(line, position);
|
|
192
|
+
const resolver = createSnippetResolver({
|
|
193
|
+
uri,
|
|
194
|
+
languageId: this.language.languageId,
|
|
195
|
+
position,
|
|
196
|
+
selection,
|
|
197
|
+
selectedText: editor.textInRange(selection),
|
|
198
|
+
currentLine: line,
|
|
199
|
+
currentWord: word === null ? '' : editor.textInRange(word),
|
|
200
|
+
cursorIndex: Math.max(0, carets.findIndex((range) => comparePositions(range.start, selection.start) === 0 && comparePositions(range.end, selection.end) === 0)),
|
|
201
|
+
cursorCount: carets.length,
|
|
202
|
+
folder: project.host.folder,
|
|
203
|
+
path: fileUriToPath(uri) ?? project.host.pathOfUri?.(uri) ?? undefined
|
|
204
|
+
}, project.host.resolveSnippetVariable?.bind(project.host), { now });
|
|
205
|
+
const atItem = position === caret || item.textEdit === undefined
|
|
206
|
+
? item
|
|
207
|
+
: { ...item, textEdit: undefined, insertText: item.textEdit?.newText ?? item.insertText };
|
|
208
|
+
const editStart = editRangeOf(atItem, replace)?.start;
|
|
209
|
+
// A commit character follows the name, so accepting must not also add a call.
|
|
210
|
+
const finish = (variableTransforms) => {
|
|
211
|
+
const insertion = insertionOf(atItem, position, line.slice(0, position.character), replace, commit === undefined ? line.slice(position.character) : '(', {
|
|
212
|
+
template,
|
|
213
|
+
variableTransforms,
|
|
214
|
+
resolve: resolver,
|
|
215
|
+
indentationBefore: editStart === undefined ? undefined : editor.textInRange({ start: { line: editStart.line, character: 0 }, end: editStart })
|
|
216
|
+
});
|
|
217
|
+
return commit === undefined ? insertion : { ...insertion, text: insertion.text + commit };
|
|
218
|
+
};
|
|
219
|
+
const rendered = finish();
|
|
220
|
+
finishes.set(rendered, finish);
|
|
221
|
+
characters += rendered.text.length;
|
|
222
|
+
ranges +=
|
|
223
|
+
(rendered.variableTransforms?.length ?? 0) +
|
|
224
|
+
(rendered.fields?.reduce((count, field) => count + field.ranges.length + (field.linked?.length ?? 0), 1) ?? rendered.stops.length);
|
|
225
|
+
if (rendered.limited || characters > MAX_COMPLETION_CHARACTERS || ranges > MAX_COMPLETION_RANGES) {
|
|
226
|
+
throw new CompletionLimitError();
|
|
119
227
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
228
|
+
return rendered;
|
|
229
|
+
};
|
|
230
|
+
try {
|
|
231
|
+
const planned = renderAt(caret, editor.getSelection());
|
|
232
|
+
const { before, rest } = this.surroundings(planned, caret, lineAfter);
|
|
233
|
+
const plan = commit === undefined ? planCall({ item, languageId: this.language.languageId, insertion: planned, before, rest, replace }) : { action: 'none' };
|
|
234
|
+
let main = plan.action === 'add' ? withParentheses(planned, plan.inside) : planned;
|
|
235
|
+
const extras = (item.additionalTextEdits ?? []).map((edit) => ({ range: edit.range, text: edit.newText }));
|
|
236
|
+
const instances = new Map();
|
|
237
|
+
const continuing = inSnippet && snippets.isActive && snippetToken === snippets.token;
|
|
238
|
+
const mirrored = mirroredInsertions(carets, editor.getSelection(), caret, (range) => editor.textInRange(range), main, continuing).map((insertion) => {
|
|
239
|
+
const selection = carets.find((range) => range.start.line === insertion.range.start.line &&
|
|
240
|
+
range.start.character >= insertion.range.start.character &&
|
|
241
|
+
range.start.character <= insertion.range.end.character);
|
|
242
|
+
const position = selection[continuing ? 'end' : 'start'];
|
|
243
|
+
const own = renderAt(position, selection);
|
|
244
|
+
const rendered = plan.action === 'add' ? withParentheses(own, plan.inside) : own;
|
|
245
|
+
const edit = { ...insertion, text: rendered.text };
|
|
246
|
+
const instance = { ...rendered, range: insertion.range };
|
|
247
|
+
instances.set(edit, instance);
|
|
248
|
+
const finish = finishes.get(own);
|
|
249
|
+
if (finish !== undefined) {
|
|
250
|
+
finishes.set(instance, (values) => ({ ...finish(values), range: insertion.range }));
|
|
251
|
+
}
|
|
252
|
+
return edit;
|
|
253
|
+
});
|
|
254
|
+
const drafts = [...mirrored.map((edit) => instances.get(edit)), main];
|
|
255
|
+
const jobs = [];
|
|
256
|
+
const keys = new Map();
|
|
257
|
+
const slots = drafts.map((draft) => (draft.variableTransforms ?? []).map((job) => {
|
|
258
|
+
const key = JSON.stringify(job);
|
|
259
|
+
let slot = keys.get(key);
|
|
260
|
+
if (slot === undefined) {
|
|
261
|
+
slot = jobs.length;
|
|
262
|
+
keys.set(key, slot);
|
|
263
|
+
jobs.push(job);
|
|
264
|
+
}
|
|
265
|
+
return slot;
|
|
266
|
+
}));
|
|
267
|
+
if (jobs.length > MAX_TRANSFORM_JOBS || jobs.reduce((sum, job) => sum + job.value.length, 0) > MAX_TRANSFORM_WORK) {
|
|
268
|
+
throw new CompletionLimitError();
|
|
123
269
|
}
|
|
270
|
+
if (jobs.length > 0 && project.host.snippetTransformWorker !== undefined) {
|
|
271
|
+
const values = await snippets.transformVariables(jobs, current);
|
|
272
|
+
if (!current()) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (values !== null) {
|
|
276
|
+
const transformed = drafts.map((draft, index) => slots[index].length === 0 ? draft : finishes.get(draft)(slots[index].map((slot) => values[slot])));
|
|
277
|
+
if (transformed.some((draft) => draft.limited) ||
|
|
278
|
+
transformed.reduce((sum, draft) => sum + draft.text.length, 0) > MAX_COMPLETION_CHARACTERS) {
|
|
279
|
+
throw new CompletionLimitError();
|
|
280
|
+
}
|
|
281
|
+
main = transformed.at(-1);
|
|
282
|
+
mirrored.forEach((edit, index) => {
|
|
283
|
+
edit.text = transformed[index].text;
|
|
284
|
+
instances.set(edit, transformed[index]);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const primary = { range: main.range, text: main.text };
|
|
289
|
+
const changes = [primary, ...mirrored, ...extras];
|
|
290
|
+
if (changes.reduce((total, edit) => total + edit.text.length, 0) > MAX_COMPLETION_CHARACTERS) {
|
|
291
|
+
throw new CompletionLimitError();
|
|
292
|
+
}
|
|
293
|
+
// Imports and earlier carets move each insertion independently; the primary instance stays last.
|
|
294
|
+
const starts = [...mirrored, primary].map((insertion) => changes
|
|
295
|
+
.filter((change) => change !== insertion && comparePositions(change.range.end, insertion.range.start) <= 0)
|
|
296
|
+
.sort((left, right) => comparePositions(right.range.start, left.range.start))
|
|
297
|
+
.reduce((place, change) => shiftPosition(place, change), insertion.range.start));
|
|
298
|
+
const applied = inSnippet ? snippets.applyEdits(changes, snippetToken, extras) : editor.applyEdits(changes);
|
|
299
|
+
if (!applied) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const landed = editor.getSelections();
|
|
303
|
+
if (commit === undefined) {
|
|
304
|
+
this.language.snippets.beginInstances([...mirrored, primary].map((insertion, index) => {
|
|
305
|
+
const rendered = insertion === primary ? main : instances.get(insertion);
|
|
306
|
+
return { start: starts[index], text: rendered.text, stops: rendered.stops, fields: rendered.fields };
|
|
307
|
+
}), { nested: continuing && snippets.isActive });
|
|
308
|
+
if (plan.action === 'add' && plan.inside && mirrored.length > 0 && main.fields === undefined) {
|
|
309
|
+
this.placeMirroredCarets(carets, mirrored, landed);
|
|
310
|
+
}
|
|
311
|
+
if (plan.action === 'enter') {
|
|
312
|
+
const end = editor.getCaret();
|
|
313
|
+
editor.setCaret({ line: end.line, character: end.character + plan.shift });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
if (this.triggerCharacters().includes(commit)) {
|
|
318
|
+
this.beginRequest({ triggerKind: 2, triggerCharacter: commit }, 0);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
this.afterInsert(item, commit, lineBefore);
|
|
322
|
+
editor.focus();
|
|
124
323
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
324
|
+
catch (error) {
|
|
325
|
+
if (!current()) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (error instanceof SnippetTransformQueueError) {
|
|
329
|
+
project.host.notify?.({ id: TOAST_ID, kind: 'error', title: project.i18n.t('editor:language.completion.transformBusy') });
|
|
330
|
+
}
|
|
331
|
+
else if (error instanceof CompletionLimitError || (error instanceof SnippetTransformError && error.kind === 'range')) {
|
|
332
|
+
this.refuseLargeCompletion();
|
|
333
|
+
}
|
|
334
|
+
else if (error instanceof SnippetTransformError) {
|
|
335
|
+
project.host.notify?.({ id: TOAST_ID, kind: 'error', title: project.i18n.t('editor:language.completion.transformFailed') });
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
throw error;
|
|
130
339
|
}
|
|
131
340
|
}
|
|
132
|
-
|
|
133
|
-
|
|
341
|
+
}
|
|
342
|
+
refuseLargeCompletion() {
|
|
343
|
+
this.language.project.host.notify?.({ id: TOAST_ID, kind: 'error', title: this.language.project.i18n.t('editor:language.completion.tooLarge') });
|
|
134
344
|
}
|
|
135
345
|
/* The carets that got the same `()` go inside it like the primary one, which the snippet already placed. */
|
|
136
346
|
placeMirroredCarets(before, mirrored, landed) {
|
|
@@ -183,7 +393,7 @@ export class CompletionFeature {
|
|
|
183
393
|
async resolveNow(item) {
|
|
184
394
|
const { editor, project, uri } = this.language;
|
|
185
395
|
try {
|
|
186
|
-
return await project.service.resolveCompletion(uri, item);
|
|
396
|
+
return { ...item, ...(await project.service.resolveCompletion(uri, item)) };
|
|
187
397
|
}
|
|
188
398
|
catch (error) {
|
|
189
399
|
if (!(error instanceof StaleResultError)) {
|
|
@@ -192,7 +402,7 @@ export class CompletionFeature {
|
|
|
192
402
|
}
|
|
193
403
|
try {
|
|
194
404
|
const fresh = itemsOf(await project.service.completion(uri, editor.getCaret(), { triggerKind: 1 })).items.find((candidate) => candidate.label === item.label && candidate.kind === item.kind);
|
|
195
|
-
return fresh === undefined ? item : await project.service.resolveCompletion(uri, fresh).catch(() => fresh);
|
|
405
|
+
return fresh === undefined ? item : { ...fresh, ...(await project.service.resolveCompletion(uri, fresh).catch(() => fresh)) };
|
|
196
406
|
}
|
|
197
407
|
catch {
|
|
198
408
|
return item;
|
|
@@ -268,6 +478,7 @@ export class CompletionFeature {
|
|
|
268
478
|
return this.ranked[this.active]?.item.commitCharacters?.includes(event.key) === true;
|
|
269
479
|
}
|
|
270
480
|
edited(change) {
|
|
481
|
+
this.textRevision++;
|
|
271
482
|
if (change.source !== 'input' || change.changes.length !== 1) {
|
|
272
483
|
this.close();
|
|
273
484
|
return;
|
|
@@ -340,17 +551,21 @@ export class CompletionFeature {
|
|
|
340
551
|
this.controller?.abort();
|
|
341
552
|
const controller = new AbortController();
|
|
342
553
|
this.controller = controller;
|
|
343
|
-
let
|
|
554
|
+
let normalized;
|
|
344
555
|
try {
|
|
345
|
-
result = await project.service.completion(uri, position, context, { signal: controller.signal });
|
|
556
|
+
const result = await project.service.completion(uri, position, context, { signal: controller.signal });
|
|
557
|
+
if (request !== this.request || this.controller !== controller || controller.signal.aborted) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
normalized = itemsOf(result);
|
|
346
561
|
}
|
|
347
562
|
catch {
|
|
563
|
+
if (request === this.request && this.controller === controller && !controller.signal.aborted) {
|
|
564
|
+
this.close();
|
|
565
|
+
}
|
|
348
566
|
return;
|
|
349
567
|
}
|
|
350
|
-
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
const { items, incomplete } = itemsOf(result);
|
|
568
|
+
const { items, incomplete } = normalized;
|
|
354
569
|
this.pool = items;
|
|
355
570
|
this.incomplete = incomplete;
|
|
356
571
|
this.resolved.clear();
|
|
@@ -428,7 +643,7 @@ export class CompletionFeature {
|
|
|
428
643
|
void project.service
|
|
429
644
|
.resolveCompletion(uri, entry.item)
|
|
430
645
|
.then((item) => {
|
|
431
|
-
this.resolved.set(entry.item, item);
|
|
646
|
+
this.resolved.set(entry.item, { ...entry.item, ...item });
|
|
432
647
|
if (this.ranked[this.active]?.item === entry.item) {
|
|
433
648
|
this.publish();
|
|
434
649
|
}
|
package/dist/diagnostics.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isShortcut } from './shortcut-keys.js';
|
|
2
|
-
import { comparePositions, markerOf, neighborProblem, problemCountsOf, problemsAt, shiftRange } from './diagnostics-model.js';
|
|
2
|
+
import { comparePositions, markerOf, neighborProblem, problemCountsOf, problemsAt, severityOf, shiftRange } from './diagnostics-model.js';
|
|
3
3
|
/*
|
|
4
4
|
* The problems the servers report for one file. A report from a server replaces the one before it
|
|
5
5
|
* from the same server, the editor draws them, and each one follows its text through edits made
|
|
@@ -54,7 +54,7 @@ export class DiagnosticsFeature {
|
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
56
|
const { start } = target.diagnostic.range;
|
|
57
|
-
this.reveal(
|
|
57
|
+
this.reveal(target, comparePositions(start, caret) > 0 ? 'centerDown' : 'centerUp');
|
|
58
58
|
return true;
|
|
59
59
|
}
|
|
60
60
|
/* Puts the caret on the first problem of the file. */
|
|
@@ -63,7 +63,7 @@ export class DiagnosticsFeature {
|
|
|
63
63
|
if (target === null) {
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
|
-
this.reveal(target
|
|
66
|
+
this.reveal(target, 'center');
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
69
|
onChange(listener) {
|
|
@@ -72,11 +72,13 @@ export class DiagnosticsFeature {
|
|
|
72
72
|
this.listeners.delete(listener);
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
reveal(
|
|
76
|
-
const { editor } = this.language;
|
|
77
|
-
editor.setCaret(
|
|
75
|
+
reveal(problem, reveal) {
|
|
76
|
+
const { editor, project } = this.language;
|
|
77
|
+
editor.setCaret(problem.diagnostic.range.start, reveal);
|
|
78
78
|
editor.focus();
|
|
79
79
|
this.language.hover.showProblemsAt(editor.getCaret());
|
|
80
|
+
const severity = project.i18n.t(`editor:language.announce.severity.${severityOf(problem.diagnostic)}`);
|
|
81
|
+
editor.announce(project.i18n.t('editor:language.announce.problem', { severity, message: problem.diagnostic.message }));
|
|
80
82
|
}
|
|
81
83
|
shift(changes) {
|
|
82
84
|
for (const [server, diagnostics] of this.reports) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type DiffDocument } from '@adecore/merge';
|
|
2
|
+
export interface DiffLimits {
|
|
3
|
+
readonly rows: number;
|
|
4
|
+
readonly characters: number;
|
|
5
|
+
readonly lineLength: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_UNFOLD_LIMITS: DiffLimits;
|
|
8
|
+
export declare function fitsDiff(document: DiffDocument, unfolded: ReadonlyMap<number, number>, limits: DiffLimits, maxRows?: number): boolean;
|
|
9
|
+
export declare function expandDiff(document: DiffDocument, unfolded: ReadonlyMap<number, number>, id: number, limits: DiffLimits): ReadonlyMap<number, number>;
|