implements PLN-0025

This commit is contained in:
bQUARKz 2026-03-31 17:19:08 +01:00
parent bcc89dcfbd
commit b64be2a9a1
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
13 changed files with 325 additions and 10 deletions

View File

@ -11,4 +11,4 @@
{"type":"discussion","id":"DSC-0010","status":"done","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-31","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0010-studio-code-editor-workspace-foundations/LSN-0026-read-only-editor-foundations-and-semantic-deferral.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0012","status":"done","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"discussion/lessons/DSC-0012-studio-editor-document-vfs-boundary/LSN-0027-project-document-vfs-and-session-owned-editor-boundary.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
{"type":"discussion","id":"DSC-0013","status":"open","ticket":"studio-editor-write-wave-supported-non-frontend-files","title":"Definir a wave inicial de edicao no Code Editor apenas para arquivos aceitos e nao relacionados ao FE","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[{"id":"AGD-0013","file":"AGD-0013-studio-editor-write-wave-supported-non-frontend-files.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"},{"id":"AGD-0014","file":"AGD-0014-studio-editor-frontend-edit-rights.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0010","file":"DEC-0010-studio-controlled-non-frontend-editor-write-wave.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0013"},{"id":"DEC-0011","file":"DEC-0011-studio-frontend-read-only-minimum-lsp-phase.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0014"}],"plans":[{"id":"PLN-0019","file":"PLN-0019-propagate-dec-0010-into-studio-and-vfs-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0020","file":"PLN-0020-build-dec-0010-vfs-access-policy-and-save-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0021","file":"PLN-0021-integrate-dec-0010-editor-write-ui-and-workflow.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0022","file":"PLN-0022-propagate-dec-0011-into-studio-vfs-and-lsp-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0023","file":"PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0024","file":"PLN-0024-implement-read-only-fe-diagnostics-symbols-and-definition.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0025","file":"PLN-0025-implement-fe-semantic-highlight-consumption.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0013","status":"open","ticket":"studio-editor-write-wave-supported-non-frontend-files","title":"Definir a wave inicial de edicao no Code Editor apenas para arquivos aceitos e nao relacionados ao FE","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","write","read-only","vfs","frontend-boundary"],"agendas":[{"id":"AGD-0013","file":"AGD-0013-studio-editor-write-wave-supported-non-frontend-files.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"},{"id":"AGD-0014","file":"AGD-0014-studio-editor-frontend-edit-rights.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0010","file":"DEC-0010-studio-controlled-non-frontend-editor-write-wave.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0013"},{"id":"DEC-0011","file":"DEC-0011-studio-frontend-read-only-minimum-lsp-phase.md","status":"accepted","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0014"}],"plans":[{"id":"PLN-0019","file":"PLN-0019-propagate-dec-0010-into-studio-and-vfs-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0020","file":"PLN-0020-build-dec-0010-vfs-access-policy-and-save-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0021","file":"PLN-0021-integrate-dec-0010-editor-write-ui-and-workflow.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0010"]},{"id":"PLN-0022","file":"PLN-0022-propagate-dec-0011-into-studio-vfs-and-lsp-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0023","file":"PLN-0023-scaffold-flat-packed-prometeu-lsp-api-and-session-seams.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0024","file":"PLN-0024-implement-read-only-fe-diagnostics-symbols-and-definition.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]},{"id":"PLN-0025","file":"PLN-0025-implement-fe-semantic-highlight-consumption.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0011"]}],"lessons":[]}

View File

@ -2,9 +2,9 @@
id: PLN-0025
ticket: studio-editor-write-wave-supported-non-frontend-files
title: Implement FE semantic highlight and editor consumption for the read-only LSP phase
status: review
status: done
created: 2026-03-31
completed:
completed: 2026-03-31
tags: [studio, lsp, highlight, frontend, editor]
---

View File

@ -0,0 +1,13 @@
package p.lsp.dtos;
import java.util.Objects;
public record PrometeuLspHighlightSpanDTO(
PrometeuLspRangeDTO range,
String semanticKey) {
public PrometeuLspHighlightSpanDTO {
range = Objects.requireNonNull(range, "range");
semanticKey = Objects.requireNonNull(semanticKey, "semanticKey");
}
}

View File

@ -1,6 +1,7 @@
package p.lsp.messages;
import p.lsp.dtos.PrometeuLspDiagnosticDTO;
import p.lsp.dtos.PrometeuLspHighlightSpanDTO;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.dtos.PrometeuLspSymbolDTO;
@ -10,12 +11,14 @@ import java.util.Objects;
public record PrometeuLspAnalyzeDocumentResult(
PrometeuLspSessionStateDTO sessionState,
List<PrometeuLspDiagnosticDTO> diagnostics,
List<PrometeuLspHighlightSpanDTO> semanticHighlights,
List<PrometeuLspSymbolDTO> documentSymbols,
List<PrometeuLspSymbolDTO> workspaceSymbols) {
public PrometeuLspAnalyzeDocumentResult {
Objects.requireNonNull(sessionState, "sessionState");
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
workspaceSymbols = List.copyOf(Objects.requireNonNull(workspaceSymbols, "workspaceSymbols"));
}

View File

@ -50,6 +50,7 @@ final class PrometeuLspSemanticReadPhase {
return new PrometeuLspAnalyzeDocumentResult(
new PrometeuLspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.workspaceSymbols());
}
@ -142,6 +143,7 @@ final class PrometeuLspSemanticReadPhase {
normalize(requestedDocumentPath),
diagnosticsByDocument,
Map.of(),
Map.of(),
List.of(),
Map.of(),
Map.of());
@ -160,6 +162,7 @@ final class PrometeuLspSemanticReadPhase {
return new SemanticSession(
normalize(requestedDocumentPath),
diagnosticsByDocument,
semanticIndex.semanticHighlightsByDocument(),
semanticIndex.documentSymbolsByDocument(),
semanticIndex.workspaceSymbols(),
semanticIndex.symbolsByName(),
@ -281,6 +284,7 @@ final class PrometeuLspSemanticReadPhase {
private record SemanticSession(
Path requestedDocumentPath,
Map<Path, List<PrometeuLspDiagnosticDTO>> diagnosticsByDocument,
Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument,
Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument,
List<PrometeuLspSymbolDTO> workspaceSymbols,
Map<String, List<PrometeuLspSymbolDTO>> symbolsByName,
@ -293,6 +297,7 @@ final class PrometeuLspSemanticReadPhase {
}
private static final class SemanticIndex {
private final Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument = new LinkedHashMap<>();
private final Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument = new LinkedHashMap<>();
private final List<PrometeuLspSymbolDTO> workspaceSymbols = new ArrayList<>();
private final Map<String, List<PrometeuLspSymbolDTO>> symbolsByName = new LinkedHashMap<>();
@ -317,9 +322,70 @@ final class PrometeuLspSemanticReadPhase {
symbolsByName.computeIfAbsent(child.name(), ignored -> new ArrayList<>()).add(child);
}
}
semanticHighlightsByDocument.put(
normalizedDocumentPath,
buildSemanticHighlights(tokens, symbolsByName));
documentSymbolsByDocument.put(normalizedDocumentPath, List.copyOf(documentSymbols));
}
private List<PrometeuLspHighlightSpanDTO> buildSemanticHighlights(
final List<PbsToken> tokens,
final Map<String, List<PrometeuLspSymbolDTO>> indexedSymbolsByName) {
final List<PrometeuLspHighlightSpanDTO> highlights = new ArrayList<>();
for (final PbsToken token : tokens) {
if (token.kind() == PbsTokenKind.EOF) {
continue;
}
final String semanticKey = semanticKey(token, indexedSymbolsByName);
if (semanticKey == null || semanticKey.isBlank()) {
continue;
}
highlights.add(new PrometeuLspHighlightSpanDTO(
new PrometeuLspRangeDTO(token.start(), token.end()),
semanticKey));
}
return List.copyOf(highlights);
}
private String semanticKey(
final PbsToken token,
final Map<String, List<PrometeuLspSymbolDTO>> indexedSymbolsByName) {
return switch (token.kind()) {
case COMMENT -> "fe-comment";
case STRING_LITERAL -> "fe-string";
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> "fe-number";
case TRUE, FALSE, NONE -> "fe-literal";
case IDENTIFIER -> semanticKeyForIdentifier(token.lexeme(), indexedSymbolsByName);
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 -> "fe-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 -> "fe-operator";
case LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, LEFT_BRACKET, RIGHT_BRACKET,
COMMA, COLON, SEMICOLON, AT, DOT, DOT_DOT -> "fe-punctuation";
default -> null;
};
}
private String semanticKeyForIdentifier(
final String lexeme,
final Map<String, List<PrometeuLspSymbolDTO>> indexedSymbolsByName) {
final List<PrometeuLspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
if (symbols.isEmpty()) {
return "fe-identifier";
}
final PrometeuLspSymbolKindDTO kind = symbols.get(0).kind();
return switch (kind) {
case FUNCTION, METHOD, CALLBACK -> "fe-callable";
case STRUCT, CONTRACT, HOST, BUILTIN_TYPE, SERVICE, ERROR, ENUM -> "fe-type";
case GLOBAL, CONST -> "fe-binding";
default -> "fe-identifier";
};
}
private PrometeuLspSymbolDTO symbolForTopDecl(
final Path documentPath,
final PbsAst.TopDecl topDecl) {
@ -407,6 +473,10 @@ final class PrometeuLspSemanticReadPhase {
children);
}
Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument() {
return Map.copyOf(semanticHighlightsByDocument);
}
Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument() {
return Map.copyOf(documentSymbolsByDocument);
}

View File

@ -0,0 +1,6 @@
package p.studio.workspaces.editor;
enum EditorDocumentHighlightOwner {
LOCAL,
LSP
}

View File

@ -0,0 +1,16 @@
package p.studio.workspaces.editor;
import org.fxmisc.richtext.model.StyleSpans;
import java.util.Collection;
import java.util.Objects;
record EditorDocumentHighlightingResult(
EditorDocumentHighlightOwner owner,
StyleSpans<Collection<String>> styleSpans) {
EditorDocumentHighlightingResult {
owner = Objects.requireNonNull(owner, "owner");
styleSpans = Objects.requireNonNull(styleSpans, "styleSpans");
}
}

View File

@ -0,0 +1,25 @@
package p.studio.workspaces.editor;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSemanticHighlighting;
final class EditorDocumentHighlightingRouter {
private EditorDocumentHighlightingRouter() {
}
static EditorDocumentHighlightingResult route(
final EditorOpenFileBuffer fileBuffer,
final EditorDocumentPresentation presentation,
final PrometeuLspAnalyzeDocumentResult analysis) {
if (fileBuffer.frontendDocument()
&& analysis != null
&& !analysis.semanticHighlights().isEmpty()) {
return new EditorDocumentHighlightingResult(
EditorDocumentHighlightOwner.LSP,
EditorDocumentSemanticHighlighting.highlight(fileBuffer.content(), analysis.semanticHighlights()));
}
return new EditorDocumentHighlightingResult(
EditorDocumentHighlightOwner.LOCAL,
presentation.highlight(fileBuffer.content()));
}
}

View File

@ -21,7 +21,7 @@ final class EditorDocumentPresentationRegistry {
EditorDocumentSyntaxHighlighting.bash());
private static final EditorDocumentPresentation FRONTEND_PRESENTATION = new EditorDocumentPresentation(
"fe",
java.util.List.of(),
java.util.List.of(stylesheet("presentations/fe.css")),
EditorDocumentSyntaxHighlighting.plainText());
EditorDocumentPresentation resolve(final String typeId) {

View File

@ -128,11 +128,18 @@ public final class EditorWorkspace extends Workspace {
final var fileBuffer = activeFile.orElseThrow();
final EditorDocumentPresentation presentation = presentationRegistry.resolve(fileBuffer.typeId());
final PrometeuLspAnalyzeDocumentResult analysis = fileBuffer.frontendDocument()
? prometeuLspService.analyzeDocument(new PrometeuLspAnalyzeDocumentRequest(fileBuffer.path()))
: null;
final EditorDocumentHighlightingResult highlighting = EditorDocumentHighlightingRouter.route(
fileBuffer,
presentation,
analysis);
applyPresentationStylesheets(presentation);
syncingEditor = true;
try {
codeArea.replaceText(fileBuffer.content());
codeArea.setStyleSpans(0, presentation.highlight(fileBuffer.content()));
codeArea.setStyleSpans(0, highlighting.styleSpans());
} finally {
syncingEditor = false;
}
@ -140,7 +147,7 @@ public final class EditorWorkspace extends Workspace {
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
refreshCommandSurfaces(fileBuffer);
statusBar.showFile(projectReference, fileBuffer, presentation);
refreshSemanticOutline(fileBuffer);
refreshSemanticOutline(fileBuffer, analysis);
}
private void revealActiveFileInNavigator() {
@ -317,13 +324,13 @@ public final class EditorWorkspace extends Workspace {
textDocument.dirty());
}
private void refreshSemanticOutline(final EditorOpenFileBuffer fileBuffer) {
if (!fileBuffer.frontendDocument()) {
private void refreshSemanticOutline(
final EditorOpenFileBuffer fileBuffer,
final PrometeuLspAnalyzeDocumentResult analysis) {
if (!fileBuffer.frontendDocument() || analysis == null) {
outlinePanel.showPlaceholder();
return;
}
final PrometeuLspAnalyzeDocumentResult analysis = prometeuLspService.analyzeDocument(
new PrometeuLspAnalyzeDocumentRequest(fileBuffer.path()));
outlinePanel.showSemanticReadResult(
fileBuffer.path(),
analysis.diagnostics(),

View File

@ -0,0 +1,43 @@
package p.studio.workspaces.editor.syntaxhighlight;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
import p.lsp.dtos.PrometeuLspHighlightSpanDTO;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public final class EditorDocumentSemanticHighlighting {
private EditorDocumentSemanticHighlighting() {
}
public static StyleSpans<Collection<String>> highlight(
final String content,
final List<PrometeuLspHighlightSpanDTO> semanticHighlights) {
final StyleSpansBuilder<Collection<String>> builder = new StyleSpansBuilder<>();
final List<PrometeuLspHighlightSpanDTO> orderedHighlights = semanticHighlights.stream()
.sorted(Comparator.comparingInt(highlight -> highlight.range().startOffset()))
.toList();
int cursor = 0;
for (final PrometeuLspHighlightSpanDTO highlight : orderedHighlights) {
final int start = Math.max(cursor, highlight.range().startOffset());
final int end = Math.min(content.length(), highlight.range().endOffset());
if (start > cursor) {
builder.add(Collections.emptyList(), start - cursor);
}
if (end > start) {
builder.add(List.of("editor-syntax-" + highlight.semanticKey()), end - start);
cursor = end;
}
}
if (cursor < content.length()) {
builder.add(Collections.emptyList(), content.length() - cursor);
}
if (content.isEmpty()) {
builder.add(Collections.emptyList(), 0);
}
return builder.create();
}
}

View File

@ -0,0 +1,61 @@
.editor-workspace-code-area-type-fe {
-fx-highlight-fill: #1b3244;
}
.editor-workspace-code-area-type-fe .text {
-fx-fill: #edf4fb;
}
.editor-workspace-code-area-type-fe .lineno {
-fx-text-fill: #71859a;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-keyword {
-fx-fill: #8dc7ff;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-callable {
-fx-fill: #f0cb79;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-type {
-fx-fill: #9ddba8;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-binding {
-fx-fill: #ffb1c8;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-string {
-fx-fill: #e2c48c;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-number {
-fx-fill: #c4e58a;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-comment {
-fx-fill: #6f8192;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-literal {
-fx-fill: #c8a2ff;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-operator {
-fx-fill: #dbe6f1;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-punctuation {
-fx-fill: #adc1d4;
}
.editor-workspace-code-area-type-fe .text.editor-syntax-fe-identifier {
-fx-fill: #edf4fb;
}
.editor-workspace-status-chip-type-fe {
-fx-background-color: #152432;
-fx-border-color: #4d8db9;
-fx-text-fill: #e8f5ff;
}

View File

@ -0,0 +1,71 @@
package p.studio.workspaces.editor;
import org.junit.jupiter.api.Test;
import p.lsp.dtos.PrometeuLspHighlightSpanDTO;
import p.lsp.dtos.PrometeuLspRangeDTO;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.studio.vfs.VfsDocumentAccessMode;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
final class EditorDocumentHighlightingRouterTest {
@Test
void frontendDocumentsUseLspOwnedHighlightsWhenSemanticSpansExist() {
final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
final EditorOpenFileBuffer fileBuffer = new EditorOpenFileBuffer(
Path.of("/tmp/example/src/main.pbs"),
"main.pbs",
"pbs",
"fn main() -> void {}",
"LF",
true,
VfsDocumentAccessMode.READ_ONLY,
false);
final PrometeuLspAnalyzeDocumentResult analysis = new PrometeuLspAnalyzeDocumentResult(
new PrometeuLspSessionStateDTO(true, List.of("highlight")),
List.of(),
List.of(new PrometeuLspHighlightSpanDTO(new PrometeuLspRangeDTO(0, 2), "fe-keyword")),
List.of(),
List.of());
final EditorDocumentHighlightingResult result = EditorDocumentHighlightingRouter.route(
fileBuffer,
registry.resolve("pbs"),
analysis);
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
}
@Test
void nonFrontendDocumentsStayOnLocalHighlighting() {
final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
final EditorOpenFileBuffer fileBuffer = new EditorOpenFileBuffer(
Path.of("/tmp/example/prometeu.json"),
"prometeu.json",
"json",
"{\n \"name\": \"Example\"\n}\n",
"LF",
false,
VfsDocumentAccessMode.EDITABLE,
false);
final PrometeuLspAnalyzeDocumentResult analysis = new PrometeuLspAnalyzeDocumentResult(
new PrometeuLspSessionStateDTO(true, List.of("highlight")),
List.of(),
List.of(new PrometeuLspHighlightSpanDTO(new PrometeuLspRangeDTO(0, 1), "fe-punctuation")),
List.of(),
List.of());
final EditorDocumentHighlightingResult result = EditorDocumentHighlightingRouter.route(
fileBuffer,
registry.resolve("json"),
analysis);
assertEquals(EditorDocumentHighlightOwner.LOCAL, result.owner());
}
}