fix(editor): highlight builtin SQL functions in editor themes

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-09-05 09:22:16 +08:00
1 parent ab6a7645f6
commit d5a511b719
2 files changed
+39 -3

No files matched your search

+31 -1
View File
@@ -1,7 +1,9 @@
import { describe, expect, it } from "vitest";
import { buildEditorFontThemeRules, buildSqlCompletionThemeRules, editorDiagnosticColors, editorThemeAppearanceFor, resolveCustomThemeBackgrounds, resolveEditorTheme } from "@/lib/editor/editorThemes";
import { buildEditorFontThemeRules, buildSqlCompletionThemeRules, editorDiagnosticColors, editorThemeAppearanceFor, resolveCustomThemeBackgrounds, resolveEditorTheme, SQL_BUILTIN_HIGHLIGHT_TAG } from "@/lib/editor/editorThemes";
import { DEFAULT_APP_CUSTOM_UI_COLORS, wcagContrastRatio, type AppThemePalette } from "@/lib/app/appTheme";
import type { EditorTheme } from "@/stores/settingsStore";
import { createDbxCodeMirrorSqlDialect } from "@/lib/editor/codemirrorSqlDialect";
import * as langSql from "@codemirror/lang-sql";
describe("resolveEditorTheme", () => {
it("maps only the follow-app editor theme to application IDE palettes", () => {
@@ -191,6 +193,34 @@ describe("SQL completion theme", () => {
});
});
describe("SQL builtin highlight tag", () => {
// #7950: count/date_format/etc. were added to the dialect builtin word lists (#7222) but
// never actually rendered in a distinct color, because the theme's highlight rule matched
// standard(variableName) while @codemirror/lang-sql tags builtin words as standard(name) —
// variableName is a *child* tag of name, so a rule keyed on the child never matches the
// token's actual (parent) tag.
it("gives builtin SQL functions their own highlight class, distinct from plain identifiers and keywords", async () => {
const { highlightTree } = await import("@lezer/highlight");
const { HighlightStyle } = await import("@codemirror/language");
const { tags } = await import("@lezer/highlight");
const style = HighlightStyle.define([
{ tag: tags.keyword, color: "keyword" },
{ tag: [tags.name, tags.variableName], color: "variable" },
{ tag: SQL_BUILTIN_HIGHLIGHT_TAG, color: "builtin" },
]);
const dialect = createDbxCodeMirrorSqlDialect(langSql, "postgres", "postgres");
const doc = "select count(*) from t";
const tree = dialect.language.parser.parse(doc);
const classesByToken = new Map<string, string>();
highlightTree(tree, style, (from, to, cls) => classesByToken.set(doc.slice(from, to), cls));
expect(classesByToken.get("count")).toBeDefined();
expect(classesByToken.get("count")).not.toBe(classesByToken.get("t"));
expect(classesByToken.get("count")).not.toBe(classesByToken.get("select"));
});
});
describe("editor gutters", () => {
it("keeps single line numbers vertically centered in the base rule", () => {
const rules = buildEditorFontThemeRules();
+8 -2
View File
@@ -4,6 +4,12 @@ import { customUiAppearance, type AppCustomUiColors, type AppThemeAppearance, ty
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import { tags } from "@lezer/highlight";
// @codemirror/lang-sql tags dialect builtin-list words (COUNT, DATE_FORMAT, ...) as
// standard(name) — not standard(variableName), which is a *child* tag of name and so never
// matches a token tagged with the parent. Shared so both editor theme builders below stay in
// sync with what the SQL tokenizer actually emits.
export const SQL_BUILTIN_HIGHLIGHT_TAG = tags.standard(tags.name);
type CodeMirrorStyleSpec = Parameters<typeof import("@codemirror/view").EditorView.theme>[0];
type LucideIconNode = Array<[string, Record<string, string>]>;
@@ -165,7 +171,7 @@ function createCustomTheme(EditorView: typeof import("@codemirror/view").EditorV
{ tag: tags.definition(tags.variableName), color: c.variable },
{ tag: tags.function(tags.variableName), color: c.function },
{ tag: tags.function(tags.propertyName), color: c.function },
{ tag: tags.standard(tags.variableName), color: c.builtin },
{ tag: SQL_BUILTIN_HIGHLIGHT_TAG, color: c.builtin },
{ tag: tags.propertyName, color: c.property },
{ tag: tags.operator, color: c.operator },
{ tag: tags.compareOperator, color: c.operator },
@@ -563,7 +569,7 @@ function createIdeEditorTheme(EditorView: typeof import("@codemirror/view").Edit
{ tag: [tags.typeName, tags.typeOperator, tags.unit], color: c.type },
{ tag: [tags.name, tags.variableName, tags.definition(tags.variableName)], color: c.variable },
{ tag: [tags.function(tags.variableName), tags.function(tags.propertyName), tags.function(tags.name), tags.macroName], color: c.function },
{ tag: [tags.standard(tags.variableName), tags.special(tags.name)], color: c.builtin },
{ tag: [SQL_BUILTIN_HIGHLIGHT_TAG, tags.special(tags.name)], color: c.builtin },
{ tag: [tags.propertyName, tags.labelName, tags.annotation], color: c.property },
{ tag: [tags.operator, tags.compareOperator, tags.logicOperator, tags.arithmeticOperator, tags.derefOperator], color: c.operator },
{ tag: [tags.punctuation, tags.separator, tags.paren, tags.brace, tags.bracket, tags.angleBracket], color: c.punctuation },