diff --git a/e2e/usage-highlight-contrast.spec.ts b/e2e/usage-highlight-contrast.spec.ts
new file mode 100644
index 000000000..c10e212ea
--- /dev/null
+++ b/e2e/usage-highlight-contrast.spec.ts
@@ -0,0 +1,81 @@
+import { expect, test } from "./fixtures";
+
+const themes = [
+ { theme: "light", scheme: "theme" },
+ { theme: "dark", scheme: "theme" },
+ { theme: "dark-hc", scheme: "theme" },
+ { theme: "light", scheme: "dark" },
+ { theme: "dark", scheme: "light" },
+] as const;
+
+for (const { theme, scheme } of themes) {
+ test(`usage highlights have readable text with ${theme} theme and ${scheme} scheme`, async ({
+ workbench: page,
+ }, info) => {
+ await page.evaluate(
+ async ({ theme, scheme }) => {
+ const themePath = "/src/workbench/theme.ts";
+ const schemePath = "/src/platform/colorScheme.ts";
+ const usagesPath = "/src/features/code-insight/usages.ts";
+ const { applyThemeAttributes } = await import(themePath);
+ const { schemeCss } = await import(schemePath);
+ const { useUsagesPopup } = await import(usagesPath);
+ applyThemeAttributes(theme);
+ const style = document.createElement("style");
+ style.textContent = schemeCss(scheme);
+ document.head.append(style);
+ const symbol = "trainingSessionFetch";
+ const texts = [
+ `export function ${symbol}() {}`,
+ `import { ${symbol} } from './api';`,
+ `// Call ${symbol} here`,
+ `const name = '${symbol}';`,
+ ];
+ useUsagesPopup.setState({
+ open: true,
+ anchor: { x: 100, y: 100 },
+ symbol,
+ groups: texts.map((text, index) => ({
+ path: `/contrast/example-${index}.ts`,
+ usages: [
+ {
+ path: `/contrast/example-${index}.ts`,
+ line: 0,
+ col: text.indexOf(symbol),
+ endCol: text.indexOf(symbol) + symbol.length,
+ text,
+ },
+ ],
+ })),
+ note: null,
+ });
+ },
+ { theme, scheme },
+ );
+ const popup = page.getByRole("dialog", { name: "Usages", exact: true });
+ await expect(popup.locator(".tok-comment").first()).toBeVisible();
+ await expect(popup.locator("mark")).toHaveCount(4);
+ const ratios = await popup.locator("mark").evaluateAll((marks) => {
+ const luminance = (color: string) => {
+ const channels = color
+ .match(/[\d.]+/g)!
+ .slice(0, 3)
+ .map(Number)
+ .map((value) => {
+ const channel = value / 255;
+ return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
+ });
+ return channels[0]! * 0.2126 + channels[1]! * 0.7152 + channels[2]! * 0.0722;
+ };
+ return marks.flatMap((mark) => {
+ const background = luminance(getComputedStyle(mark).backgroundColor);
+ return [mark, ...mark.querySelectorAll("*")].map((node) => {
+ const foreground = luminance(getComputedStyle(node).color);
+ return (Math.max(background, foreground) + 0.05) / (Math.min(background, foreground) + 0.05);
+ });
+ });
+ });
+ for (const ratio of ratios) expect(ratio).toBeGreaterThanOrEqual(4.5);
+ await popup.screenshot({ path: info.outputPath("usage-highlights.png") });
+ });
+}
diff --git a/src/features/code-insight/UsagesView.tsx b/src/features/code-insight/UsagesView.tsx
index ca7ea44c0..0f7795054 100644
--- a/src/features/code-insight/UsagesView.tsx
+++ b/src/features/code-insight/UsagesView.tsx
@@ -54,7 +54,7 @@ export function UsagePreview({ usage }: { usage: Usage }) {
return (
{tokenSpans(text, 0, from, tokens, "b:")}
- {tokenSpans(text, from, to, tokens, "m:")}
+ {text.slice(from, to)}
{tokenSpans(text, to, text.length, tokens, "a:")}
);
diff --git a/src/features/code-insight/usagePreview.test.tsx b/src/features/code-insight/usagePreview.test.tsx
index 7c276925d..79e3b8c23 100644
--- a/src/features/code-insight/usagePreview.test.tsx
+++ b/src/features/code-insight/usagePreview.test.tsx
@@ -17,14 +17,17 @@ describe("UsagePreview", () => {
);
await waitFor(() => expect(container.querySelector(".tok-keyword")?.textContent).toBe("const"));
expect(container.querySelector("mark")?.textContent).toBe("foo");
+ expect(container.querySelector("mark")?.childElementCount).toBe(0);
expect(container.textContent).toBe("const x = foo();");
});
- it("colours a usage inside a doc comment as a comment", async () => {
+ it("keeps comment colours outside the match and the search foreground inside it", async () => {
const { container } = render(
,
);
- await waitFor(() => expect(container.querySelector("mark .tok-comment")?.textContent).toBe("foo"));
+ await waitFor(() => expect(container.querySelector(".tok-comment")?.textContent).toBe("* Uses "));
+ expect(container.querySelector("mark")?.textContent).toBe("foo");
+ expect(container.querySelector("mark")?.childElementCount).toBe(0);
});
});