implements PLN-0074
This commit is contained in:
parent
e5ab4cd021
commit
7ae2e94d63
@ -1,5 +1,5 @@
|
|||||||
{"type":"meta","next_id":{"DSC":35,"AGD":38,"DEC":35,"PLN":75,"LSN":47,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":35,"AGD":38,"DEC":35,"PLN":75,"LSN":47,"CLSN":1}}
|
||||||
{"type":"discussion","id":"DSC-0034","status":"in_progress","ticket":"frontend-semantic-host-projection-flexibility","title":"Frontend semantic vocabulary flexibility and declarative host projection","created_at":"2026-05-06","updated_at":"2026-05-06","tags":["compiler","compiler-general","frontend","semantics","vscode","host-projection","lsp"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0072","file":"PLN-0072-frontend-semantic-host-projection-contract.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0073","file":"PLN-0073-lsp-semantic-host-projection-transport.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0074","file":"PLN-0074-vscode-semantic-host-projection-adapter.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0034","status":"in_progress","ticket":"frontend-semantic-host-projection-flexibility","title":"Frontend semantic vocabulary flexibility and declarative host projection","created_at":"2026-05-06","updated_at":"2026-05-06","tags":["compiler","compiler-general","frontend","semantics","vscode","host-projection","lsp"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-frontend-semantic-host-projection-flexibility.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0072","file":"PLN-0072-frontend-semantic-host-projection-contract.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0073","file":"PLN-0073-lsp-semantic-host-projection-transport.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]},{"id":"PLN-0074","file":"PLN-0074-vscode-semantic-host-projection-adapter.md","status":"done","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0034"]}],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0033","status":"in_progress","ticket":"frontend-visual-theme-spec-and-css-retirement","title":"Frontend visual theme spec and retirement of host-consumed semantic CSS","created_at":"2026-05-06","updated_at":"2026-05-06","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[{"id":"AGD-0036","file":"AGD-0036-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0033","file":"DEC-0033-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0036"}],"plans":[{"id":"PLN-0068","file":"PLN-0068-frontend-visual-contract-and-pbs-migration.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0069","file":"PLN-0069-lsp-visual-theme-transport-and-selection.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0070","file":"PLN-0070-studio-visual-theme-adapter-and-css-retirement.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0071","file":"PLN-0071-vscode-dynamic-theme-translation-adapter.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0033","status":"in_progress","ticket":"frontend-visual-theme-spec-and-css-retirement","title":"Frontend visual theme spec and retirement of host-consumed semantic CSS","created_at":"2026-05-06","updated_at":"2026-05-06","tags":["compiler","compiler-general","frontend","presentation","theming","studio","vscode","lsp","pbs"],"agendas":[{"id":"AGD-0036","file":"AGD-0036-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06"}],"decisions":[{"id":"DEC-0033","file":"DEC-0033-frontend-visual-theme-spec-and-css-retirement.md","status":"accepted","created_at":"2026-05-06","updated_at":"2026-05-06","ref_agenda":"AGD-0036"}],"plans":[{"id":"PLN-0068","file":"PLN-0068-frontend-visual-contract-and-pbs-migration.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0069","file":"PLN-0069-lsp-visual-theme-transport-and-selection.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0070","file":"PLN-0070-studio-visual-theme-adapter-and-css-retirement.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]},{"id":"PLN-0071","file":"PLN-0071-vscode-dynamic-theme-translation-adapter.md","status":"open","created_at":"2026-05-06","updated_at":"2026-05-06","ref_decisions":["DEC-0033"]}],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0032","status":"in_progress","ticket":"studio-new-lsp-api-and-v1-boundary","title":"Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","lsp","vscode","protocol","api","boundary"],"agendas":[{"id":"AGD-0035","file":"AGD-0035-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0032","file":"DEC-0032-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0035"}],"plans":[{"id":"PLN-0065","file":"PLN-0065-lsp-boundary-and-module-scaffolding.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0066","file":"PLN-0066-project-scoped-lsp-server-lifecycle-in-studio.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0067","file":"PLN-0067-compiler-backed-dumb-lsp-server-baseline.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0032","status":"in_progress","ticket":"studio-new-lsp-api-and-v1-boundary","title":"Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","lsp","vscode","protocol","api","boundary"],"agendas":[{"id":"AGD-0035","file":"AGD-0035-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0032","file":"DEC-0032-studio-new-lsp-api-and-v1-boundary.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0035"}],"plans":[{"id":"PLN-0065","file":"PLN-0065-lsp-boundary-and-module-scaffolding.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0066","file":"PLN-0066-project-scoped-lsp-server-lifecycle-in-studio.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]},{"id":"PLN-0067","file":"PLN-0067-compiler-backed-dumb-lsp-server-baseline.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0032"]}],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"studio-editor-workspace-cleanup","title":"Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","editor","cleanup","vfs","lsp","migration"],"agendas":[{"id":"AGD-0034","file":"AGD-0034-vscode-editor-migration-feasibility.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0031","file":"DEC-0031-studio-editor-workspace-cleanup.md","status":"in_progress","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0034"}],"plans":[{"id":"PLN-0062","file":"PLN-0062-shell-session-and-state-cleanup-after-editor-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0063","file":"PLN-0063-legacy-lsp-and-vfs-module-removal-with-runtime-preservation.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0064","file":"PLN-0064-normative-test-and-lesson-cleanup-for-editor-stack-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"studio-editor-workspace-cleanup","title":"Limpeza completa do Workspace Editor do Studio e remoção dos acoplamentos legados","created_at":"2026-05-05","updated_at":"2026-05-05","tags":["studio","editor","cleanup","vfs","lsp","migration"],"agendas":[{"id":"AGD-0034","file":"AGD-0034-vscode-editor-migration-feasibility.md","status":"accepted","created_at":"2026-05-05","updated_at":"2026-05-05"}],"decisions":[{"id":"DEC-0031","file":"DEC-0031-studio-editor-workspace-cleanup.md","status":"in_progress","created_at":"2026-05-05","updated_at":"2026-05-05","ref_agenda":"AGD-0034"}],"plans":[{"id":"PLN-0062","file":"PLN-0062-shell-session-and-state-cleanup-after-editor-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0063","file":"PLN-0063-legacy-lsp-and-vfs-module-removal-with-runtime-preservation.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]},{"id":"PLN-0064","file":"PLN-0064-normative-test-and-lesson-cleanup-for-editor-stack-removal.md","status":"done","created_at":"2026-05-05","updated_at":"2026-05-05","ref_decisions":["DEC-0031"]}],"lessons":[]}
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
id: PLN-0074
|
id: PLN-0074
|
||||||
ticket: frontend-semantic-host-projection-flexibility
|
ticket: frontend-semantic-host-projection-flexibility
|
||||||
title: VS Code Semantic Host Projection Adapter
|
title: VS Code Semantic Host Projection Adapter
|
||||||
status: open
|
status: done
|
||||||
created: 2026-05-06
|
created: 2026-05-06
|
||||||
completed:
|
completed: 2026-05-06
|
||||||
tags: [vscode, extension, frontend, semantics, host-projection, adapter]
|
tags: [vscode, extension, frontend, semantics, host-projection, adapter]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,7 @@ let reconnectContext;
|
|||||||
let stoppingClient = false;
|
let stoppingClient = false;
|
||||||
let lastAppliedThemeSignature;
|
let lastAppliedThemeSignature;
|
||||||
const RECONNECT_DELAY_MS = 2000;
|
const RECONNECT_DELAY_MS = 2000;
|
||||||
|
const VSCODE_HOST_ID = "vscode";
|
||||||
function activate(context) {
|
function activate(context) {
|
||||||
reconnectContext = context;
|
reconnectContext = context;
|
||||||
output = vscode.window.createOutputChannel("Prometeu LSP");
|
output = vscode.window.createOutputChannel("Prometeu LSP");
|
||||||
@ -207,21 +208,30 @@ function clearReconnectTimer() {
|
|||||||
reconnectTimer = undefined;
|
reconnectTimer = undefined;
|
||||||
}
|
}
|
||||||
async function applyPrometeuVisualTheme(initializeResult) {
|
async function applyPrometeuVisualTheme(initializeResult) {
|
||||||
const payload = extractPrometeuVisualThemesPayload(initializeResult);
|
const visualThemesPayload = extractPrometeuVisualThemesPayload(initializeResult);
|
||||||
if (!payload) {
|
if (!visualThemesPayload) {
|
||||||
output.appendLine("Prometeu visual theme payload not present in initialize result.");
|
output.appendLine("Prometeu visual theme payload not present in initialize result.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const activeTheme = payload.visualThemes.find((theme) => theme.themeId === payload.activeVisualThemeId);
|
const semanticProjectionPayload = extractPrometeuSemanticHostProjectionsPayload(initializeResult);
|
||||||
|
const vscodeProjection = selectHostProjection(semanticProjectionPayload, VSCODE_HOST_ID);
|
||||||
|
if (!semanticProjectionPayload) {
|
||||||
|
output.appendLine("Prometeu semantic host projection payload not present in initialize result.");
|
||||||
|
}
|
||||||
|
else if (!vscodeProjection) {
|
||||||
|
output.appendLine("Prometeu semantic host projection payload does not declare a vscode projection.");
|
||||||
|
}
|
||||||
|
const activeTheme = visualThemesPayload.visualThemes.find((theme) => theme.themeId === visualThemesPayload.activeVisualThemeId);
|
||||||
if (!activeTheme) {
|
if (!activeTheme) {
|
||||||
output.appendLine(`Prometeu visual theme '${payload.activeVisualThemeId}' was not declared by the server.`);
|
output.appendLine(`Prometeu visual theme '${visualThemesPayload.activeVisualThemeId}' was not declared by the server.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const themeSignature = JSON.stringify({
|
const themeSignature = JSON.stringify({
|
||||||
languageId: payload.frontendLanguageId,
|
languageId: visualThemesPayload.frontendLanguageId,
|
||||||
themeId: activeTheme.themeId,
|
themeId: activeTheme.themeId,
|
||||||
editorPalette: activeTheme.editorPalette,
|
editorPalette: activeTheme.editorPalette,
|
||||||
tokenStyles: activeTheme.tokenStyles
|
tokenStyles: activeTheme.tokenStyles,
|
||||||
|
semanticProjection: vscodeProjection ?? null
|
||||||
});
|
});
|
||||||
if (themeSignature === lastAppliedThemeSignature) {
|
if (themeSignature === lastAppliedThemeSignature) {
|
||||||
output.appendLine(`Prometeu visual theme '${activeTheme.themeId}' is already applied.`);
|
output.appendLine(`Prometeu visual theme '${activeTheme.themeId}' is already applied.`);
|
||||||
@ -234,7 +244,7 @@ async function applyPrometeuVisualTheme(initializeResult) {
|
|||||||
const existingSemanticCustomizations = configuration.get("editor.semanticTokenColorCustomizations") ?? {};
|
const existingSemanticCustomizations = configuration.get("editor.semanticTokenColorCustomizations") ?? {};
|
||||||
const semanticRules = {
|
const semanticRules = {
|
||||||
...(readObject(existingSemanticCustomizations.rules)),
|
...(readObject(existingSemanticCustomizations.rules)),
|
||||||
...buildSemanticTokenRules(payload.frontendLanguageId, activeTheme)
|
...buildSemanticTokenRules(visualThemesPayload.frontendLanguageId, activeTheme, vscodeProjection)
|
||||||
};
|
};
|
||||||
await configuration.update("editor.semanticTokenColorCustomizations", {
|
await configuration.update("editor.semanticTokenColorCustomizations", {
|
||||||
...existingSemanticCustomizations,
|
...existingSemanticCustomizations,
|
||||||
@ -249,7 +259,7 @@ async function applyPrometeuVisualTheme(initializeResult) {
|
|||||||
"editorLineNumber.foreground": activeTheme.editorPalette.lineNumberForeground
|
"editorLineNumber.foreground": activeTheme.editorPalette.lineNumberForeground
|
||||||
}, configurationTarget);
|
}, configurationTarget);
|
||||||
lastAppliedThemeSignature = themeSignature;
|
lastAppliedThemeSignature = themeSignature;
|
||||||
output.appendLine(`Applied Prometeu visual theme '${activeTheme.themeId}' for language '${payload.frontendLanguageId}'.`);
|
output.appendLine(`Applied Prometeu visual theme '${activeTheme.themeId}' for language '${visualThemesPayload.frontendLanguageId}'.`);
|
||||||
}
|
}
|
||||||
function extractPrometeuVisualThemesPayload(initializeResult) {
|
function extractPrometeuVisualThemesPayload(initializeResult) {
|
||||||
if (!initializeResult || typeof initializeResult !== "object") {
|
if (!initializeResult || typeof initializeResult !== "object") {
|
||||||
@ -266,27 +276,63 @@ function extractPrometeuVisualThemesPayload(initializeResult) {
|
|||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
function buildSemanticTokenRules(languageId, theme) {
|
function extractPrometeuSemanticHostProjectionsPayload(initializeResult) {
|
||||||
|
if (!initializeResult || typeof initializeResult !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const capabilities = readObject(initializeResult.capabilities);
|
||||||
|
const experimental = readObject(capabilities.experimental);
|
||||||
|
const payload = readObject(experimental.prometeuSemanticHostProjections);
|
||||||
|
if (typeof payload.frontendLanguageId !== "string" || !Array.isArray(payload.semanticKeys) || !Array.isArray(payload.hostProjections)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
function selectHostProjection(payload, hostId) {
|
||||||
|
if (!payload) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return payload.hostProjections.find((projection) => projection.hostId === hostId);
|
||||||
|
}
|
||||||
|
function buildSemanticTokenRules(languageId, theme, hostProjection) {
|
||||||
const rules = {};
|
const rules = {};
|
||||||
|
const projectionBySemanticKey = new Map((hostProjection?.tokenProjections ?? []).map((projection) => [projection.semanticKey, projection]));
|
||||||
for (const tokenStyle of theme.tokenStyles) {
|
for (const tokenStyle of theme.tokenStyles) {
|
||||||
const selector = `${tokenStyle.semanticKey}:${languageId}`;
|
const styleValue = !tokenStyle.italic && !tokenStyle.bold && !tokenStyle.underline
|
||||||
if (!tokenStyle.italic && !tokenStyle.bold && !tokenStyle.underline) {
|
? tokenStyle.foreground
|
||||||
rules[selector] = tokenStyle.foreground;
|
: {
|
||||||
|
foreground: tokenStyle.foreground,
|
||||||
|
...(tokenStyle.italic ? { italic: true } : {}),
|
||||||
|
...(tokenStyle.bold ? { bold: true } : {}),
|
||||||
|
...(tokenStyle.underline ? { underline: true } : {})
|
||||||
|
};
|
||||||
|
rules[`${tokenStyle.semanticKey}:${languageId}`] = styleValue;
|
||||||
|
const projection = projectionBySemanticKey.get(tokenStyle.semanticKey);
|
||||||
|
if (!projection) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rules[selector] = {
|
const projectedSelector = buildSemanticSelector(languageId, projection.hostTokenType, projection.hostTokenModifiers);
|
||||||
foreground: tokenStyle.foreground,
|
if (!(projectedSelector in rules)) {
|
||||||
...(tokenStyle.italic ? { italic: true } : {}),
|
rules[projectedSelector] = styleValue;
|
||||||
...(tokenStyle.bold ? { bold: true } : {}),
|
}
|
||||||
...(tokenStyle.underline ? { underline: true } : {})
|
const fallbackSelector = buildSemanticSelector(languageId, projection.fallbackTokenType, projection.fallbackTokenModifiers);
|
||||||
};
|
if (!(fallbackSelector in rules)) {
|
||||||
|
rules[fallbackSelector] = styleValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
function buildSemanticSelector(languageId, tokenType, modifiers) {
|
||||||
|
const normalizedModifiers = [...modifiers].sort();
|
||||||
|
if (normalizedModifiers.length === 0) {
|
||||||
|
return `${tokenType}:${languageId}`;
|
||||||
|
}
|
||||||
|
return `${tokenType}.${normalizedModifiers.join(".")}:${languageId}`;
|
||||||
|
}
|
||||||
function readObject(value) {
|
function readObject(value) {
|
||||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=extension.js.map
|
//# sourceMappingURL=extension.js.map
|
||||||
|
|||||||
@ -53,100 +53,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semanticTokenTypes": [
|
|
||||||
{
|
|
||||||
"id": "pbs-comment",
|
|
||||||
"description": "PBS comment token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-string",
|
|
||||||
"description": "PBS string token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-number",
|
|
||||||
"description": "PBS number token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-literal",
|
|
||||||
"description": "PBS literal token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-lifecycle",
|
|
||||||
"description": "PBS lifecycle token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-keyword",
|
|
||||||
"description": "PBS keyword token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-operator",
|
|
||||||
"description": "PBS operator token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-punctuation",
|
|
||||||
"description": "PBS punctuation token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-function",
|
|
||||||
"description": "PBS function token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-method",
|
|
||||||
"description": "PBS method token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-constructor",
|
|
||||||
"description": "PBS constructor token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-struct",
|
|
||||||
"description": "PBS struct token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-contract",
|
|
||||||
"description": "PBS contract token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-host",
|
|
||||||
"description": "PBS host token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-builtin-type",
|
|
||||||
"description": "PBS builtin type token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-service",
|
|
||||||
"description": "PBS service token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-error",
|
|
||||||
"description": "PBS error token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-enum",
|
|
||||||
"description": "PBS enum token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-callback",
|
|
||||||
"description": "PBS callback token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-global",
|
|
||||||
"description": "PBS global token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-const",
|
|
||||||
"description": "PBS const token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-implements",
|
|
||||||
"description": "PBS implements token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "pbs-identifier",
|
|
||||||
"description": "PBS identifier token"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configurationDefaults": {
|
"configurationDefaults": {
|
||||||
"editor.semanticHighlighting.enabled": true
|
"editor.semanticHighlighting.enabled": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,27 @@ type PrometeuVisualThemesPayload = {
|
|||||||
visualThemes: PrometeuVisualTheme[];
|
visualThemes: PrometeuVisualTheme[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PrometeuSemanticHostProjectionEntry = {
|
||||||
|
semanticKey: string;
|
||||||
|
hostTokenType: string;
|
||||||
|
hostTokenModifiers: string[];
|
||||||
|
fallbackTokenType: string;
|
||||||
|
fallbackTokenModifiers: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type PrometeuSemanticHostProjection = {
|
||||||
|
hostId: string;
|
||||||
|
tokenProjections: PrometeuSemanticHostProjectionEntry[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type PrometeuSemanticHostProjectionsPayload = {
|
||||||
|
frontendLanguageId: string;
|
||||||
|
semanticKeys: string[];
|
||||||
|
hostProjections: PrometeuSemanticHostProjection[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const VSCODE_HOST_ID = "vscode";
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext): void {
|
export function activate(context: vscode.ExtensionContext): void {
|
||||||
reconnectContext = context;
|
reconnectContext = context;
|
||||||
output = vscode.window.createOutputChannel("Prometeu LSP");
|
output = vscode.window.createOutputChannel("Prometeu LSP");
|
||||||
@ -255,23 +276,31 @@ function clearReconnectTimer(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function applyPrometeuVisualTheme(initializeResult: unknown): Promise<void> {
|
async function applyPrometeuVisualTheme(initializeResult: unknown): Promise<void> {
|
||||||
const payload = extractPrometeuVisualThemesPayload(initializeResult);
|
const visualThemesPayload = extractPrometeuVisualThemesPayload(initializeResult);
|
||||||
if (!payload) {
|
if (!visualThemesPayload) {
|
||||||
output.appendLine("Prometeu visual theme payload not present in initialize result.");
|
output.appendLine("Prometeu visual theme payload not present in initialize result.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const semanticProjectionPayload = extractPrometeuSemanticHostProjectionsPayload(initializeResult);
|
||||||
|
const vscodeProjection = selectHostProjection(semanticProjectionPayload, VSCODE_HOST_ID);
|
||||||
|
if (!semanticProjectionPayload) {
|
||||||
|
output.appendLine("Prometeu semantic host projection payload not present in initialize result.");
|
||||||
|
} else if (!vscodeProjection) {
|
||||||
|
output.appendLine("Prometeu semantic host projection payload does not declare a vscode projection.");
|
||||||
|
}
|
||||||
|
|
||||||
const activeTheme = payload.visualThemes.find((theme) => theme.themeId === payload.activeVisualThemeId);
|
const activeTheme = visualThemesPayload.visualThemes.find((theme) => theme.themeId === visualThemesPayload.activeVisualThemeId);
|
||||||
if (!activeTheme) {
|
if (!activeTheme) {
|
||||||
output.appendLine(`Prometeu visual theme '${payload.activeVisualThemeId}' was not declared by the server.`);
|
output.appendLine(`Prometeu visual theme '${visualThemesPayload.activeVisualThemeId}' was not declared by the server.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeSignature = JSON.stringify({
|
const themeSignature = JSON.stringify({
|
||||||
languageId: payload.frontendLanguageId,
|
languageId: visualThemesPayload.frontendLanguageId,
|
||||||
themeId: activeTheme.themeId,
|
themeId: activeTheme.themeId,
|
||||||
editorPalette: activeTheme.editorPalette,
|
editorPalette: activeTheme.editorPalette,
|
||||||
tokenStyles: activeTheme.tokenStyles
|
tokenStyles: activeTheme.tokenStyles,
|
||||||
|
semanticProjection: vscodeProjection ?? null
|
||||||
});
|
});
|
||||||
if (themeSignature === lastAppliedThemeSignature) {
|
if (themeSignature === lastAppliedThemeSignature) {
|
||||||
output.appendLine(`Prometeu visual theme '${activeTheme.themeId}' is already applied.`);
|
output.appendLine(`Prometeu visual theme '${activeTheme.themeId}' is already applied.`);
|
||||||
@ -286,7 +315,7 @@ async function applyPrometeuVisualTheme(initializeResult: unknown): Promise<void
|
|||||||
const existingSemanticCustomizations = configuration.get<Record<string, unknown>>("editor.semanticTokenColorCustomizations") ?? {};
|
const existingSemanticCustomizations = configuration.get<Record<string, unknown>>("editor.semanticTokenColorCustomizations") ?? {};
|
||||||
const semanticRules = {
|
const semanticRules = {
|
||||||
...(readObject(existingSemanticCustomizations.rules)),
|
...(readObject(existingSemanticCustomizations.rules)),
|
||||||
...buildSemanticTokenRules(payload.frontendLanguageId, activeTheme)
|
...buildSemanticTokenRules(visualThemesPayload.frontendLanguageId, activeTheme, vscodeProjection)
|
||||||
};
|
};
|
||||||
|
|
||||||
await configuration.update(
|
await configuration.update(
|
||||||
@ -312,7 +341,7 @@ async function applyPrometeuVisualTheme(initializeResult: unknown): Promise<void
|
|||||||
);
|
);
|
||||||
|
|
||||||
lastAppliedThemeSignature = themeSignature;
|
lastAppliedThemeSignature = themeSignature;
|
||||||
output.appendLine(`Applied Prometeu visual theme '${activeTheme.themeId}' for language '${payload.frontendLanguageId}'.`);
|
output.appendLine(`Applied Prometeu visual theme '${activeTheme.themeId}' for language '${visualThemesPayload.frontendLanguageId}'.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractPrometeuVisualThemesPayload(initializeResult: unknown): PrometeuVisualThemesPayload | undefined {
|
function extractPrometeuVisualThemesPayload(initializeResult: unknown): PrometeuVisualThemesPayload | undefined {
|
||||||
@ -334,27 +363,82 @@ function extractPrometeuVisualThemesPayload(initializeResult: unknown): Prometeu
|
|||||||
return payload as unknown as PrometeuVisualThemesPayload;
|
return payload as unknown as PrometeuVisualThemesPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractPrometeuSemanticHostProjectionsPayload(initializeResult: unknown): PrometeuSemanticHostProjectionsPayload | undefined {
|
||||||
|
if (!initializeResult || typeof initializeResult !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const capabilities = readObject((initializeResult as { capabilities?: unknown }).capabilities);
|
||||||
|
const experimental = readObject(capabilities.experimental);
|
||||||
|
const payload = readObject(experimental.prometeuSemanticHostProjections);
|
||||||
|
|
||||||
|
if (typeof payload.frontendLanguageId !== "string" || !Array.isArray(payload.semanticKeys) || !Array.isArray(payload.hostProjections)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload as unknown as PrometeuSemanticHostProjectionsPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectHostProjection(
|
||||||
|
payload: PrometeuSemanticHostProjectionsPayload | undefined,
|
||||||
|
hostId: string
|
||||||
|
): PrometeuSemanticHostProjection | undefined {
|
||||||
|
if (!payload) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return payload.hostProjections.find((projection) => projection.hostId === hostId);
|
||||||
|
}
|
||||||
|
|
||||||
function buildSemanticTokenRules(
|
function buildSemanticTokenRules(
|
||||||
languageId: string,
|
languageId: string,
|
||||||
theme: PrometeuVisualTheme
|
theme: PrometeuVisualTheme,
|
||||||
|
hostProjection: PrometeuSemanticHostProjection | undefined
|
||||||
): Record<string, string | { foreground: string; italic?: boolean; bold?: boolean; underline?: boolean }> {
|
): Record<string, string | { foreground: string; italic?: boolean; bold?: boolean; underline?: boolean }> {
|
||||||
const rules: Record<string, string | { foreground: string; italic?: boolean; bold?: boolean; underline?: boolean }> = {};
|
const rules: Record<string, string | { foreground: string; italic?: boolean; bold?: boolean; underline?: boolean }> = {};
|
||||||
|
const projectionBySemanticKey = new Map<string, PrometeuSemanticHostProjectionEntry>(
|
||||||
|
(hostProjection?.tokenProjections ?? []).map((projection) => [projection.semanticKey, projection])
|
||||||
|
);
|
||||||
for (const tokenStyle of theme.tokenStyles) {
|
for (const tokenStyle of theme.tokenStyles) {
|
||||||
const selector = `${tokenStyle.semanticKey}:${languageId}`;
|
const styleValue = !tokenStyle.italic && !tokenStyle.bold && !tokenStyle.underline
|
||||||
if (!tokenStyle.italic && !tokenStyle.bold && !tokenStyle.underline) {
|
? tokenStyle.foreground
|
||||||
rules[selector] = tokenStyle.foreground;
|
: {
|
||||||
|
foreground: tokenStyle.foreground,
|
||||||
|
...(tokenStyle.italic ? { italic: true } : {}),
|
||||||
|
...(tokenStyle.bold ? { bold: true } : {}),
|
||||||
|
...(tokenStyle.underline ? { underline: true } : {})
|
||||||
|
};
|
||||||
|
rules[`${tokenStyle.semanticKey}:${languageId}`] = styleValue;
|
||||||
|
|
||||||
|
const projection = projectionBySemanticKey.get(tokenStyle.semanticKey);
|
||||||
|
if (!projection) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rules[selector] = {
|
|
||||||
foreground: tokenStyle.foreground,
|
const projectedSelector = buildSemanticSelector(languageId, projection.hostTokenType, projection.hostTokenModifiers);
|
||||||
...(tokenStyle.italic ? { italic: true } : {}),
|
if (!(projectedSelector in rules)) {
|
||||||
...(tokenStyle.bold ? { bold: true } : {}),
|
rules[projectedSelector] = styleValue;
|
||||||
...(tokenStyle.underline ? { underline: true } : {})
|
}
|
||||||
};
|
|
||||||
|
const fallbackSelector = buildSemanticSelector(languageId, projection.fallbackTokenType, projection.fallbackTokenModifiers);
|
||||||
|
if (!(fallbackSelector in rules)) {
|
||||||
|
rules[fallbackSelector] = styleValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildSemanticSelector(
|
||||||
|
languageId: string,
|
||||||
|
tokenType: string,
|
||||||
|
modifiers: string[]
|
||||||
|
): string {
|
||||||
|
const normalizedModifiers = [...modifiers].sort();
|
||||||
|
if (normalizedModifiers.length === 0) {
|
||||||
|
return `${tokenType}:${languageId}`;
|
||||||
|
}
|
||||||
|
return `${tokenType}.${normalizedModifiers.join(".")}:${languageId}`;
|
||||||
|
}
|
||||||
|
|
||||||
function readObject(value: unknown): Record<string, unknown> {
|
function readObject(value: unknown): Record<string, unknown> {
|
||||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user