From 001381459ec8aa75e4beedbc329facc5390b401c Mon Sep 17 00:00:00 2001
From: Lee Dong Geon <100822592+ldg030201@users.noreply.github.com>
Date: Thu, 5 Nov 2026 11:48:21 +0900
Subject: [PATCH] fix(themes): match IntelliJ's gutter, selected tab border and
completion colours on import (#1424)
Themes imported from IntelliJ drew a lighter gutter strip and kept the built-in blue for the selected tab's border and completion matches. The converter read the classic UI's GUTTER_BACKGROUND, where the new UI paints EDITOR_GUTTER_BACKGROUND or else the editor's background; it read EditorTabs.underlineColor, where Islands borders the selected tab with underlinedBorderColor; and it didn't map CompletionPopup.matchForeground. Scheme attributes written as `` (no colours, not inherited) now show as plain text instead of keeping the built-in colours.
Co-authored-by: ldg030201 <100822592+ldg030201@users.noreply.github.com>
---
docs/plugins/themes.md | 9 ++++++--
scripts/theme/theme.test.ts | 12 ++++++++++
src/features/plugins/intellijTheme.ts | 33 ++++++++++++++++++++-------
3 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/docs/plugins/themes.md b/docs/plugins/themes.md
index f1bff5350..e28db7a85 100644
--- a/docs/plugins/themes.md
+++ b/docs/plugins/themes.md
@@ -192,7 +192,9 @@ ship no `.icls` at all keep the built-in syntax colours.
| `--danger`, `--warning` | `Component.errorFocusColor`, `Component.warningFocusColor` |
| `--bg-tooltip`, `--fg-tooltip`, `--border-tooltip` | `ToolTip.background`, `.foreground`, `.borderColor` |
| `--bg-button`, `--border-button` | `Button.startBackground`, `Button.startBorderColor` |
-| `--bg-tab-selected`, `--border-tab-selected` | `EditorTabs.underlinedTabBackground`, `EditorTabs.underlineColor` |
+| `--bg-tab-selected` | `EditorTabs.underlinedTabBackground` |
+| `--border-tab-selected` | `EditorTabs.underlinedBorderColor` (Islands), `EditorTabs.underlineColor` |
+| `--fg-completion-match` | `CompletionPopup.matchForeground` |
| `--bg-toolbar-hover`, `--bg-toolbar-pressed` | `ActionButton.hoverBackground`, `ActionButton.pressedBackground` |
| `--bg-stripe-hover`, `--bg-stripe-selected` | `ToolWindow.Button.hoverBackground`, `.selectedBackground` |
| `--scrollbar-thumb`, `--scrollbar-thumb-hover` | `ScrollBar.Mac.thumbColor`, `ScrollBar.Mac.hoverThumbColor` |
@@ -209,6 +211,7 @@ The full table is `UI_KEYS` in [`src/features/plugins/intellijTheme.ts`](../../s
| `--caret`, `--bg-current-line` | colour `CARET_COLOR`, `CARET_ROW_COLOR` |
| `--bg-editor-selection` | colour `SELECTION_BACKGROUND` |
| `--fg-line-number`, `--fg-line-number-current` | colour `LINE_NUMBERS_COLOR`, `LINE_NUMBER_ON_CARET_ROW_COLOR` |
+| `--bg-editor-gutter` | colour `EDITOR_GUTTER_BACKGROUND`, else the editor background (as the new UI paints it; `GUTTER_BACKGROUND` is the classic UI's) |
| `--border-indent-guide`, `--border-right-margin`, `--fg-whitespace` | colour `INDENT_GUIDE`, `RIGHT_MARGIN_COLOR`, `WHITESPACES` |
| `--vcs-*`, `--gutter-*` | colour `FILESTATUS_*`, `MODIFIED_LINES_COLOR`, `ADDED_LINES_COLOR`, `DELETED_LINES_COLOR` |
| `--syn-keyword`, `--syn-string`, `--syn-number` | `DEFAULT_KEYWORD`, `DEFAULT_STRING`, `DEFAULT_NUMBER` |
@@ -226,7 +229,9 @@ The full table is `UI_KEYS` in [`src/features/plugins/intellijTheme.ts`](../../s
| `--syn-self`, `--syn-keyword-arg`, `--syn-docstring`, `--syn-predefined` | `PY.SELF_PARAMETER`, `PY.KEYWORD_ARGUMENT`, `PY.DOC_COMMENT`, `PY.BUILTIN_NAME` |
| `--ansi-*` | `CONSOLE_*_OUTPUT` (`CONSOLE_DARKGRAY_OUTPUT` is bright black, `CONSOLE_GRAY_OUTPUT` white) |
-The full tables are `ICLS_COLORS` and `ICLS_ATTRIBUTES` in the same file.
+The full tables are `ICLS_COLORS` and `ICLS_ATTRIBUTES` in the same file. An
+attribute written as `` has no colours of its own and shows as plain
+text (`TEXT`'s foreground), as in IntelliJ, not with the parent scheme's colour.
### From VS Code (by hand)
diff --git a/scripts/theme/theme.test.ts b/scripts/theme/theme.test.ts
index 4e340c609..404d99096 100644
--- a/scripts/theme/theme.test.ts
+++ b/scripts/theme/theme.test.ts
@@ -91,6 +91,8 @@ describe("IntelliJ theme import", () => {
"Button": { "default": { "startBackground": "accent" } },
"Popup.background": "#181f28",
"Label.infoForeground": "#80888F80",
+ "EditorTabs": { "underlineColor": "#405060", "underlinedBorderColor": "#bd93f9" },
+ "CompletionPopup.matchForeground": "#ff79c6",
"Tree": { "rowHeight": 24 }
}
}`;
@@ -99,6 +101,7 @@ describe("IntelliJ theme import", () => {
+
+
`;
@@ -138,6 +144,9 @@ describe("IntelliJ theme import", () => {
expect(t.tokens["--bg-selected"]).toBe("#203040");
expect(t.tokens["--fg-muted"]).toBe("#80888f80");
expect(t.tokens["--fg"]).toBe("#c0c8d0");
+ // Islands borders the selected tab with underlinedBorderColor.
+ expect(t.tokens["--border-tab-selected"]).toBe("#bd93f9");
+ expect(t.tokens["--fg-completion-match"]).toBe("#ff79c6");
expect(s).toBeNull();
});
@@ -165,9 +174,12 @@ describe("IntelliJ theme import", () => {
"--syn-comment-style": "italic",
"--fg-editor": "#a9b7c6",
"--bg-editor": "#2b2b2b",
+ // GUTTER_BACKGROUND is the classic UI's: the gutter keeps the editor's background.
"--bg-editor-gutter": "#2b2b2b",
"--wave-error": "#ff0000",
"--editor-error-decoration": "underline wavy #ff0000",
+ // : plain text, not the parent scheme's colour.
+ "--syn-property": "#a9b7c6",
});
expect(s.tokens["--syn-constant"]).toBeUndefined();
});
diff --git a/src/features/plugins/intellijTheme.ts b/src/features/plugins/intellijTheme.ts
index 0d345f3fe..963b80efe 100644
--- a/src/features/plugins/intellijTheme.ts
+++ b/src/features/plugins/intellijTheme.ts
@@ -53,7 +53,8 @@ export const UI_KEYS: Record = {
"--bg-checkbox": ["Checkbox.Background.Default"],
"--border-checkbox": ["Checkbox.Border.Default"],
"--bg-tab-selected": ["EditorTabs.underlinedTabBackground", "EditorTabs.selectedBackground"],
- "--border-tab-selected": ["EditorTabs.underlineColor"],
+ // Islands paints the selected tab's border with `underlinedBorderColor`; the classic tabs underline with `underlineColor`.
+ "--border-tab-selected": ["EditorTabs.underlinedBorderColor", "EditorTabs.underlineColor"],
"--bg-tab-hover": ["EditorTabs.hoverBackground"],
"--border-editor-tabs": ["EditorTabs.borderColor"],
"--bg-toolbar-hover": ["ActionButton.hoverBackground"],
@@ -66,6 +67,7 @@ export const UI_KEYS: Record = {
"--bg-counter": ["Counter.background"],
"--fg-counter": ["Counter.foreground"],
"--bg-search-match": ["SearchMatch.startBackground", "SearchMatch.endBackground"],
+ "--fg-completion-match": ["CompletionPopup.matchForeground"],
"--bg-toggle-on": ["ToggleButton.onBackground"],
"--fg-toggle-on": ["ToggleButton.onForeground"],
"--bg-toggle-off": ["ToggleButton.offBackground"],
@@ -89,7 +91,8 @@ export const ICLS_COLORS: Record = {
SELECTION_BACKGROUND: ["--bg-editor-selection"],
LINE_NUMBERS_COLOR: ["--fg-line-number"],
LINE_NUMBER_ON_CARET_ROW_COLOR: ["--fg-line-number-current"],
- GUTTER_BACKGROUND: ["--bg-editor-gutter"],
+ // The new UI paints the gutter with this, else with the editor's background; `GUTTER_BACKGROUND` is the classic UI's.
+ EDITOR_GUTTER_BACKGROUND: ["--bg-editor-gutter"],
INDENT_GUIDE: ["--border-indent-guide"],
SELECTED_INDENT_GUIDE: ["--border-indent-guide-active"],
RIGHT_MARGIN_COLOR: ["--border-right-margin"],
@@ -244,19 +247,33 @@ export function convertIcls(xml: string): { name: string; dark: boolean | null;
if (key && value !== undefined && ICLS_COLORS[key]) set(ICLS_COLORS[key], iclsColor(value));
}
- const attributes = /([\s\S]*?)<\/attributes>/.exec(xml)?.[1] ?? "";
- for (const m of attributes.matchAll(/