Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/vscode-extension/src/diagnosticCacheKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type {
Diagnostic,
DiagnosticRelatedInformation,
} from "vscode-languageserver-types";

export function getDiagnosticCacheKey(diagnostic: Diagnostic): string {
return JSON.stringify({
message: diagnostic.message,
code: diagnostic.code,
range: diagnostic.range,
relatedInformation:
diagnostic.relatedInformation?.map(getRelatedInformationCacheKey) ?? [],
});
}

function getRelatedInformationCacheKey({
location,
message,
}: DiagnosticRelatedInformation) {
return {
message,
location: {
uri: location.uri,
range: location.range,
},
};
}
8 changes: 5 additions & 3 deletions apps/vscode-extension/src/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "./formattedDiagnosticsStore";
import { logger } from "./logger";
import { enabledCommands } from "./commands/enabledCommands";
import { getDiagnosticCacheKey } from "./diagnosticCacheKey";

/**
* The list of diagnostic sources that pretty-ts-errors supports
Expand Down Expand Up @@ -84,7 +85,7 @@ export function registerOnDidChangeDiagnostics(context: ExtensionContext) {
const CACHE_SIZE_MAX = 100;

/**
* A local cache that maps TS diagnostics as `string` to their formatted `MarkdownString` counter part.
* A local cache that maps TS diagnostics to their formatted `MarkdownString` counter part.
* @see https://github.com/yoavbls/pretty-ts-errors/pull/62
*
* One reason this cache is critical is because the TypeScript Language Features extension is very noisy and will constantly push all diagnostics for a file,
Expand All @@ -103,8 +104,9 @@ async function getFormattedDiagnostic(
// formatDiagnosticForHover converts message based on LSP Diagnostic type, not VSCode Diagnostic type, so it can be used in other IDEs.
// Here we convert VSCode Diagnostic to LSP Diagnostic to make formatDiagnosticForHover recognize it.
const lspDiagnostic = converter.asDiagnostic(diagnostic);
const cacheKey = getDiagnosticCacheKey(lspDiagnostic);

let formattedMessage = cache.get(diagnostic.message);
let formattedMessage = cache.get(cacheKey);
if (!formattedMessage) {
const formattedDiagnostic = await prettifyDiagnosticForHover(lspDiagnostic);
const markdownString = new MarkdownString(formattedDiagnostic);
Expand All @@ -117,7 +119,7 @@ async function getFormattedDiagnostic(
const firstCacheKey = cache.keys().next().value!;
cache.delete(firstCacheKey);
}
cache.set(diagnostic.message, formattedMessage);
cache.set(cacheKey, formattedMessage);
}

return {
Expand Down
3 changes: 2 additions & 1 deletion apps/vscode-extension/src/provider/webviewViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from "@pretty-ts-errors/vscode-formatter";
import { SUPPORTED_LANGUAGE_IDS } from "../supportedLanguageIds";
import { logger } from "../logger";
import { getDiagnosticCacheKey } from "../diagnosticCacheKey";

const NO_DIAGNOSTICS_MESSAGE =
"Select code with an error to show the prettified diagnostic in this view.";
Expand Down Expand Up @@ -81,7 +82,7 @@ export function registerWebviewViewProvider(context: ExtensionContext) {
async function diagnosticToItem(
formattedDiagnostic: FormattedDiagnostic
): Promise<DiagnosticItem> {
const cacheKey = formattedDiagnostic.lspDiagnostic.message;
const cacheKey = getDiagnosticCacheKey(formattedDiagnostic.lspDiagnostic);
let html = sidebarHtmlCache.get(cacheKey);
if (!html) {
html = await prettifyDiagnosticForSidebar(
Expand Down
53 changes: 53 additions & 0 deletions apps/vscode-extension/src/test/suite/diagnosticCacheKey.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { strict as assert } from "node:assert";
import type { Diagnostic } from "vscode-languageserver-types";
import { getDiagnosticCacheKey } from "../../diagnosticCacheKey";

suite("getDiagnosticCacheKey", () => {
test("distinguishes duplicate messages that link to different symbols", () => {
const firstKey = getDiagnosticCacheKey(
diagnosticWithRelatedInformation("file:///workspace/example.ts", 1)
);
const secondKey = getDiagnosticCacheKey(
diagnosticWithRelatedInformation("file:///workspace/other.ts", 1)
);

assert.notEqual(firstKey, secondKey);
});

test("distinguishes duplicate messages at different ranges", () => {
const firstKey = getDiagnosticCacheKey(
diagnosticWithRelatedInformation("file:///workspace/example.ts", 1)
);
const secondKey = getDiagnosticCacheKey(
diagnosticWithRelatedInformation("file:///workspace/example.ts", 8)
);

assert.notEqual(firstKey, secondKey);
});
});

function diagnosticWithRelatedInformation(
symbolUri: string,
startCharacter: number
): Diagnostic {
return {
message: "Cannot find name 'Person'.",
code: 2304,
range: {
start: { line: 0, character: startCharacter },
end: { line: 0, character: startCharacter + 6 },
},
relatedInformation: [
{
message: "'Person' is declared here.",
location: {
uri: symbolUri,
range: {
start: { line: 3, character: 12 },
end: { line: 3, character: 18 },
},
},
},
],
};
}