@singapore-editor/lsp-plugin 0.1.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/README.md +103 -0
- package/dist/bufferSync.d.ts +12 -0
- package/dist/bufferSync.d.ts.map +1 -0
- package/dist/bufferSync.js +35 -0
- package/dist/bufferSync.js.map +1 -0
- package/dist/codeActions.d.ts +83 -0
- package/dist/codeActions.d.ts.map +1 -0
- package/dist/codeActions.js +294 -0
- package/dist/codeActions.js.map +1 -0
- package/dist/colorAlpha.d.ts +4 -0
- package/dist/colorAlpha.d.ts.map +1 -0
- package/dist/colorAlpha.js +28 -0
- package/dist/colorAlpha.js.map +1 -0
- package/dist/completion.d.ts +124 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +609 -0
- package/dist/completion.js.map +1 -0
- package/dist/completionCommit.d.ts +19 -0
- package/dist/completionCommit.d.ts.map +1 -0
- package/dist/completionCommit.js +57 -0
- package/dist/completionCommit.js.map +1 -0
- package/dist/completionController.d.ts +89 -0
- package/dist/completionController.d.ts.map +1 -0
- package/dist/completionController.js +376 -0
- package/dist/completionController.js.map +1 -0
- package/dist/completionProviders.d.ts +50 -0
- package/dist/completionProviders.d.ts.map +1 -0
- package/dist/completionProviders.js +64 -0
- package/dist/completionProviders.js.map +1 -0
- package/dist/connectionContext.d.ts +7 -0
- package/dist/connectionContext.d.ts.map +1 -0
- package/dist/definitionLinkController.d.ts +54 -0
- package/dist/definitionLinkController.d.ts.map +1 -0
- package/dist/definitionLinkController.js +221 -0
- package/dist/definitionLinkController.js.map +1 -0
- package/dist/definitionNavigation.d.ts +76 -0
- package/dist/definitionNavigation.d.ts.map +1 -0
- package/dist/definitionNavigation.js +143 -0
- package/dist/definitionNavigation.js.map +1 -0
- package/dist/diagnosticNotes.d.ts +7 -0
- package/dist/diagnosticNotes.d.ts.map +1 -0
- package/dist/diagnosticNotes.js +37 -0
- package/dist/diagnosticNotes.js.map +1 -0
- package/dist/diagnosticProjection.d.ts +31 -0
- package/dist/diagnosticProjection.d.ts.map +1 -0
- package/dist/diagnosticProjection.js +79 -0
- package/dist/diagnosticProjection.js.map +1 -0
- package/dist/diagnostics.d.ts +16 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +94 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/diagnosticsPresenter.d.ts +64 -0
- package/dist/diagnosticsPresenter.d.ts.map +1 -0
- package/dist/diagnosticsPresenter.js +305 -0
- package/dist/diagnosticsPresenter.js.map +1 -0
- package/dist/document.d.ts +69 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +190 -0
- package/dist/document.js.map +1 -0
- package/dist/documentHighlightController.d.ts +34 -0
- package/dist/documentHighlightController.d.ts.map +1 -0
- package/dist/documentHighlightController.js +101 -0
- package/dist/documentHighlightController.js.map +1 -0
- package/dist/documentSnapshot.d.ts +9 -0
- package/dist/documentSnapshot.d.ts.map +1 -0
- package/dist/documentSnapshot.js +31 -0
- package/dist/documentSnapshot.js.map +1 -0
- package/dist/documentSync.d.ts +53 -0
- package/dist/documentSync.d.ts.map +1 -0
- package/dist/documentSync.js +334 -0
- package/dist/documentSync.js.map +1 -0
- package/dist/documentSyncController.d.ts +26 -0
- package/dist/documentSyncController.d.ts.map +1 -0
- package/dist/documentSyncController.js +24 -0
- package/dist/documentSyncController.js.map +1 -0
- package/dist/formatOnType.d.ts +43 -0
- package/dist/formatOnType.d.ts.map +1 -0
- package/dist/formatOnType.js +198 -0
- package/dist/formatOnType.js.map +1 -0
- package/dist/formatting.d.ts +18 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +52 -0
- package/dist/formatting.js.map +1 -0
- package/dist/fuzzyMatch.d.ts +22 -0
- package/dist/fuzzyMatch.d.ts.map +1 -0
- package/dist/fuzzyMatch.js +216 -0
- package/dist/fuzzyMatch.js.map +1 -0
- package/dist/hoverParticipant.d.ts +24 -0
- package/dist/hoverParticipant.d.ts.map +1 -0
- package/dist/hoverParticipant.js +156 -0
- package/dist/hoverParticipant.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/keyCommands.d.ts +19 -0
- package/dist/keyCommands.d.ts.map +1 -0
- package/dist/keyCommands.js +21 -0
- package/dist/keyCommands.js.map +1 -0
- package/dist/lane.d.ts +28 -0
- package/dist/lane.d.ts.map +1 -0
- package/dist/lane.js +138 -0
- package/dist/lane.js.map +1 -0
- package/dist/lspConnection.d.ts +94 -0
- package/dist/lspConnection.d.ts.map +1 -0
- package/dist/lspConnection.js +195 -0
- package/dist/lspConnection.js.map +1 -0
- package/dist/lspConnectionPool.d.ts +34 -0
- package/dist/lspConnectionPool.d.ts.map +1 -0
- package/dist/lspConnectionPool.js +255 -0
- package/dist/lspConnectionPool.js.map +1 -0
- package/dist/minimalEdits.d.ts +21 -0
- package/dist/minimalEdits.d.ts.map +1 -0
- package/dist/minimalEdits.js +181 -0
- package/dist/minimalEdits.js.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +38 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugin.d.ts +100 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +837 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.styles.d.ts +31 -0
- package/dist/plugin.styles.d.ts.map +1 -0
- package/dist/plugin.styles.js +102 -0
- package/dist/plugin.styles.js.map +1 -0
- package/dist/pluginTypes.d.ts +41 -0
- package/dist/pluginTypes.d.ts.map +1 -0
- package/dist/pluginTypes.js +0 -0
- package/dist/pullDiagnostics.d.ts +30 -0
- package/dist/pullDiagnostics.d.ts.map +1 -0
- package/dist/pullDiagnostics.js +94 -0
- package/dist/pullDiagnostics.js.map +1 -0
- package/dist/renameWidget.d.ts +30 -0
- package/dist/renameWidget.d.ts.map +1 -0
- package/dist/renameWidget.js +117 -0
- package/dist/renameWidget.js.map +1 -0
- package/dist/semanticTokenDecoder.d.ts +81 -0
- package/dist/semanticTokenDecoder.d.ts.map +1 -0
- package/dist/semanticTokenDecoder.js +132 -0
- package/dist/semanticTokenDecoder.js.map +1 -0
- package/dist/semanticTokens.d.ts +47 -0
- package/dist/semanticTokens.d.ts.map +1 -0
- package/dist/semanticTokens.js +68 -0
- package/dist/semanticTokens.js.map +1 -0
- package/dist/serverSet.d.ts +69 -0
- package/dist/serverSet.d.ts.map +1 -0
- package/dist/serverSet.js +331 -0
- package/dist/serverSet.js.map +1 -0
- package/dist/signatureHelp.d.ts +40 -0
- package/dist/signatureHelp.d.ts.map +1 -0
- package/dist/signatureHelp.js +108 -0
- package/dist/signatureHelp.js.map +1 -0
- package/dist/signatureHelpController.d.ts +52 -0
- package/dist/signatureHelpController.d.ts.map +1 -0
- package/dist/signatureHelpController.js +160 -0
- package/dist/signatureHelpController.js.map +1 -0
- package/dist/sourceText.d.ts +22 -0
- package/dist/sourceText.d.ts.map +1 -0
- package/dist/sourceText.js +70 -0
- package/dist/sourceText.js.map +1 -0
- package/dist/types.d.ts +238 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/viewDocumentSnapshot.d.ts +5 -0
- package/dist/viewDocumentSnapshot.d.ts.map +1 -0
- package/dist/viewDocumentSnapshot.js +12 -0
- package/dist/viewDocumentSnapshot.js.map +1 -0
- package/dist/websocket.d.ts +6 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +4 -0
- package/dist/workspaceEdit.d.ts +66 -0
- package/dist/workspaceEdit.d.ts.map +1 -0
- package/dist/workspaceEdit.js +276 -0
- package/dist/workspaceEdit.js.map +1 -0
- package/dist/workspaceEditProvenance.d.ts +5 -0
- package/dist/workspaceEditProvenance.d.ts.map +1 -0
- package/dist/workspaceEditProvenance.js +16 -0
- package/dist/workspaceEditProvenance.js.map +1 -0
- package/dist/workspaceTextEdits.d.ts +60 -0
- package/dist/workspaceTextEdits.d.ts.map +1 -0
- package/dist/workspaceTextEdits.js +395 -0
- package/dist/workspaceTextEdits.js.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { documentUriToFileName } from "./paths.js";
|
|
2
|
+
import { rangeAroundOffset } from "./sourceText.js";
|
|
3
|
+
import { lspPositionToOffsetInSnapshot, offsetToLspPositionInSnapshot } from "@singapore-editor/lsp";
|
|
4
|
+
import { identifierRangeAtOffset } from "@singapore-editor/plugin-ui/offset-range";
|
|
5
|
+
//#region src/definitionNavigation.ts
|
|
6
|
+
var SET_SELECTION_TIMING_NAME = "lspPlugin.goToDefinition";
|
|
7
|
+
var REQUEST_METHODS = {
|
|
8
|
+
definition: "textDocument/definition",
|
|
9
|
+
implementation: "textDocument/implementation",
|
|
10
|
+
typeDefinition: "textDocument/typeDefinition"
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Issue a `textDocument/definition` request and normalize the response
|
|
14
|
+
* into a flat list of {@link LanguageServerDefinitionTarget}s. The raw LSP
|
|
15
|
+
* union (`Location`, `Location[]`, `LocationLink[]`, or `null`) is hidden
|
|
16
|
+
* from callers so higher-level orchestration can work with a single shape.
|
|
17
|
+
*/
|
|
18
|
+
async function requestDefinition(client, request) {
|
|
19
|
+
return requestNavigationTargets(client, {
|
|
20
|
+
...request,
|
|
21
|
+
kind: "definition"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function requestNavigationTargets(client, request) {
|
|
25
|
+
if (request.kind === "references") return requestReferences(client, request);
|
|
26
|
+
const raw = await client.request(REQUEST_METHODS[request.kind], {
|
|
27
|
+
textDocument: { uri: request.uri },
|
|
28
|
+
position: offsetToLspPositionInSnapshot(request.document, request.offset)
|
|
29
|
+
}, request.signal ? { signal: request.signal } : void 0);
|
|
30
|
+
const sourceRange = definitionSourceRange(raw, request);
|
|
31
|
+
return {
|
|
32
|
+
targets: definitionTargets(raw),
|
|
33
|
+
...sourceRange ? { sourceRange } : {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function requestReferences(client, request) {
|
|
37
|
+
return { targets: definitionTargets(await client.request("textDocument/references", {
|
|
38
|
+
textDocument: { uri: request.uri },
|
|
39
|
+
position: offsetToLspPositionInSnapshot(request.document, request.offset),
|
|
40
|
+
context: { includeDeclaration: request.includeDeclaration ?? true }
|
|
41
|
+
}, request.signal ? { signal: request.signal } : void 0)) };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Navigate the editor to `target` by translating its LSP range into
|
|
45
|
+
* offsets in the editor's current text and applying the selection. The
|
|
46
|
+
* caller is responsible for ensuring `editor.document` is the text of the same
|
|
47
|
+
* document `target` refers to (i.e. a same-document jump); cross-document
|
|
48
|
+
* jumps should be routed through `onOpenDefinition`, not this function.
|
|
49
|
+
*/
|
|
50
|
+
function navigateToDefinition(target, editor) {
|
|
51
|
+
navigateToTarget(target, editor, SET_SELECTION_TIMING_NAME);
|
|
52
|
+
}
|
|
53
|
+
function navigateToTarget(target, editor, timingName) {
|
|
54
|
+
const start = lspPositionToOffsetInSnapshot(editor.document, target.range.start);
|
|
55
|
+
const end = lspPositionToOffsetInSnapshot(editor.document, target.range.end);
|
|
56
|
+
editor.setSelection(start, end, timingName, {
|
|
57
|
+
revealBlock: "center",
|
|
58
|
+
revealOffset: start
|
|
59
|
+
});
|
|
60
|
+
editor.focusEditor();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Pick the best target to jump to from a definition result. Prefers
|
|
64
|
+
* targets inside the active document, then targets outside `node_modules`,
|
|
65
|
+
* then any target; returns `null` when no targets were returned.
|
|
66
|
+
*/
|
|
67
|
+
function preferredDefinitionTarget(activeUri, result) {
|
|
68
|
+
return preferredTarget(activeUri, result.targets);
|
|
69
|
+
}
|
|
70
|
+
function preferredReferenceTarget(activeUri, activeDocument, sourceOffset, result) {
|
|
71
|
+
const sourceRange = rangeAroundOffset(activeDocument, sourceOffset, identifierRangeAtOffset) ?? {
|
|
72
|
+
start: sourceOffset,
|
|
73
|
+
end: sourceOffset
|
|
74
|
+
};
|
|
75
|
+
const sameDocumentTargets = result.targets.flatMap((target) => targetWithOffset(activeUri, activeDocument, target));
|
|
76
|
+
const nextTarget = sameDocumentTargets.find((target) => target.start > sourceRange.end);
|
|
77
|
+
if (nextTarget) return nextTarget.target;
|
|
78
|
+
return sameDocumentTargets.find((target) => !rangesOverlap(sourceRange, target))?.target ?? preferredTarget(activeUri, result.targets);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Pick the best target to render a ctrl/cmd-hover definition link for.
|
|
82
|
+
* Same preference rules as {@link preferredDefinitionTarget} but filters
|
|
83
|
+
* out targets that would point back at (or overlap with) the hovered
|
|
84
|
+
* identifier itself — the user would not be able to "jump" to the
|
|
85
|
+
* definition they are already looking at.
|
|
86
|
+
*/
|
|
87
|
+
function preferredJumpableDefinitionTarget(activeUri, activeDocument, sourceRange, result) {
|
|
88
|
+
return preferredTarget(activeUri, result.targets.filter((target) => !targetIsSourceRange(activeUri, activeDocument, sourceRange, target)));
|
|
89
|
+
}
|
|
90
|
+
function preferredTarget(activeUri, targets) {
|
|
91
|
+
return targets.find((target) => target.uri === activeUri) ?? targets.find((target) => !target.path.includes("/node_modules/")) ?? targets[0] ?? null;
|
|
92
|
+
}
|
|
93
|
+
function targetWithOffset(activeUri, activeDocument, target) {
|
|
94
|
+
if (target.uri !== activeUri) return [];
|
|
95
|
+
return [{
|
|
96
|
+
start: lspPositionToOffsetInSnapshot(activeDocument, target.range.start),
|
|
97
|
+
end: lspPositionToOffsetInSnapshot(activeDocument, target.range.end),
|
|
98
|
+
target
|
|
99
|
+
}];
|
|
100
|
+
}
|
|
101
|
+
function targetIsSourceRange(activeUri, activeDocument, sourceRange, target) {
|
|
102
|
+
if (target.uri !== activeUri) return false;
|
|
103
|
+
return rangesOverlap(sourceRange, {
|
|
104
|
+
start: lspPositionToOffsetInSnapshot(activeDocument, target.range.start),
|
|
105
|
+
end: lspPositionToOffsetInSnapshot(activeDocument, target.range.end)
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function rangesOverlap(left, right) {
|
|
109
|
+
return left.start < right.end && right.start < left.end;
|
|
110
|
+
}
|
|
111
|
+
function definitionTargets(result) {
|
|
112
|
+
if (!result) return [];
|
|
113
|
+
return (Array.isArray(result) ? result : [result]).flatMap(definitionTarget);
|
|
114
|
+
}
|
|
115
|
+
function definitionSourceRange(result, request) {
|
|
116
|
+
if (!result) return null;
|
|
117
|
+
const items = Array.isArray(result) ? result : [result];
|
|
118
|
+
for (const item of items) {
|
|
119
|
+
if (!("originSelectionRange" in item) || !item.originSelectionRange) continue;
|
|
120
|
+
const start = lspPositionToOffsetInSnapshot(request.document, item.originSelectionRange.start);
|
|
121
|
+
const end = lspPositionToOffsetInSnapshot(request.document, item.originSelectionRange.end);
|
|
122
|
+
if (request.offset >= start && request.offset < end) return {
|
|
123
|
+
start,
|
|
124
|
+
end
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
function definitionTarget(item) {
|
|
130
|
+
const uri = "targetUri" in item ? item.targetUri : item.uri;
|
|
131
|
+
const range = "targetSelectionRange" in item ? item.targetSelectionRange : item.range;
|
|
132
|
+
const fileName = documentUriToFileName(uri);
|
|
133
|
+
if (!fileName) return [];
|
|
134
|
+
return [{
|
|
135
|
+
uri,
|
|
136
|
+
path: fileName.replace(/^\/+/, ""),
|
|
137
|
+
range
|
|
138
|
+
}];
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { navigateToDefinition, navigateToTarget, preferredDefinitionTarget, preferredJumpableDefinitionTarget, preferredReferenceTarget, requestDefinition, requestNavigationTargets };
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=definitionNavigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitionNavigation.js","names":[],"sources":["../src/definitionNavigation.ts"],"sourcesContent":["import {\n lspPositionToOffsetInSnapshot,\n offsetToLspPositionInSnapshot,\n type LspTextDocumentSnapshot,\n} from '@singapore-editor/lsp'\nimport { type OffsetRange, identifierRangeAtOffset } from '@singapore-editor/plugin-ui/offset-range'\nimport type { EditorSetSelectionOptions } from '@singapore-editor/core/editor'\nimport type * as lsp from 'vscode-languageserver-protocol'\n\nimport { documentUriToFileName } from './paths'\nimport { rangeAroundOffset } from './sourceText'\nimport type { LanguageServerFeatureRouter } from './serverSet'\nimport type { LanguageServerDefinitionTarget, LanguageServerNavigationKind } from './types'\n\n/**\n * Inputs required to issue a `textDocument/definition` request against the\n * LSP client. The request is expressed in editor-native coordinates\n * (`offset` into `document`) and converted to an LSP `Position` internally.\n */\nexport type DefinitionRequest = {\n readonly uri: lsp.DocumentUri\n readonly document: LspTextDocumentSnapshot\n readonly offset: number\n readonly signal?: AbortSignal\n}\n\nexport type NavigationRequest = DefinitionRequest & {\n readonly kind: LanguageServerNavigationKind\n readonly includeDeclaration?: boolean\n}\n\n/**\n * Normalized response from {@link requestDefinition}. The LSP protocol\n * permits a single `Location`, an array of `Location`, or an array of\n * `LocationLink` (or `null`); callers should not have to care which shape\n * was returned, so the raw result is collapsed into a flat list of\n * resolvable targets.\n */\nexport type DefinitionResult = {\n readonly targets: readonly LanguageServerDefinitionTarget[]\n readonly sourceRange?: OffsetRange\n}\n\n/**\n * Minimum editor surface required by {@link navigateToDefinition}. Matches\n * the corresponding subset of `EditorViewContributionContext` so the\n * contribution can pass its `context` through directly while keeping this\n * module decoupled from `@singapore-editor/core`'s full contribution surface.\n */\nexport type NavigationEditor = {\n readonly document: LspTextDocumentSnapshot\n setSelection(\n anchor: number,\n head: number,\n timingName: string,\n options?: EditorSetSelectionOptions,\n ): void\n focusEditor(): void\n}\n\nconst SET_SELECTION_TIMING_NAME = 'lspPlugin.goToDefinition'\n\nconst REQUEST_METHODS: Record<Exclude<LanguageServerNavigationKind, 'references'>, string> = {\n definition: 'textDocument/definition',\n implementation: 'textDocument/implementation',\n typeDefinition: 'textDocument/typeDefinition',\n}\n\n/**\n * Issue a `textDocument/definition` request and normalize the response\n * into a flat list of {@link LanguageServerDefinitionTarget}s. The raw LSP\n * union (`Location`, `Location[]`, `LocationLink[]`, or `null`) is hidden\n * from callers so higher-level orchestration can work with a single shape.\n */\nexport async function requestDefinition(\n client: LanguageServerFeatureRouter,\n request: DefinitionRequest,\n): Promise<DefinitionResult> {\n return requestNavigationTargets(client, { ...request, kind: 'definition' })\n}\n\nexport async function requestNavigationTargets(\n client: LanguageServerFeatureRouter,\n request: NavigationRequest,\n): Promise<DefinitionResult> {\n if (request.kind === 'references') return requestReferences(client, request)\n\n const raw = await client.request<lsp.Location[] | lsp.Location | lsp.LocationLink[] | null>(\n REQUEST_METHODS[request.kind],\n {\n textDocument: { uri: request.uri },\n position: offsetToLspPositionInSnapshot(request.document, request.offset),\n } satisfies lsp.TextDocumentPositionParams,\n request.signal ? { signal: request.signal } : undefined,\n )\n const sourceRange = definitionSourceRange(raw, request)\n return { targets: definitionTargets(raw), ...(sourceRange ? { sourceRange } : {}) }\n}\n\nasync function requestReferences(\n client: LanguageServerFeatureRouter,\n request: NavigationRequest,\n): Promise<DefinitionResult> {\n const raw = await client.request<lsp.Location[] | null>(\n 'textDocument/references',\n {\n textDocument: { uri: request.uri },\n position: offsetToLspPositionInSnapshot(request.document, request.offset),\n context: {\n includeDeclaration: request.includeDeclaration ?? true,\n },\n } satisfies lsp.ReferenceParams,\n request.signal ? { signal: request.signal } : undefined,\n )\n return { targets: definitionTargets(raw) }\n}\n\n/**\n * Navigate the editor to `target` by translating its LSP range into\n * offsets in the editor's current text and applying the selection. The\n * caller is responsible for ensuring `editor.document` is the text of the same\n * document `target` refers to (i.e. a same-document jump); cross-document\n * jumps should be routed through `onOpenDefinition`, not this function.\n */\nexport function navigateToDefinition(\n target: LanguageServerDefinitionTarget,\n editor: NavigationEditor,\n): void {\n navigateToTarget(target, editor, SET_SELECTION_TIMING_NAME)\n}\n\nexport function navigateToTarget(\n target: LanguageServerDefinitionTarget,\n editor: NavigationEditor,\n timingName: string,\n): void {\n const start = lspPositionToOffsetInSnapshot(editor.document, target.range.start)\n const end = lspPositionToOffsetInSnapshot(editor.document, target.range.end)\n editor.setSelection(start, end, timingName, { revealBlock: 'center', revealOffset: start })\n editor.focusEditor()\n}\n\n/**\n * Pick the best target to jump to from a definition result. Prefers\n * targets inside the active document, then targets outside `node_modules`,\n * then any target; returns `null` when no targets were returned.\n */\nexport function preferredDefinitionTarget(\n activeUri: lsp.DocumentUri,\n result: DefinitionResult,\n): LanguageServerDefinitionTarget | null {\n return preferredTarget(activeUri, result.targets)\n}\n\nexport function preferredReferenceTarget(\n activeUri: lsp.DocumentUri,\n activeDocument: LspTextDocumentSnapshot,\n sourceOffset: number,\n result: DefinitionResult,\n): LanguageServerDefinitionTarget | null {\n const sourceRange =\n rangeAroundOffset(activeDocument, sourceOffset, identifierRangeAtOffset) ??\n ({ start: sourceOffset, end: sourceOffset } satisfies OffsetRange)\n const sameDocumentTargets = result.targets.flatMap((target) =>\n targetWithOffset(activeUri, activeDocument, target),\n )\n const nextTarget = sameDocumentTargets.find((target) => target.start > sourceRange.end)\n if (nextTarget) return nextTarget.target\n\n const otherTarget = sameDocumentTargets.find((target) => !rangesOverlap(sourceRange, target))\n return otherTarget?.target ?? preferredTarget(activeUri, result.targets)\n}\n\n/**\n * Pick the best target to render a ctrl/cmd-hover definition link for.\n * Same preference rules as {@link preferredDefinitionTarget} but filters\n * out targets that would point back at (or overlap with) the hovered\n * identifier itself — the user would not be able to \"jump\" to the\n * definition they are already looking at.\n */\nexport function preferredJumpableDefinitionTarget(\n activeUri: lsp.DocumentUri,\n activeDocument: LspTextDocumentSnapshot,\n sourceRange: OffsetRange,\n result: DefinitionResult,\n): LanguageServerDefinitionTarget | null {\n const targets = result.targets.filter(\n (target) => !targetIsSourceRange(activeUri, activeDocument, sourceRange, target),\n )\n return preferredTarget(activeUri, targets)\n}\n\nfunction preferredTarget(\n activeUri: lsp.DocumentUri,\n targets: readonly LanguageServerDefinitionTarget[],\n): LanguageServerDefinitionTarget | null {\n return (\n targets.find((target) => target.uri === activeUri) ??\n targets.find((target) => !target.path.includes('/node_modules/')) ??\n targets[0] ??\n null\n )\n}\n\nfunction targetWithOffset(\n activeUri: lsp.DocumentUri,\n activeDocument: LspTextDocumentSnapshot,\n target: LanguageServerDefinitionTarget,\n): readonly (OffsetRange & {\n readonly target: LanguageServerDefinitionTarget\n})[] {\n if (target.uri !== activeUri) return []\n\n return [\n {\n start: lspPositionToOffsetInSnapshot(activeDocument, target.range.start),\n end: lspPositionToOffsetInSnapshot(activeDocument, target.range.end),\n target,\n },\n ]\n}\n\nfunction targetIsSourceRange(\n activeUri: lsp.DocumentUri,\n activeDocument: LspTextDocumentSnapshot,\n sourceRange: OffsetRange,\n target: LanguageServerDefinitionTarget,\n): boolean {\n if (target.uri !== activeUri) return false\n\n const targetStart = lspPositionToOffsetInSnapshot(activeDocument, target.range.start)\n const targetEnd = lspPositionToOffsetInSnapshot(activeDocument, target.range.end)\n return rangesOverlap(sourceRange, { start: targetStart, end: targetEnd })\n}\n\nfunction rangesOverlap(left: OffsetRange, right: OffsetRange): boolean {\n return left.start < right.end && right.start < left.end\n}\n\nfunction definitionTargets(\n result: lsp.Location[] | lsp.Location | lsp.LocationLink[] | null,\n): readonly LanguageServerDefinitionTarget[] {\n if (!result) return []\n const items = Array.isArray(result) ? result : [result]\n return items.flatMap(definitionTarget)\n}\n\nfunction definitionSourceRange(\n result: lsp.Location[] | lsp.Location | lsp.LocationLink[] | null,\n request: DefinitionRequest,\n): OffsetRange | null {\n if (!result) return null\n const items = Array.isArray(result) ? result : [result]\n for (const item of items) {\n if (!('originSelectionRange' in item) || !item.originSelectionRange) continue\n const start = lspPositionToOffsetInSnapshot(request.document, item.originSelectionRange.start)\n const end = lspPositionToOffsetInSnapshot(request.document, item.originSelectionRange.end)\n if (request.offset >= start && request.offset < end) return { start, end }\n }\n return null\n}\n\nfunction definitionTarget(\n item: lsp.Location | lsp.LocationLink,\n): readonly LanguageServerDefinitionTarget[] {\n const uri = 'targetUri' in item ? item.targetUri : item.uri\n const range = 'targetSelectionRange' in item ? item.targetSelectionRange : item.range\n const fileName = documentUriToFileName(uri)\n if (!fileName) return []\n\n return [\n {\n uri,\n path: fileName.replace(/^\\/+/, ''),\n range,\n },\n ]\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TooltipAction, TooltipNote } from '@singapore-editor/plugin-ui/tooltip';
|
|
2
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
3
|
+
import type { LanguageServerDefinitionTarget } from './types';
|
|
4
|
+
export type OpenLocation = (target: LanguageServerDefinitionTarget) => void;
|
|
5
|
+
/** The lines a hover shows under its text for the diagnostics at that offset. */
|
|
6
|
+
export declare function diagnosticNotes(diagnostics: readonly lsp.Diagnostic[], openLocation: OpenLocation, actionsForDiagnostic?: (diagnostic: lsp.Diagnostic) => readonly TooltipAction[]): readonly TooltipNote[];
|
|
7
|
+
//# sourceMappingURL=diagnosticNotes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticNotes.d.ts","sourceRoot":"","sources":["../src/diagnosticNotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEZ,MAAM,qCAAqC,CAAA;AAC5C,OAAO,KAAK,KAAK,GAAG,MAAM,gCAAgC,CAAA;AAG1D,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,SAAS,CAAA;AAE7D,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAA;AAE3E,iFAAiF;AACjF,wBAAgB,eAAe,CAC7B,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,EACtC,YAAY,EAAE,YAAY,EAC1B,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,KAAK,SAAS,aAAa,EAAE,GAC9E,SAAS,WAAW,EAAE,CAIxB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { documentUriToFileName } from "./paths.js";
|
|
2
|
+
//#region src/diagnosticNotes.ts
|
|
3
|
+
/** The lines a hover shows under its text for the diagnostics at that offset. */
|
|
4
|
+
function diagnosticNotes(diagnostics, openLocation, actionsForDiagnostic) {
|
|
5
|
+
return diagnostics.map((diagnostic) => diagnosticNote(diagnostic, openLocation, actionsForDiagnostic?.(diagnostic)));
|
|
6
|
+
}
|
|
7
|
+
function diagnosticNote(diagnostic, openLocation, actions) {
|
|
8
|
+
return {
|
|
9
|
+
actions,
|
|
10
|
+
text: typeof diagnostic.message === "string" ? diagnostic.message : diagnostic.message.value,
|
|
11
|
+
source: diagnostic.source,
|
|
12
|
+
code: diagnostic.code === void 0 ? void 0 : String(diagnostic.code),
|
|
13
|
+
codeHref: diagnostic.codeDescription?.href,
|
|
14
|
+
related: (diagnostic.relatedInformation ?? []).flatMap((related) => relatedLink(related, openLocation))
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function relatedLink(related, openLocation) {
|
|
18
|
+
const fileName = documentUriToFileName(related.location.uri);
|
|
19
|
+
if (!fileName) return [];
|
|
20
|
+
const { start } = related.location.range;
|
|
21
|
+
return [{
|
|
22
|
+
label: `${basename(fileName)}(${start.line + 1}, ${start.character + 1}): `,
|
|
23
|
+
text: related.message,
|
|
24
|
+
open: () => openLocation({
|
|
25
|
+
uri: related.location.uri,
|
|
26
|
+
path: fileName.replace(/^\/+/, ""),
|
|
27
|
+
range: related.location.range
|
|
28
|
+
})
|
|
29
|
+
}];
|
|
30
|
+
}
|
|
31
|
+
function basename(fileName) {
|
|
32
|
+
return fileName.slice(fileName.lastIndexOf("/") + 1);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { diagnosticNotes };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=diagnosticNotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticNotes.js","names":[],"sources":["../src/diagnosticNotes.ts"],"sourcesContent":["import type {\n TooltipAction,\n TooltipNote,\n TooltipNoteLink,\n} from '@singapore-editor/plugin-ui/tooltip'\nimport type * as lsp from 'vscode-languageserver-protocol'\n\nimport { documentUriToFileName } from './paths'\nimport type { LanguageServerDefinitionTarget } from './types'\n\nexport type OpenLocation = (target: LanguageServerDefinitionTarget) => void\n\n/** The lines a hover shows under its text for the diagnostics at that offset. */\nexport function diagnosticNotes(\n diagnostics: readonly lsp.Diagnostic[],\n openLocation: OpenLocation,\n actionsForDiagnostic?: (diagnostic: lsp.Diagnostic) => readonly TooltipAction[],\n): readonly TooltipNote[] {\n return diagnostics.map((diagnostic) =>\n diagnosticNote(diagnostic, openLocation, actionsForDiagnostic?.(diagnostic)),\n )\n}\n\nfunction diagnosticNote(\n diagnostic: lsp.Diagnostic,\n openLocation: OpenLocation,\n actions?: readonly TooltipAction[],\n): TooltipNote {\n return {\n actions,\n text: typeof diagnostic.message === 'string' ? diagnostic.message : diagnostic.message.value,\n source: diagnostic.source,\n code: diagnostic.code === undefined ? undefined : String(diagnostic.code),\n codeHref: diagnostic.codeDescription?.href,\n related: (diagnostic.relatedInformation ?? []).flatMap((related) =>\n relatedLink(related, openLocation),\n ),\n }\n}\n\nfunction relatedLink(\n related: lsp.DiagnosticRelatedInformation,\n openLocation: OpenLocation,\n): readonly TooltipNoteLink[] {\n const fileName = documentUriToFileName(related.location.uri)\n if (!fileName) return []\n\n const { start } = related.location.range\n return [\n {\n label: `${basename(fileName)}(${start.line + 1}, ${start.character + 1}): `,\n text: related.message,\n open: () =>\n openLocation({\n uri: related.location.uri,\n path: fileName.replace(/^\\/+/, ''),\n range: related.location.range,\n }),\n },\n ]\n}\n\nfunction basename(fileName: string): string {\n return fileName.slice(fileName.lastIndexOf('/') + 1)\n}\n"],"mappings":";;;AAaA,SAAgB,gBACd,aACA,cACA,sBACwB;CACxB,OAAO,YAAY,KAAK,eACtB,eAAe,YAAY,cAAc,uBAAuB,UAAU,CAAC,CAC7E;AACF;AAEA,SAAS,eACP,YACA,cACA,SACa;CACb,OAAO;EACL;EACA,MAAM,OAAO,WAAW,YAAY,WAAW,WAAW,UAAU,WAAW,QAAQ;EACvF,QAAQ,WAAW;EACnB,MAAM,WAAW,SAAS,KAAA,IAAY,KAAA,IAAY,OAAO,WAAW,IAAI;EACxE,UAAU,WAAW,iBAAiB;EACtC,UAAU,WAAW,sBAAsB,CAAC,EAAA,CAAG,SAAS,YACtD,YAAY,SAAS,YAAY,CACnC;CACF;AACF;AAEA,SAAS,YACP,SACA,cAC4B;CAC5B,MAAM,WAAW,sBAAsB,QAAQ,SAAS,GAAG;CAC3D,IAAI,CAAC,UAAU,OAAO,CAAC;CAEvB,MAAM,EAAE,UAAU,QAAQ,SAAS;CACnC,OAAO,CACL;EACE,OAAO,GAAG,SAAS,QAAQ,EAAE,GAAG,MAAM,OAAO,EAAE,IAAI,MAAM,YAAY,EAAE;EACvE,MAAM,QAAQ;EACd,YACE,aAAa;GACX,KAAK,QAAQ,SAAS;GACtB,MAAM,SAAS,QAAQ,QAAQ,EAAE;GACjC,OAAO,QAAQ,SAAS;EAC1B,CAAC;CACL,CACF;AACF;AAEA,SAAS,SAAS,UAA0B;CAC1C,OAAO,SAAS,MAAM,SAAS,YAAY,GAAG,IAAI,CAAC;AACrD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type EditorContributionChange } from '@singapore-editor/core/extensions';
|
|
2
|
+
import { type LspTextDocumentSnapshot } from '@singapore-editor/lsp';
|
|
3
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
4
|
+
export type SnapshotDocumentSession = {
|
|
5
|
+
readonly previousDocument: LspTextDocumentSnapshot;
|
|
6
|
+
readonly nextDocument: LspTextDocumentSnapshot;
|
|
7
|
+
readonly change: EditorContributionChange | null;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A diagnostic whose range has been projected onto the post-edit text.
|
|
11
|
+
* Structurally identical to {@link lsp.Diagnostic}; the alias exists to
|
|
12
|
+
* document intent at call sites that consume already-projected results.
|
|
13
|
+
*/
|
|
14
|
+
export type ProjectedDiagnostic = lsp.Diagnostic;
|
|
15
|
+
export declare function projectDiagnosticsInSnapshot(diagnostics: readonly lsp.Diagnostic[], documentSession: SnapshotDocumentSession): readonly ProjectedDiagnostic[];
|
|
16
|
+
/**
|
|
17
|
+
* Diagnostic ranges resolved to offsets once per publish, so a pointer move over a large file
|
|
18
|
+
* compares numbers instead of walking the line index for every diagnostic.
|
|
19
|
+
*/
|
|
20
|
+
export type DiagnosticOffsetIndex = {
|
|
21
|
+
readonly diagnostics: readonly lsp.Diagnostic[];
|
|
22
|
+
readonly starts: readonly number[];
|
|
23
|
+
readonly ends: readonly number[];
|
|
24
|
+
};
|
|
25
|
+
export declare function indexDiagnosticOffsets(document: LspTextDocumentSnapshot, diagnostics: readonly lsp.Diagnostic[]): DiagnosticOffsetIndex;
|
|
26
|
+
/**
|
|
27
|
+
* The diagnostics whose range contains `offset`. Zero-width diagnostics match only their exact
|
|
28
|
+
* start offset.
|
|
29
|
+
*/
|
|
30
|
+
export declare function diagnosticsAtOffset(index: DiagnosticOffsetIndex, offset: number): readonly lsp.Diagnostic[];
|
|
31
|
+
//# sourceMappingURL=diagnosticProjection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticProjection.d.ts","sourceRoot":"","sources":["../src/diagnosticProjection.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,wBAAwB,EAE9B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,KAAK,GAAG,MAAM,gCAAgC,CAAA;AAE1D,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,gBAAgB,EAAE,uBAAuB,CAAA;IAClD,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAA;IAC9C,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAAA;CACjD,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,GAAG,CAAC,UAAU,CAAA;AAQhD,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,EACtC,eAAe,EAAE,uBAAuB,GACvC,SAAS,mBAAmB,EAAE,CAOhC;AAOD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,CAAA;IAC/C,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC,CAAA;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,uBAAuB,EACjC,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,GACrC,qBAAqB,CAQvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,GACb,SAAS,GAAG,CAAC,UAAU,EAAE,CAO3B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { lspPositionToOffsetInSnapshot, offsetToLspPositionInSnapshot } from "@singapore-editor/lsp";
|
|
2
|
+
import { projectDecorationRangeThroughEdits } from "@singapore-editor/core/extensions";
|
|
3
|
+
//#region src/diagnosticProjection.ts
|
|
4
|
+
/**
|
|
5
|
+
* A diagnostic describes text the server has already seen, so neither edge takes in what is typed
|
|
6
|
+
* against it — the character you add after a squiggle is not yet part of the problem it reports.
|
|
7
|
+
*/
|
|
8
|
+
var DIAGNOSTIC_STICKINESS = {
|
|
9
|
+
startBias: "right",
|
|
10
|
+
endBias: "left"
|
|
11
|
+
};
|
|
12
|
+
function projectDiagnosticsInSnapshot(diagnostics, documentSession) {
|
|
13
|
+
return projectDiagnosticsThroughSnapshotChange(documentSession.previousDocument, documentSession.nextDocument, diagnostics, documentSession.change);
|
|
14
|
+
}
|
|
15
|
+
function editsForChange(change) {
|
|
16
|
+
if (!change) return [];
|
|
17
|
+
return change.edits;
|
|
18
|
+
}
|
|
19
|
+
function indexDiagnosticOffsets(document, diagnostics) {
|
|
20
|
+
const starts = [];
|
|
21
|
+
const ends = [];
|
|
22
|
+
for (const diagnostic of diagnostics) {
|
|
23
|
+
starts.push(lspPositionToOffsetInSnapshot(document, diagnostic.range.start));
|
|
24
|
+
ends.push(lspPositionToOffsetInSnapshot(document, diagnostic.range.end));
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
diagnostics,
|
|
28
|
+
starts,
|
|
29
|
+
ends
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The diagnostics whose range contains `offset`. Zero-width diagnostics match only their exact
|
|
34
|
+
* start offset.
|
|
35
|
+
*/
|
|
36
|
+
function diagnosticsAtOffset(index, offset) {
|
|
37
|
+
const matches = [];
|
|
38
|
+
for (let position = 0; position < index.diagnostics.length; position += 1) {
|
|
39
|
+
if (!rangeContainsOffset(index.starts[position], index.ends[position], offset)) continue;
|
|
40
|
+
matches.push(index.diagnostics[position]);
|
|
41
|
+
}
|
|
42
|
+
return matches;
|
|
43
|
+
}
|
|
44
|
+
function rangeContainsOffset(start, end, offset) {
|
|
45
|
+
if (end > start) return offset >= start && offset <= end;
|
|
46
|
+
return offset === start;
|
|
47
|
+
}
|
|
48
|
+
function projectDiagnosticsThroughSnapshotChange(previousDocument, nextDocument, diagnostics, change) {
|
|
49
|
+
if (diagnostics.length === 0) return diagnostics;
|
|
50
|
+
const edits = editsForChange(change);
|
|
51
|
+
if (edits.length === 0) return [];
|
|
52
|
+
const projected = [];
|
|
53
|
+
for (const diagnostic of diagnostics) {
|
|
54
|
+
const next = projectDiagnosticThroughSnapshotEdits(previousDocument, nextDocument, diagnostic, edits);
|
|
55
|
+
if (next) projected.push(next);
|
|
56
|
+
}
|
|
57
|
+
return projected;
|
|
58
|
+
}
|
|
59
|
+
function projectDiagnosticThroughSnapshotEdits(previousDocument, nextDocument, diagnostic, edits) {
|
|
60
|
+
const start = lspPositionToOffsetInSnapshot(previousDocument, diagnostic.range.start);
|
|
61
|
+
const end = lspPositionToOffsetInSnapshot(previousDocument, diagnostic.range.end);
|
|
62
|
+
const range = projectDecorationRangeThroughEdits({
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
...DIAGNOSTIC_STICKINESS
|
|
66
|
+
}, edits);
|
|
67
|
+
if (!range) return null;
|
|
68
|
+
return {
|
|
69
|
+
...diagnostic,
|
|
70
|
+
range: {
|
|
71
|
+
start: offsetToLspPositionInSnapshot(nextDocument, range.start),
|
|
72
|
+
end: offsetToLspPositionInSnapshot(nextDocument, range.end)
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { diagnosticsAtOffset, indexDiagnosticOffsets, projectDiagnosticsInSnapshot };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=diagnosticProjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticProjection.js","names":[],"sources":["../src/diagnosticProjection.ts"],"sourcesContent":["import type { TextEdit } from '@singapore-editor/core/document'\nimport {\n type EditorContributionChange,\n projectDecorationRangeThroughEdits,\n} from '@singapore-editor/core/extensions'\nimport {\n lspPositionToOffsetInSnapshot,\n offsetToLspPositionInSnapshot,\n type LspTextDocumentSnapshot,\n} from '@singapore-editor/lsp'\nimport type * as lsp from 'vscode-languageserver-protocol'\n\nexport type SnapshotDocumentSession = {\n readonly previousDocument: LspTextDocumentSnapshot\n readonly nextDocument: LspTextDocumentSnapshot\n readonly change: EditorContributionChange | null\n}\n\n/**\n * A diagnostic whose range has been projected onto the post-edit text.\n * Structurally identical to {@link lsp.Diagnostic}; the alias exists to\n * document intent at call sites that consume already-projected results.\n */\nexport type ProjectedDiagnostic = lsp.Diagnostic\n\n/**\n * A diagnostic describes text the server has already seen, so neither edge takes in what is typed\n * against it — the character you add after a squiggle is not yet part of the problem it reports.\n */\nconst DIAGNOSTIC_STICKINESS = { startBias: 'right', endBias: 'left' } as const\n\nexport function projectDiagnosticsInSnapshot(\n diagnostics: readonly lsp.Diagnostic[],\n documentSession: SnapshotDocumentSession,\n): readonly ProjectedDiagnostic[] {\n return projectDiagnosticsThroughSnapshotChange(\n documentSession.previousDocument,\n documentSession.nextDocument,\n diagnostics,\n documentSession.change,\n )\n}\n\nfunction editsForChange(change: EditorContributionChange | null): readonly TextEdit[] {\n if (!change) return []\n return change.edits\n}\n\n/**\n * Diagnostic ranges resolved to offsets once per publish, so a pointer move over a large file\n * compares numbers instead of walking the line index for every diagnostic.\n */\nexport type DiagnosticOffsetIndex = {\n readonly diagnostics: readonly lsp.Diagnostic[]\n readonly starts: readonly number[]\n readonly ends: readonly number[]\n}\n\nexport function indexDiagnosticOffsets(\n document: LspTextDocumentSnapshot,\n diagnostics: readonly lsp.Diagnostic[],\n): DiagnosticOffsetIndex {\n const starts: number[] = []\n const ends: number[] = []\n for (const diagnostic of diagnostics) {\n starts.push(lspPositionToOffsetInSnapshot(document, diagnostic.range.start))\n ends.push(lspPositionToOffsetInSnapshot(document, diagnostic.range.end))\n }\n return { diagnostics, starts, ends }\n}\n\n/**\n * The diagnostics whose range contains `offset`. Zero-width diagnostics match only their exact\n * start offset.\n */\nexport function diagnosticsAtOffset(\n index: DiagnosticOffsetIndex,\n offset: number,\n): readonly lsp.Diagnostic[] {\n const matches: lsp.Diagnostic[] = []\n for (let position = 0; position < index.diagnostics.length; position += 1) {\n if (!rangeContainsOffset(index.starts[position]!, index.ends[position]!, offset)) continue\n matches.push(index.diagnostics[position]!)\n }\n return matches\n}\n\nfunction rangeContainsOffset(start: number, end: number, offset: number): boolean {\n if (end > start) return offset >= start && offset <= end\n return offset === start\n}\n\nfunction projectDiagnosticsThroughSnapshotChange(\n previousDocument: LspTextDocumentSnapshot,\n nextDocument: LspTextDocumentSnapshot,\n diagnostics: readonly lsp.Diagnostic[],\n change: EditorContributionChange | null,\n): readonly lsp.Diagnostic[] {\n if (diagnostics.length === 0) return diagnostics\n\n const edits = editsForChange(change)\n if (edits.length === 0) return []\n\n const projected: lsp.Diagnostic[] = []\n for (const diagnostic of diagnostics) {\n const next = projectDiagnosticThroughSnapshotEdits(\n previousDocument,\n nextDocument,\n diagnostic,\n edits,\n )\n if (next) projected.push(next)\n }\n\n return projected\n}\n\nfunction projectDiagnosticThroughSnapshotEdits(\n previousDocument: LspTextDocumentSnapshot,\n nextDocument: LspTextDocumentSnapshot,\n diagnostic: lsp.Diagnostic,\n edits: readonly TextEdit[],\n): lsp.Diagnostic | null {\n const start = lspPositionToOffsetInSnapshot(previousDocument, diagnostic.range.start)\n const end = lspPositionToOffsetInSnapshot(previousDocument, diagnostic.range.end)\n // An edit that reaches into a flagged span leaves the marker over whatever survives rather than\n // taking it away. This projection only has to hold until the server republishes, and deleting a\n // character out of a bad identifier rarely makes it good — a squiggle that blinks out under the\n // caret claims the problem is fixed, which is the more misleading of the two wrong answers. The\n // price, and the change from what shipped before this milestone, is that the painted extent can\n // be shorter than the one the server actually described.\n const range = projectDecorationRangeThroughEdits({ start, end, ...DIAGNOSTIC_STICKINESS }, edits)\n if (!range) return null\n\n return {\n ...diagnostic,\n range: {\n start: offsetToLspPositionInSnapshot(nextDocument, range.start),\n end: offsetToLspPositionInSnapshot(nextDocument, range.end),\n },\n }\n}\n"],"mappings":";;;;;;;AA6BA,IAAM,wBAAwB;CAAE,WAAW;CAAS,SAAS;AAAO;AAEpE,SAAgB,6BACd,aACA,iBACgC;CAChC,OAAO,wCACL,gBAAgB,kBAChB,gBAAgB,cAChB,aACA,gBAAgB,MAClB;AACF;AAEA,SAAS,eAAe,QAA8D;CACpF,IAAI,CAAC,QAAQ,OAAO,CAAC;CACrB,OAAO,OAAO;AAChB;AAYA,SAAgB,uBACd,UACA,aACuB;CACvB,MAAM,SAAmB,CAAC;CAC1B,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,cAAc,aAAa;EACpC,OAAO,KAAK,8BAA8B,UAAU,WAAW,MAAM,KAAK,CAAC;EAC3E,KAAK,KAAK,8BAA8B,UAAU,WAAW,MAAM,GAAG,CAAC;CACzE;CACA,OAAO;EAAE;EAAa;EAAQ;CAAK;AACrC;;;;;AAMA,SAAgB,oBACd,OACA,QAC2B;CAC3B,MAAM,UAA4B,CAAC;CACnC,KAAK,IAAI,WAAW,GAAG,WAAW,MAAM,YAAY,QAAQ,YAAY,GAAG;EACzE,IAAI,CAAC,oBAAoB,MAAM,OAAO,WAAY,MAAM,KAAK,WAAY,MAAM,GAAG;EAClF,QAAQ,KAAK,MAAM,YAAY,SAAU;CAC3C;CACA,OAAO;AACT;AAEA,SAAS,oBAAoB,OAAe,KAAa,QAAyB;CAChF,IAAI,MAAM,OAAO,OAAO,UAAU,SAAS,UAAU;CACrD,OAAO,WAAW;AACpB;AAEA,SAAS,wCACP,kBACA,cACA,aACA,QAC2B;CAC3B,IAAI,YAAY,WAAW,GAAG,OAAO;CAErC,MAAM,QAAQ,eAAe,MAAM;CACnC,IAAI,MAAM,WAAW,GAAG,OAAO,CAAC;CAEhC,MAAM,YAA8B,CAAC;CACrC,KAAK,MAAM,cAAc,aAAa;EACpC,MAAM,OAAO,sCACX,kBACA,cACA,YACA,KACF;EACA,IAAI,MAAM,UAAU,KAAK,IAAI;CAC/B;CAEA,OAAO;AACT;AAEA,SAAS,sCACP,kBACA,cACA,YACA,OACuB;CACvB,MAAM,QAAQ,8BAA8B,kBAAkB,WAAW,MAAM,KAAK;CACpF,MAAM,MAAM,8BAA8B,kBAAkB,WAAW,MAAM,GAAG;CAOhF,MAAM,QAAQ,mCAAmC;EAAE;EAAO;EAAK,GAAG;CAAsB,GAAG,KAAK;CAChG,IAAI,CAAC,OAAO,OAAO;CAEnB,OAAO;EACL,GAAG;EACH,OAAO;GACL,OAAO,8BAA8B,cAAc,MAAM,KAAK;GAC9D,KAAK,8BAA8B,cAAc,MAAM,GAAG;EAC5D;CACF;AACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type LspTextDocumentSnapshot } from '@singapore-editor/lsp';
|
|
2
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
3
|
+
import type { LanguageServerDiagnosticSummary, LanguageServerDiagnosticsFreshness } from './types';
|
|
4
|
+
export type LanguageServerDiagnosticSeverity = 'error' | 'warning' | 'information' | 'hint';
|
|
5
|
+
export type LanguageServerDiagnosticHighlightLayer = LanguageServerDiagnosticSeverity | 'deprecated' | 'unnecessary';
|
|
6
|
+
export type LanguageServerDiagnosticHighlightGroups = Readonly<Record<LanguageServerDiagnosticHighlightLayer, readonly DiagnosticHighlightRange[]>>;
|
|
7
|
+
type DiagnosticHighlightRange = {
|
|
8
|
+
readonly start: number;
|
|
9
|
+
readonly end: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function summarizeDiagnostics(uri: lsp.DocumentUri | null, version: number | null, diagnostics: readonly lsp.Diagnostic[], freshness?: LanguageServerDiagnosticsFreshness): LanguageServerDiagnosticSummary;
|
|
12
|
+
/** Several servers on one document: any answer still coming outranks the ones that arrived. */
|
|
13
|
+
export declare function combineDiagnosticsFreshness(values: readonly LanguageServerDiagnosticsFreshness[]): LanguageServerDiagnosticsFreshness;
|
|
14
|
+
export declare function diagnosticHighlightGroups(document: LspTextDocumentSnapshot, diagnostics: readonly lsp.Diagnostic[]): LanguageServerDiagnosticHighlightGroups;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACnG,OAAO,KAAK,KAAK,GAAG,MAAM,gCAAgC,CAAA;AAE1D,OAAO,KAAK,EAAE,+BAA+B,EAAE,kCAAkC,EAAE,MAAM,SAAS,CAAA;AAElG,MAAM,MAAM,gCAAgC,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;AAE3F,MAAM,MAAM,sCAAsC,GAC9C,gCAAgC,GAChC,YAAY,GACZ,aAAa,CAAA;AAEjB,MAAM,MAAM,uCAAuC,GAAG,QAAQ,CAC5D,MAAM,CAAC,sCAAsC,EAAE,SAAS,wBAAwB,EAAE,CAAC,CACpF,CAAA;AAED,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA;AAUD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,EAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,EACtC,SAAS,GAAE,kCAA8C,GACxD,+BAA+B,CAajC;AAUD,+FAA+F;AAC/F,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,kCAAkC,EAAE,GACpD,kCAAkC,CAGpC;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,uBAAuB,EACjC,WAAW,EAAE,SAAS,GAAG,CAAC,UAAU,EAAE,GACrC,uCAAuC,CAczC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { lspPositionToOffsetInSnapshot } from "@singapore-editor/lsp";
|
|
2
|
+
//#region src/diagnostics.ts
|
|
3
|
+
var ERROR = 1;
|
|
4
|
+
var WARNING = 2;
|
|
5
|
+
var INFORMATION = 3;
|
|
6
|
+
var HINT = 4;
|
|
7
|
+
var DIAGNOSTIC_TAG_UNNECESSARY = 1;
|
|
8
|
+
var DIAGNOSTIC_TAG_DEPRECATED = 2;
|
|
9
|
+
function summarizeDiagnostics(uri, version, diagnostics, freshness = "current") {
|
|
10
|
+
const counts = {
|
|
11
|
+
error: 0,
|
|
12
|
+
warning: 0,
|
|
13
|
+
information: 0,
|
|
14
|
+
hint: 0
|
|
15
|
+
};
|
|
16
|
+
for (const diagnostic of diagnostics) counts[severityForDiagnostic(diagnostic)] += 1;
|
|
17
|
+
return {
|
|
18
|
+
uri,
|
|
19
|
+
version,
|
|
20
|
+
diagnostics,
|
|
21
|
+
counts: {
|
|
22
|
+
...counts,
|
|
23
|
+
total: diagnostics.length
|
|
24
|
+
},
|
|
25
|
+
freshness
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
var FRESHNESS_PRECEDENCE = [
|
|
29
|
+
"awaiting",
|
|
30
|
+
"refreshing",
|
|
31
|
+
"current",
|
|
32
|
+
"silent",
|
|
33
|
+
"unavailable"
|
|
34
|
+
];
|
|
35
|
+
/** Several servers on one document: any answer still coming outranks the ones that arrived. */
|
|
36
|
+
function combineDiagnosticsFreshness(values) {
|
|
37
|
+
for (const freshness of FRESHNESS_PRECEDENCE) if (values.includes(freshness)) return freshness;
|
|
38
|
+
return "silent";
|
|
39
|
+
}
|
|
40
|
+
function diagnosticHighlightGroups(document, diagnostics) {
|
|
41
|
+
const groups = emptyHighlightGroups();
|
|
42
|
+
for (const diagnostic of diagnostics) {
|
|
43
|
+
const range = highlightRangeForDiagnostic(document, diagnostic);
|
|
44
|
+
if (!range) continue;
|
|
45
|
+
const unnecessary = diagnostic.tags?.includes(DIAGNOSTIC_TAG_UNNECESSARY);
|
|
46
|
+
const severity = severityForDiagnostic(diagnostic);
|
|
47
|
+
if (!unnecessary || severity !== "hint") groups[severity].push(range);
|
|
48
|
+
if (unnecessary) groups.unnecessary.push(range);
|
|
49
|
+
if (diagnostic.tags?.includes(DIAGNOSTIC_TAG_DEPRECATED)) groups.deprecated.push(range);
|
|
50
|
+
}
|
|
51
|
+
return groups;
|
|
52
|
+
}
|
|
53
|
+
function highlightRangeForDiagnostic(document, diagnostic) {
|
|
54
|
+
const start = lspPositionToOffsetInSnapshot(document, diagnostic.range.start);
|
|
55
|
+
const end = lspPositionToOffsetInSnapshot(document, diagnostic.range.end);
|
|
56
|
+
if (end > start) return {
|
|
57
|
+
start,
|
|
58
|
+
end
|
|
59
|
+
};
|
|
60
|
+
return expandEmptyRange(document.textSnapshot.length, start);
|
|
61
|
+
}
|
|
62
|
+
function expandEmptyRange(length, offset) {
|
|
63
|
+
if (length === 0) return null;
|
|
64
|
+
if (offset < length) return {
|
|
65
|
+
start: offset,
|
|
66
|
+
end: offset + 1
|
|
67
|
+
};
|
|
68
|
+
if (offset > 0) return {
|
|
69
|
+
start: offset - 1,
|
|
70
|
+
end: offset
|
|
71
|
+
};
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function emptyHighlightGroups() {
|
|
75
|
+
return {
|
|
76
|
+
error: [],
|
|
77
|
+
warning: [],
|
|
78
|
+
information: [],
|
|
79
|
+
hint: [],
|
|
80
|
+
deprecated: [],
|
|
81
|
+
unnecessary: []
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function severityForDiagnostic(diagnostic) {
|
|
85
|
+
if (diagnostic.severity === WARNING) return "warning";
|
|
86
|
+
if (diagnostic.severity === INFORMATION) return "information";
|
|
87
|
+
if (diagnostic.severity === HINT) return "hint";
|
|
88
|
+
if (diagnostic.severity === ERROR) return "error";
|
|
89
|
+
return "error";
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { combineDiagnosticsFreshness, diagnosticHighlightGroups, summarizeDiagnostics };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","names":[],"sources":["../src/diagnostics.ts"],"sourcesContent":["import { lspPositionToOffsetInSnapshot, type LspTextDocumentSnapshot } from '@singapore-editor/lsp'\nimport type * as lsp from 'vscode-languageserver-protocol'\n\nimport type { LanguageServerDiagnosticSummary, LanguageServerDiagnosticsFreshness } from './types'\n\nexport type LanguageServerDiagnosticSeverity = 'error' | 'warning' | 'information' | 'hint'\n\nexport type LanguageServerDiagnosticHighlightLayer =\n | LanguageServerDiagnosticSeverity\n | 'deprecated'\n | 'unnecessary'\n\nexport type LanguageServerDiagnosticHighlightGroups = Readonly<\n Record<LanguageServerDiagnosticHighlightLayer, readonly DiagnosticHighlightRange[]>\n>\n\ntype DiagnosticHighlightRange = {\n readonly start: number\n readonly end: number\n}\n\nconst ERROR = 1\nconst WARNING = 2\nconst INFORMATION = 3\nconst HINT = 4\n\nconst DIAGNOSTIC_TAG_UNNECESSARY = 1\nconst DIAGNOSTIC_TAG_DEPRECATED = 2\n\nexport function summarizeDiagnostics(\n uri: lsp.DocumentUri | null,\n version: number | null,\n diagnostics: readonly lsp.Diagnostic[],\n freshness: LanguageServerDiagnosticsFreshness = 'current',\n): LanguageServerDiagnosticSummary {\n const counts = { error: 0, warning: 0, information: 0, hint: 0 }\n for (const diagnostic of diagnostics) counts[severityForDiagnostic(diagnostic)] += 1\n return {\n uri,\n version,\n diagnostics,\n counts: {\n ...counts,\n total: diagnostics.length,\n },\n freshness,\n }\n}\n\nconst FRESHNESS_PRECEDENCE: readonly LanguageServerDiagnosticsFreshness[] = [\n 'awaiting',\n 'refreshing',\n 'current',\n 'silent',\n 'unavailable',\n]\n\n/** Several servers on one document: any answer still coming outranks the ones that arrived. */\nexport function combineDiagnosticsFreshness(\n values: readonly LanguageServerDiagnosticsFreshness[],\n): LanguageServerDiagnosticsFreshness {\n for (const freshness of FRESHNESS_PRECEDENCE) if (values.includes(freshness)) return freshness\n return 'silent'\n}\n\nexport function diagnosticHighlightGroups(\n document: LspTextDocumentSnapshot,\n diagnostics: readonly lsp.Diagnostic[],\n): LanguageServerDiagnosticHighlightGroups {\n const groups = emptyHighlightGroups()\n\n for (const diagnostic of diagnostics) {\n const range = highlightRangeForDiagnostic(document, diagnostic)\n if (!range) continue\n const unnecessary = diagnostic.tags?.includes(DIAGNOSTIC_TAG_UNNECESSARY)\n const severity = severityForDiagnostic(diagnostic)\n if (!unnecessary || severity !== 'hint') groups[severity].push(range)\n if (unnecessary) groups.unnecessary.push(range)\n if (diagnostic.tags?.includes(DIAGNOSTIC_TAG_DEPRECATED)) groups.deprecated.push(range)\n }\n\n return groups\n}\n\nfunction highlightRangeForDiagnostic(\n document: LspTextDocumentSnapshot,\n diagnostic: lsp.Diagnostic,\n): DiagnosticHighlightRange | null {\n const start = lspPositionToOffsetInSnapshot(document, diagnostic.range.start)\n const end = lspPositionToOffsetInSnapshot(document, diagnostic.range.end)\n if (end > start) return { start, end }\n return expandEmptyRange(document.textSnapshot.length, start)\n}\n\nfunction expandEmptyRange(length: number, offset: number): DiagnosticHighlightRange | null {\n if (length === 0) return null\n if (offset < length) return { start: offset, end: offset + 1 }\n if (offset > 0) return { start: offset - 1, end: offset }\n return null\n}\n\nfunction emptyHighlightGroups(): Record<\n LanguageServerDiagnosticHighlightLayer,\n DiagnosticHighlightRange[]\n> {\n return {\n error: [],\n warning: [],\n information: [],\n hint: [],\n deprecated: [],\n unnecessary: [],\n }\n}\n\nfunction severityForDiagnostic(diagnostic: lsp.Diagnostic): LanguageServerDiagnosticSeverity {\n if (diagnostic.severity === WARNING) return 'warning'\n if (diagnostic.severity === INFORMATION) return 'information'\n if (diagnostic.severity === HINT) return 'hint'\n if (diagnostic.severity === ERROR) return 'error'\n return 'error'\n}\n"],"mappings":";;AAqBA,IAAM,QAAQ;AACd,IAAM,UAAU;AAChB,IAAM,cAAc;AACpB,IAAM,OAAO;AAEb,IAAM,6BAA6B;AACnC,IAAM,4BAA4B;AAElC,SAAgB,qBACd,KACA,SACA,aACA,YAAgD,WACf;CACjC,MAAM,SAAS;EAAE,OAAO;EAAG,SAAS;EAAG,aAAa;EAAG,MAAM;CAAE;CAC/D,KAAK,MAAM,cAAc,aAAa,OAAO,sBAAsB,UAAU,MAAM;CACnF,OAAO;EACL;EACA;EACA;EACA,QAAQ;GACN,GAAG;GACH,OAAO,YAAY;EACrB;EACA;CACF;AACF;AAEA,IAAM,uBAAsE;CAC1E;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,4BACd,QACoC;CACpC,KAAK,MAAM,aAAa,sBAAsB,IAAI,OAAO,SAAS,SAAS,GAAG,OAAO;CACrF,OAAO;AACT;AAEA,SAAgB,0BACd,UACA,aACyC;CACzC,MAAM,SAAS,qBAAqB;CAEpC,KAAK,MAAM,cAAc,aAAa;EACpC,MAAM,QAAQ,4BAA4B,UAAU,UAAU;EAC9D,IAAI,CAAC,OAAO;EACZ,MAAM,cAAc,WAAW,MAAM,SAAS,0BAA0B;EACxE,MAAM,WAAW,sBAAsB,UAAU;EACjD,IAAI,CAAC,eAAe,aAAa,QAAQ,OAAO,SAAS,CAAC,KAAK,KAAK;EACpE,IAAI,aAAa,OAAO,YAAY,KAAK,KAAK;EAC9C,IAAI,WAAW,MAAM,SAAS,yBAAyB,GAAG,OAAO,WAAW,KAAK,KAAK;CACxF;CAEA,OAAO;AACT;AAEA,SAAS,4BACP,UACA,YACiC;CACjC,MAAM,QAAQ,8BAA8B,UAAU,WAAW,MAAM,KAAK;CAC5E,MAAM,MAAM,8BAA8B,UAAU,WAAW,MAAM,GAAG;CACxE,IAAI,MAAM,OAAO,OAAO;EAAE;EAAO;CAAI;CACrC,OAAO,iBAAiB,SAAS,aAAa,QAAQ,KAAK;AAC7D;AAEA,SAAS,iBAAiB,QAAgB,QAAiD;CACzF,IAAI,WAAW,GAAG,OAAO;CACzB,IAAI,SAAS,QAAQ,OAAO;EAAE,OAAO;EAAQ,KAAK,SAAS;CAAE;CAC7D,IAAI,SAAS,GAAG,OAAO;EAAE,OAAO,SAAS;EAAG,KAAK;CAAO;CACxD,OAAO;AACT;AAEA,SAAS,uBAGP;CACA,OAAO;EACL,OAAO,CAAC;EACR,SAAS,CAAC;EACV,aAAa,CAAC;EACd,MAAM,CAAC;EACP,YAAY,CAAC;EACb,aAAa,CAAC;CAChB;AACF;AAEA,SAAS,sBAAsB,YAA8D;CAC3F,IAAI,WAAW,aAAa,SAAS,OAAO;CAC5C,IAAI,WAAW,aAAa,aAAa,OAAO;CAChD,IAAI,WAAW,aAAa,MAAM,OAAO;CACzC,IAAI,WAAW,aAAa,OAAO,OAAO;CAC1C,OAAO;AACT"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type EditorViewContributionContext } from '@singapore-editor/core/extensions';
|
|
2
|
+
import { type LspTextDocumentSnapshot } from '@singapore-editor/lsp';
|
|
3
|
+
import { type EditorTheme } from '@singapore-editor/core/rendering';
|
|
4
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
5
|
+
import { summarizeDiagnostics } from './diagnostics';
|
|
6
|
+
import type { LanguageServerDiagnosticMarkerClaim, LanguageServerDiagnosticMarkerEvent, LanguageServerDiagnosticSummary, LanguageServerDiagnosticsFreshness } from './types';
|
|
7
|
+
export { viewDocumentSnapshot } from './viewDocumentSnapshot';
|
|
8
|
+
export type DiagnosticsPresenterActiveDocument = LspTextDocumentSnapshot & {
|
|
9
|
+
readonly textVersion: number;
|
|
10
|
+
readonly uri: lsp.DocumentUri;
|
|
11
|
+
};
|
|
12
|
+
export type DiagnosticsPresenterMarkerDirection = 'next' | 'previous';
|
|
13
|
+
export type DiagnosticsPresenterOptions = {
|
|
14
|
+
readonly minimapSourceId: string;
|
|
15
|
+
readonly highlightNameNamespace: string;
|
|
16
|
+
readonly markerTimingNamePrefix: string;
|
|
17
|
+
readonly onDiagnostics?: (summary: ReturnType<typeof summarizeDiagnostics>) => void;
|
|
18
|
+
readonly onDidNavigateDiagnostic?: (event: LanguageServerDiagnosticMarkerEvent) => LanguageServerDiagnosticMarkerClaim;
|
|
19
|
+
readonly onError?: (error: unknown) => void;
|
|
20
|
+
};
|
|
21
|
+
export declare class DiagnosticsPresenter {
|
|
22
|
+
private readonly context;
|
|
23
|
+
private readonly options;
|
|
24
|
+
private readonly highlightNames;
|
|
25
|
+
private markerClaim;
|
|
26
|
+
private unnecessaryDimCache;
|
|
27
|
+
constructor(context: EditorViewContributionContext, prefix: string, options: DiagnosticsPresenterOptions);
|
|
28
|
+
render(document: LspTextDocumentSnapshot, diagnostics: readonly lsp.Diagnostic[]): void;
|
|
29
|
+
invalidateTheme(): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
publishSummary(uri: lsp.DocumentUri, version: number | null, diagnostics: readonly lsp.Diagnostic[]): void;
|
|
32
|
+
moveMarker(active: DiagnosticsPresenterActiveDocument | null, diagnostics: readonly lsp.Diagnostic[], direction: DiagnosticsPresenterMarkerDirection): boolean;
|
|
33
|
+
private publishMarker;
|
|
34
|
+
private releaseMarkerClaim;
|
|
35
|
+
private renderHighlights;
|
|
36
|
+
private unnecessaryDim;
|
|
37
|
+
private renderMinimapMarkers;
|
|
38
|
+
private clearMinimapMarkers;
|
|
39
|
+
private minimapFeature;
|
|
40
|
+
}
|
|
41
|
+
export type CompositeDiagnosticsLanePresenter = {
|
|
42
|
+
clear(): void;
|
|
43
|
+
render(document: LspTextDocumentSnapshot, diagnostics: readonly lsp.Diagnostic[]): void;
|
|
44
|
+
publishSummary(uri: lsp.DocumentUri, version: number | null, diagnostics: readonly lsp.Diagnostic[], freshness: LanguageServerDiagnosticsFreshness): void;
|
|
45
|
+
};
|
|
46
|
+
export declare class CompositeDiagnosticsPresenter {
|
|
47
|
+
#private;
|
|
48
|
+
private readonly presenter;
|
|
49
|
+
private readonly laneIds;
|
|
50
|
+
private readonly onDiagnostics?;
|
|
51
|
+
private theme;
|
|
52
|
+
updateTheme(theme: EditorTheme | null): void;
|
|
53
|
+
constructor(presenter: DiagnosticsPresenter, laneIds: readonly string[], onDiagnostics?: ((summary: LanguageServerDiagnosticSummary) => void) | undefined);
|
|
54
|
+
get diagnostics(): readonly lsp.Diagnostic[];
|
|
55
|
+
forLane(laneId: string, onDiagnostics?: (summary: LanguageServerDiagnosticSummary) => void): CompositeDiagnosticsLanePresenter;
|
|
56
|
+
clear(): void;
|
|
57
|
+
moveMarker(active: DiagnosticsPresenterActiveDocument | null, direction: DiagnosticsPresenterMarkerDirection): boolean;
|
|
58
|
+
private renderCombined;
|
|
59
|
+
private refreshDiagnostics;
|
|
60
|
+
private publishCombinedSummary;
|
|
61
|
+
private currentDocument;
|
|
62
|
+
private currentBatch;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=diagnosticsPresenter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticsPresenter.d.ts","sourceRoot":"","sources":["../src/diagnosticsPresenter.ts"],"names":[],"mappings":"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"}
|