Frontend Visual Theme Spec and Retirement of Host-Consumed Semantic CSS

This commit is contained in:
bQUARKz 2026-05-06 15:46:46 +01:00
parent fb13e166f1
commit 10fd7f6111
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 241 additions and 86 deletions

View File

@ -12,34 +12,34 @@ public class PBSFrontendThemes {
"PBS Default",
new FrontendEditorPaletteSpec(
"#d9e2ec",
"#2d4f6b",
"#2d4f6b",
"#6f8193",
"#2c2140",
"#a47dff",
"#efe6ff"),
"#2f3f5b",
"#23314a",
"#6b7f99",
"#241f38",
"#b392f0",
"#f5efff"),
List.of(
new FrontendTokenStyleSpec("pbs-comment", "#c090b0", true, false, false),
new FrontendTokenStyleSpec("pbs-string", "#00c088", false, false, false),
new FrontendTokenStyleSpec("pbs-number", "#ff90b0", false, false, false),
new FrontendTokenStyleSpec("pbs-literal", "#4fc1ff", false, false, false),
new FrontendTokenStyleSpec("pbs-lifecycle", "#ef50c0", false, false, false),
new FrontendTokenStyleSpec("pbs-keyword", "#569cd6", false, false, false),
new FrontendTokenStyleSpec("pbs-operator", "#d4d4d4", false, false, false),
new FrontendTokenStyleSpec("pbs-punctuation", "#d4d4d4", false, false, false),
new FrontendTokenStyleSpec("pbs-function", "#f2c14e", false, false, false),
new FrontendTokenStyleSpec("pbs-method", "#f2c14e", false, false, false),
new FrontendTokenStyleSpec("pbs-constructor", "#ecdcaa", false, false, false),
new FrontendTokenStyleSpec("pbs-struct", "#4ec9b0", false, false, false),
new FrontendTokenStyleSpec("pbs-contract", "#78dce8", false, false, false),
new FrontendTokenStyleSpec("pbs-host", "#b7a2fa", false, false, false),
new FrontendTokenStyleSpec("pbs-builtin-type", "#8be9fd", false, false, false),
new FrontendTokenStyleSpec("pbs-service", "#b7a2fa", false, false, false),
new FrontendTokenStyleSpec("pbs-error", "#ff5b5b", false, false, false),
new FrontendTokenStyleSpec("pbs-enum", "#56cfe1", false, false, false),
new FrontendTokenStyleSpec("pbs-callback", "#b7a2fa", false, false, false),
new FrontendTokenStyleSpec("pbs-global", "#f790fc", true, false, false),
new FrontendTokenStyleSpec("pbs-const", "#f78c6c", false, false, false),
new FrontendTokenStyleSpec("pbs-implements", "#a1c181", false, false, false),
new FrontendTokenStyleSpec("pbs-comment", "#8b949e", true, false, false),
new FrontendTokenStyleSpec("pbs-string", "#7ee787", false, false, false),
new FrontendTokenStyleSpec("pbs-number", "#ffa657", false, false, false),
new FrontendTokenStyleSpec("pbs-literal", "#79c0ff", false, false, false),
new FrontendTokenStyleSpec("pbs-lifecycle", "#ff7b72", false, true, false),
new FrontendTokenStyleSpec("pbs-keyword", "#d2a8ff", false, false, false),
new FrontendTokenStyleSpec("pbs-operator", "#c9d1d9", false, false, false),
new FrontendTokenStyleSpec("pbs-punctuation", "#8b949e", false, false, false),
new FrontendTokenStyleSpec("pbs-function", "#ffa657", false, true, false),
new FrontendTokenStyleSpec("pbs-method", "#ffd580", false, false, false),
new FrontendTokenStyleSpec("pbs-constructor", "#e3b341", false, true, false),
new FrontendTokenStyleSpec("pbs-struct", "#56d4dd", false, false, false),
new FrontendTokenStyleSpec("pbs-contract", "#79c0ff", false, false, true),
new FrontendTokenStyleSpec("pbs-host", "#c297ff", false, false, false),
new FrontendTokenStyleSpec("pbs-builtin-type", "#7ee787", true, false, false),
new FrontendTokenStyleSpec("pbs-service", "#c297ff", false, true, false),
new FrontendTokenStyleSpec("pbs-error", "#ff7b72", false, false, true),
new FrontendTokenStyleSpec("pbs-enum", "#56d4dd", false, true, false),
new FrontendTokenStyleSpec("pbs-callback", "#ffb3e6", false, false, false),
new FrontendTokenStyleSpec("pbs-global", "#f778ba", true, false, false),
new FrontendTokenStyleSpec("pbs-const", "#ffab70", false, true, false),
new FrontendTokenStyleSpec("pbs-implements", "#a5d6ff", false, false, false),
new FrontendTokenStyleSpec("pbs-identifier", "#d4d4d4", false, false, false)));
}

View File

@ -51,12 +51,13 @@ public enum PbsSemanticKind {
case COMMENT -> COMMENT;
case STRING_LITERAL -> STRING;
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> NUMBER;
case TRUE, FALSE, NONE -> LITERAL;
case TRUE, FALSE, NONE, SOME, OK, ERR -> LITERAL;
case VOID, OPTIONAL, RESULT -> BUILTIN_TYPE;
case IMPORT, FROM, AS, SERVICE, HOST, FN, APPLY, BIND, NEW, IMPLEMENTS, USING, CTOR,
DECLARE, LET, CONST, GLOBAL, STRUCT, CONTRACT, ERROR, ENUM, CALLBACK, BUILTIN,
SELF, THIS, PUB, MUT, MOD, TYPE, IF, ELSE, SWITCH, DEFAULT, FOR, UNTIL, STEP,
WHILE, BREAK, CONTINUE, RETURN, VOID, OPTIONAL, RESULT, SOME, OK, ERR, HANDLE,
AND, OR, NOT, SPAWN, YIELD, SLEEP, MATCH -> KEYWORD;
WHILE, BREAK, CONTINUE, RETURN, HANDLE, AND, OR, NOT, SPAWN, YIELD, SLEEP,
MATCH -> KEYWORD;
case PLUS, MINUS, STAR, SLASH, PERCENT, BANG, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL,
SLASH_EQUAL, PERCENT_EQUAL, BANG_EQUAL, EQUAL, EQUAL_EQUAL, LESS, LESS_EQUAL,
GREATER, GREATER_EQUAL, AND_AND, OR_OR, ARROW, QUESTION -> OPERATOR;

View File

@ -20,7 +20,8 @@ final class PbsSemanticPresentationContractTest {
assertEquals(1, presentation.themes().size());
assertEquals("PBS Default", presentation.defaultTheme().displayName());
assertEquals("#d9e2ec", presentation.defaultTheme().editorPalette().baseForeground());
assertEquals("#569cd6", presentation.defaultTheme().requireTokenStyle("pbs-keyword").foreground());
assertEquals("#d2a8ff", presentation.defaultTheme().requireTokenStyle("pbs-keyword").foreground());
assertTrue(presentation.defaultTheme().requireTokenStyle("pbs-builtin-type").italic());
assertTrue(presentation.defaultTheme().requireTokenStyle("pbs-comment").italic());
assertEquals(PBSFrontendHostProjections.VSCODE_HOST_ID, presentation.requireHostProjection("vscode").hostId());
}

View File

@ -8,12 +8,6 @@ public record FrontendSemanticPresentationSpec(
String defaultThemeId,
List<FrontendHostProjectionSpec> hostProjections) {
public FrontendSemanticPresentationSpec(
final List<FrontendVisualThemeSpec> themes,
final String defaultThemeId) {
this(themes, defaultThemeId, List.of());
}
public FrontendSemanticPresentationSpec {
themes = List.copyOf(Objects.requireNonNull(themes, "themes"));
defaultThemeId = Objects.requireNonNull(defaultThemeId, "defaultThemeId").trim();

View File

@ -14,6 +14,7 @@ import p.studio.compiler.models.FrontendVisualThemeSpec;
import p.studio.compiler.pbs.PbsSemanticKind;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.utilities.SourceProviderFactory;
@ -29,8 +30,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public record CompilerLanguageServiceBridge() implements LanguageServiceBridge {
private static final Set<String> BUILTIN_TYPE_NAMES = Set.of("int", "float", "bool", "string", "i32", "i64", "f32", "f64");
@Override
public BaselineServerDescription describeServer(final LspProjectContext context) {
@ -182,7 +186,97 @@ public record CompilerLanguageServiceBridge() implements LanguageServiceBridge {
if (isLifecycleToken(tokens, index, token)) {
return PbsSemanticKind.LIFECYCLE;
}
return PbsSemanticKind.IDENTIFIER;
return classifyIdentifierToken(tokens, index, token);
}
return null;
}
private PbsSemanticKind classifyIdentifierToken(
final ReadOnlyList<PbsToken> tokens,
final int index,
final PbsToken token) {
final PbsTokenKind previousKind = previousSignificantKind(tokens, index);
final PbsTokenKind nextKind = nextSignificantKind(tokens, index);
if (previousKind == PbsTokenKind.STRUCT) {
return PbsSemanticKind.STRUCT;
}
if (previousKind == PbsTokenKind.CONTRACT) {
return PbsSemanticKind.CONTRACT;
}
if (previousKind == PbsTokenKind.HOST) {
return PbsSemanticKind.HOST;
}
if (previousKind == PbsTokenKind.SERVICE) {
return PbsSemanticKind.SERVICE;
}
if (previousKind == PbsTokenKind.ENUM || previousKind == PbsTokenKind.ERROR) {
return PbsSemanticKind.ENUM;
}
if (previousKind == PbsTokenKind.CALLBACK) {
return PbsSemanticKind.CALLBACK;
}
if (previousKind == PbsTokenKind.GLOBAL) {
return PbsSemanticKind.GLOBAL;
}
if (previousKind == PbsTokenKind.CONST) {
return PbsSemanticKind.CONST;
}
if (previousKind == PbsTokenKind.CTOR || previousKind == PbsTokenKind.NEW) {
return PbsSemanticKind.CONSTRUCTOR;
}
if (previousKind == PbsTokenKind.BUILTIN) {
return PbsSemanticKind.BUILTIN_TYPE;
}
if (previousKind == PbsTokenKind.DOT && nextKind == PbsTokenKind.LEFT_PAREN) {
return PbsSemanticKind.METHOD;
}
if (nextKind == PbsTokenKind.LEFT_PAREN) {
return PbsSemanticKind.FUNCTION;
}
if (isTypeReferenceContext(previousKind, token.lexeme())) {
return PbsSemanticKind.BUILTIN_TYPE;
}
return PbsSemanticKind.IDENTIFIER;
}
private boolean isTypeReferenceContext(
final PbsTokenKind previousKind,
final String lexeme) {
if (previousKind == null) {
return false;
}
if (previousKind != PbsTokenKind.COLON
&& previousKind != PbsTokenKind.ARROW
&& previousKind != PbsTokenKind.AS
&& previousKind != PbsTokenKind.IMPLEMENTS) {
return false;
}
return BUILTIN_TYPE_NAMES.contains(lexeme);
}
private PbsTokenKind previousSignificantKind(
final ReadOnlyList<PbsToken> tokens,
final int index) {
for (int cursor = index - 1; cursor >= 0; cursor -= 1) {
final PbsTokenKind kind = tokens.get(cursor).kind();
if (kind == PbsTokenKind.COMMENT || kind == PbsTokenKind.EOF) {
continue;
}
return kind;
}
return null;
}
private PbsTokenKind nextSignificantKind(
final ReadOnlyList<PbsToken> tokens,
final int index) {
for (int cursor = index + 1; cursor < tokens.size(); cursor += 1) {
final PbsTokenKind kind = tokens.get(cursor).kind();
if (kind == PbsTokenKind.COMMENT || kind == PbsTokenKind.EOF) {
continue;
}
return kind;
}
return null;
}

View File

@ -46,14 +46,28 @@ class CompilerLanguageServiceBridgeTest {
new LspProjectContext("main", "pbs", Path.of(".")),
Path.of("demo.pbs").toUri().toString(),
"""
struct Vec2 {
fn length() -> int {
return 0;
}
}
fn main() -> void {
const answer = 42;
const answer: int = 42;
let value = new Vec2();
value.length();
}
""");
assertTrue(semanticTokens.legend().contains("pbs-keyword"));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-keyword")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-number")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-struct")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-function")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-method")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-const")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-builtin-type")));
assertTrue(semanticTokens.tokens().stream().anyMatch(token -> token.semanticKey().equals("pbs-constructor")));
}
@Test
@ -74,7 +88,7 @@ class CompilerLanguageServiceBridgeTest {
assertEquals("#d9e2ec", description.visualThemes().getFirst().editorPalette().baseForeground());
assertTrue(description.visualThemes().getFirst().tokenStyles().stream()
.anyMatch(tokenStyle -> tokenStyle.semanticKey().equals("pbs-keyword")
&& tokenStyle.foreground().equals("#569cd6")));
&& tokenStyle.foreground().equals("#d2a8ff")));
}
private Path findRepoRoot(final Path start) {

View File

@ -3,66 +3,117 @@
"enabled": true,
"rules": {
"pbs-comment:pbs": {
"foreground": "#c090b0",
"foreground": "#8b949e",
"italic": true
},
"pbs-string:pbs": "#00c088",
"pbs-number:pbs": "#ff90b0",
"pbs-literal:pbs": "#4fc1ff",
"pbs-lifecycle:pbs": "#ef50c0",
"pbs-keyword:pbs": "#569cd6",
"pbs-operator:pbs": "#d4d4d4",
"pbs-punctuation:pbs": "#d4d4d4",
"pbs-function:pbs": "#f2c14e",
"pbs-method:pbs": "#f2c14e",
"pbs-constructor:pbs": "#ecdcaa",
"pbs-struct:pbs": "#4ec9b0",
"pbs-contract:pbs": "#78dce8",
"pbs-host:pbs": "#b7a2fa",
"pbs-builtin-type:pbs": "#8be9fd",
"pbs-service:pbs": "#b7a2fa",
"pbs-error:pbs": "#ff5b5b",
"pbs-enum:pbs": "#56cfe1",
"pbs-callback:pbs": "#b7a2fa",
"pbs-string:pbs": "#7ee787",
"pbs-number:pbs": "#ffa657",
"pbs-literal:pbs": "#79c0ff",
"pbs-lifecycle:pbs": {
"foreground": "#ff7b72",
"bold": true
},
"pbs-keyword:pbs": "#d2a8ff",
"pbs-operator:pbs": "#c9d1d9",
"pbs-punctuation:pbs": "#8b949e",
"pbs-function:pbs": {
"foreground": "#ffa657",
"bold": true
},
"pbs-method:pbs": "#ffd580",
"pbs-constructor:pbs": {
"foreground": "#e3b341",
"bold": true
},
"pbs-struct:pbs": "#56d4dd",
"pbs-contract:pbs": {
"foreground": "#79c0ff",
"underline": true
},
"pbs-host:pbs": "#c297ff",
"pbs-builtin-type:pbs": {
"foreground": "#7ee787",
"italic": true
},
"pbs-service:pbs": {
"foreground": "#c297ff",
"bold": true
},
"pbs-error:pbs": {
"foreground": "#ff7b72",
"underline": true
},
"pbs-enum:pbs": {
"foreground": "#56d4dd",
"bold": true
},
"pbs-callback:pbs": "#ffb3e6",
"pbs-global:pbs": {
"foreground": "#f790fc",
"foreground": "#f778ba",
"italic": true
},
"pbs-const:pbs": "#f78c6c",
"pbs-implements:pbs": "#a1c181",
"pbs-const:pbs": {
"foreground": "#ffab70",
"bold": true
},
"pbs-implements:pbs": "#a5d6ff",
"pbs-identifier:pbs": "#d4d4d4",
"comment:pbs": {
"foreground": "#c090b0",
"foreground": "#8b949e",
"italic": true
},
"string:pbs": "#00c088",
"number:pbs": "#ff90b0",
"enumMember:pbs": "#4fc1ff",
"variable:pbs": "#4fc1ff",
"function.async:pbs": "#ef50c0",
"variable.async:pbs": "#ef50c0",
"keyword:pbs": "#569cd6",
"operator:pbs": "#d4d4d4",
"function:pbs": "#f2c14e",
"method:pbs": "#f2c14e",
"class.declaration:pbs": "#ecdcaa",
"type.declaration:pbs": "#ecdcaa",
"struct:pbs": "#4ec9b0",
"type:pbs": "#4ec9b0",
"interface:pbs": "#78dce8",
"class.defaultLibrary:pbs": "#b7a2fa",
"type.defaultLibrary:pbs": "#b7a2fa",
"variable.defaultLibrary:pbs": "#8be9fd",
"enum:pbs": "#ff5b5b",
"string:pbs": "#7ee787",
"number:pbs": "#ffa657",
"enumMember:pbs": "#79c0ff",
"variable:pbs": "#79c0ff",
"function.async:pbs": {
"foreground": "#ff7b72",
"bold": true
},
"variable.async:pbs": {
"foreground": "#ff7b72",
"bold": true
},
"keyword:pbs": "#d2a8ff",
"operator:pbs": "#c9d1d9",
"function:pbs": {
"foreground": "#ffa657",
"bold": true
},
"method:pbs": "#ffd580",
"class.declaration:pbs": {
"foreground": "#e3b341",
"bold": true
},
"type.declaration:pbs": {
"foreground": "#e3b341",
"bold": true
},
"struct:pbs": "#56d4dd",
"type:pbs": "#56d4dd",
"interface:pbs": {
"foreground": "#79c0ff",
"underline": true
},
"class.defaultLibrary:pbs": "#c297ff",
"type.defaultLibrary:pbs": "#c297ff",
"variable.defaultLibrary:pbs": {
"foreground": "#7ee787",
"italic": true
},
"enum:pbs": {
"foreground": "#ff7b72",
"underline": true
},
"variable.readonly:pbs": {
"foreground": "#f790fc",
"foreground": "#f778ba",
"italic": true
}
}
},
"workbench.colorCustomizations": {
"editor.foreground": "#d9e2ec",
"editor.selectionBackground": "#2d4f6b",
"editorLineNumber.foreground": "#6f8193"
"editor.selectionBackground": "#2f3f5b",
"editorLineNumber.foreground": "#6b7f99"
}
}