feat(editor): colour global variables (the semantic global modifier) (#1393)

lua-language-server marks globals with the `global` token modifier, but
the editor dropped it, so a global variable looked like a local unless
rainbow highlighting was on. The modifier now sets `cm-sem-global`, drawn
in a new Language Defaults › Identifiers › Global variable colour
(`--syn-global`): library globals keep the predefined colour and calls the
function-call colour. Imported IntelliJ themes and .icls schemes take it
from DEFAULT_GLOBAL_VARIABLE. Built-in schemes default it to the local
variable colour (IntelliJ's fallback for the key), so they look as before
until a scheme sets it.

Fixes #1370
This commit is contained in:
l0ng-ai
2026-11-05 07:59:45 +08:00
committed by GitHub
parent 8d784ac164
commit d5c4066748
8 changed files with 33 additions and 0 deletions
+12
View File
@@ -44,6 +44,18 @@ describe("semanticRanges", () => {
expect(semanticRanges(doc, input)).toEqual([{ from: 0, to: 3, className: "cm-sem-function" }]);
});
it("marks globals (lua-language-server's `global` modifier)", () => {
const input = {
tokenTypes: ["variable", "function"],
tokenModifiers: ["global"],
data: [0, 0, 3, 0, 1, 0, 4, 3, 1, 1],
};
expect(semanticRanges(doc, input)).toEqual([
{ from: 0, to: 3, className: "cm-sem-global" },
{ from: 4, to: 7, className: "cm-sem-function cm-sem-global" },
]);
});
it("colours a constructor (jdtls: class + `constructor`) as a function", () => {
const input = { tokenTypes: ["class"], tokenModifiers: ["constructor", "declaration"], data: [0, 0, 3, 0, 0b11] };
expect(semanticRanges(doc, input)[0]?.className).toBe("cm-sem-function cm-sem-declaration");
+1
View File
@@ -56,6 +56,7 @@ const MODIFIER_CLASS: Record<string, string> = {
declaration: "declaration",
definition: "declaration",
defaultLibrary: "builtin",
global: "global",
};
/** Number of `--syn-rainbow-<n>` colours (IDEA's five semantic highlighting colours). */
+2
View File
@@ -361,6 +361,8 @@ export const lspTheme = EditorView.theme({
".cm-sem-parameter": { color: "var(--syn-param, var(--syn-variable))" },
".cm-sem-self": { color: "var(--syn-self, var(--syn-keyword))" },
".cm-sem-keyword": { color: "var(--syn-keyword)", fontWeight: "var(--syn-keyword-bold, inherit)" },
// Before the builtin rule: library globals (`print`, `string`) keep the predefined colour.
".cm-sem-global:not(.cm-sem-function)": { color: "var(--syn-global, inherit)" },
".cm-sem-builtin:not(.cm-sem-function)": { color: "var(--syn-builtin, inherit)" },
".cm-sem-static": { fontStyle: "var(--syn-static-italic, italic)" },
".cm-sem-python-static": { fontStyle: "var(--syn-python-static-italic, normal)" },
+1
View File
@@ -130,6 +130,7 @@ export const ICLS_ATTRIBUTES: Record<string, Array<[Part, string]>> = {
DEFAULT_IDENTIFIER: [["FOREGROUND", "--syn-variable"]],
DEFAULT_LOCAL_VARIABLE: [["FOREGROUND", "--syn-variable"]],
DEFAULT_PARAMETER: [["FOREGROUND", "--syn-param"]],
DEFAULT_GLOBAL_VARIABLE: [["FOREGROUND", "--syn-global"]],
DEFAULT_PREDEFINED_SYMBOL: [["FOREGROUND", "--syn-builtin"]],
DEFAULT_METADATA: [
["FOREGROUND", "--syn-annotation"],
+1
View File
@@ -113,6 +113,7 @@ export const ATTRIBUTES: readonly AttributeDef[] = [
syn("keyword", "Keyword", [LD], "keyword"),
syn("identifier", "Identifiers › Local variable", [LD], "variable"),
syn("parameter", "Identifiers › Parameter", [LD], "param"),
syn("globalVariable", "Identifiers › Global variable", [LD], "global"),
syn("constant", "Identifiers › Constant", [LD], "constant"),
syn("field", "Identifiers › Instance field", [LD], "property"),
{ id: "staticSymbol", name: "Identifiers › Static symbol", path: [LD], token: "static", italicOnly: true },
+11
View File
@@ -41,6 +41,17 @@ describe(".icls schemes", () => {
expect(customs).toEqual(before);
});
it("imports the global variable colour", () => {
const xml =
'<scheme name="G" parent_scheme="Darcula"><attributes><option name="DEFAULT_GLOBAL_VARIABLE"><value>' +
'<option name="FOREGROUND" value="9876aa"/><option name="FONT_TYPE" value="2"/></value></option></attributes></scheme>';
expect(parseIcls(xml).scheme.attributes.globalVariable).toEqual({
foreground: "#9876aa",
bold: false,
italic: true,
});
});
it.each([
["Darcula", "ffffff", "dark"],
["Default", "000000", "light"],
+1
View File
@@ -11,6 +11,7 @@ const ATTRIBUTE_NAMES: Record<string, string> = {
DEFAULT_IDENTIFIER: "identifier",
DEFAULT_LOCAL_VARIABLE: "identifier",
DEFAULT_PARAMETER: "parameter",
DEFAULT_GLOBAL_VARIABLE: "globalVariable",
DEFAULT_CONSTANT: "constant",
DEFAULT_INSTANCE_FIELD: "field",
DEFAULT_STATIC_FIELD: "constant",
+4
View File
@@ -116,6 +116,7 @@ html {
--syn-number: #2aacb8;
--syn-punct: #bcbec4;
--syn-variable: #bcbec4;
--syn-global: var(--syn-variable);
--syn-param: #bcbec4;
--syn-property: #c77dbb;
--syn-constant: #c77dbb;
@@ -494,6 +495,7 @@ html {
--syn-number: #1750eb;
--syn-punct: #080808;
--syn-variable: #080808;
--syn-global: var(--syn-variable);
--syn-param: #080808;
--syn-property: #871094;
--syn-constant: #871094;
@@ -748,6 +750,7 @@ html {
--syn-number: #4fd0dc;
--syn-punct: #e3e4e8;
--syn-variable: #e3e4e8;
--syn-global: var(--syn-variable);
--syn-param: #e3e4e8;
--syn-property: #e0a0d6;
--syn-constant: #e0a0d6;
@@ -1087,6 +1090,7 @@ html {
--syn-number: var(--palette-number);
--syn-punct: var(--palette-fg);
--syn-variable: var(--palette-variable);
--syn-global: var(--syn-variable);
--syn-param: var(--palette-fg);
--syn-property: var(--palette-property);
--syn-constant: var(--palette-number);