implements PLN-0028 studio frontend-owned semantic presentation consumption
This commit is contained in:
parent
de9782c16e
commit
d6ffd9eb62
@ -10,47 +10,47 @@
|
||||
-fx-text-fill: #71859a;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-keyword {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-keyword {
|
||||
-fx-fill: #8dc7ff;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-function {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-function {
|
||||
-fx-fill: #f0cb79;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-type {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-type {
|
||||
-fx-fill: #9ddba8;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-binding {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-binding {
|
||||
-fx-fill: #ffb1c8;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-string {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-string {
|
||||
-fx-fill: #e2c48c;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-number {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-number {
|
||||
-fx-fill: #c4e58a;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-comment {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-comment {
|
||||
-fx-fill: #6f8192;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-literal {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-literal {
|
||||
-fx-fill: #c8a2ff;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-operator {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-operator {
|
||||
-fx-fill: #dbe6f1;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-punctuation {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-punctuation {
|
||||
-fx-fill: #adc1d4;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-identifier {
|
||||
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-identifier {
|
||||
-fx-fill: #edf4fb;
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ final class EditorDocumentHighlightingRouter {
|
||||
final LspAnalyzeDocumentResult analysis) {
|
||||
if (fileBuffer.frontendDocument()
|
||||
&& analysis != null
|
||||
&& presentation.supportsSemanticHighlighting()
|
||||
&& !analysis.semanticHighlights().isEmpty()) {
|
||||
return new EditorDocumentHighlightingResult(
|
||||
EditorDocumentHighlightOwner.LSP,
|
||||
|
||||
@ -10,15 +10,21 @@ import java.util.Objects;
|
||||
record EditorDocumentPresentation(
|
||||
String styleKey,
|
||||
List<String> stylesheetUrls,
|
||||
List<String> semanticKeys,
|
||||
EditorDocumentSyntaxHighlighting syntaxHighlighting) {
|
||||
|
||||
EditorDocumentPresentation {
|
||||
styleKey = Objects.requireNonNull(styleKey, "styleKey");
|
||||
stylesheetUrls = List.copyOf(Objects.requireNonNull(stylesheetUrls, "stylesheetUrls"));
|
||||
semanticKeys = List.copyOf(Objects.requireNonNull(semanticKeys, "semanticKeys"));
|
||||
syntaxHighlighting = Objects.requireNonNull(syntaxHighlighting, "syntaxHighlighting");
|
||||
}
|
||||
|
||||
StyleSpans<Collection<String>> highlight(final String content) {
|
||||
return syntaxHighlighting.highlight(content);
|
||||
}
|
||||
|
||||
boolean supportsSemanticHighlighting() {
|
||||
return !semanticKeys.isEmpty() && !stylesheetUrls.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,36 +1,47 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import p.studio.compiler.FrontendRegistryService;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
import p.studio.vfs.messages.VfsDocumentTypeIds;
|
||||
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
final class EditorDocumentPresentationRegistry {
|
||||
private static final Logger LOGGER = Logger.getLogger(EditorDocumentPresentationRegistry.class.getName());
|
||||
private static final EditorDocumentPresentation TEXT_PRESENTATION = new EditorDocumentPresentation(
|
||||
"text",
|
||||
java.util.List.of(),
|
||||
java.util.List.of(),
|
||||
EditorDocumentSyntaxHighlighting.plainText());
|
||||
private static final EditorDocumentPresentation JSON_PRESENTATION = new EditorDocumentPresentation(
|
||||
"json",
|
||||
java.util.List.of(stylesheet("presentations/json.css")),
|
||||
java.util.List.of(),
|
||||
EditorDocumentSyntaxHighlighting.json());
|
||||
private static final EditorDocumentPresentation BASH_PRESENTATION = new EditorDocumentPresentation(
|
||||
"bash",
|
||||
java.util.List.of(stylesheet("presentations/bash.css")),
|
||||
java.util.List.of(),
|
||||
EditorDocumentSyntaxHighlighting.bash());
|
||||
private static final EditorDocumentPresentation FRONTEND_PRESENTATION = new EditorDocumentPresentation(
|
||||
"fe",
|
||||
java.util.List.of(stylesheet("presentations/fe.css")),
|
||||
EditorDocumentSyntaxHighlighting.plainText());
|
||||
|
||||
EditorDocumentPresentation resolve(final String typeId) {
|
||||
return resolve(typeId, null);
|
||||
}
|
||||
|
||||
EditorDocumentPresentation resolve(
|
||||
final String typeId,
|
||||
final LspSemanticPresentationDTO semanticPresentation) {
|
||||
final String normalizedTypeId = normalize(typeId);
|
||||
if (normalizedTypeId.isBlank()) {
|
||||
return TEXT_PRESENTATION;
|
||||
}
|
||||
if (FrontendRegistryService.getFrontendSpec(normalizedTypeId).isPresent()) {
|
||||
return FRONTEND_PRESENTATION;
|
||||
return frontendPresentation(normalizedTypeId, semanticPresentation);
|
||||
}
|
||||
if (VfsDocumentTypeIds.JSON.equals(normalizedTypeId)) {
|
||||
return JSON_PRESENTATION;
|
||||
@ -41,6 +52,26 @@ final class EditorDocumentPresentationRegistry {
|
||||
return TEXT_PRESENTATION;
|
||||
}
|
||||
|
||||
private EditorDocumentPresentation frontendPresentation(
|
||||
final String normalizedTypeId,
|
||||
final LspSemanticPresentationDTO semanticPresentation) {
|
||||
if (semanticPresentation == null) {
|
||||
return new EditorDocumentPresentation(
|
||||
normalizedTypeId,
|
||||
java.util.List.of(),
|
||||
java.util.List.of(),
|
||||
EditorDocumentSyntaxHighlighting.plainText());
|
||||
}
|
||||
return new EditorDocumentPresentation(
|
||||
normalizedTypeId,
|
||||
semanticPresentation.resources().stream()
|
||||
.map(EditorDocumentPresentationRegistry::resourceStylesheet)
|
||||
.flatMap(Optional::stream)
|
||||
.toList(),
|
||||
semanticPresentation.semanticKeys(),
|
||||
EditorDocumentSyntaxHighlighting.plainText());
|
||||
}
|
||||
|
||||
private String normalize(final String typeId) {
|
||||
return typeId == null ? "" : typeId.trim().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
@ -51,4 +82,22 @@ final class EditorDocumentPresentationRegistry {
|
||||
"missing editor presentation stylesheet: " + relativePath)
|
||||
.toExternalForm();
|
||||
}
|
||||
|
||||
private static Optional<String> resourceStylesheet(final String resourcePath) {
|
||||
final String normalizedPath = normalizeResourcePath(resourcePath);
|
||||
final URL resource = EditorDocumentPresentationRegistry.class.getResource(normalizedPath);
|
||||
if (resource == null) {
|
||||
LOGGER.fine("missing frontend semantic presentation resource: " + resourcePath);
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(resource.toExternalForm());
|
||||
}
|
||||
|
||||
private static String normalizeResourcePath(final String resourcePath) {
|
||||
final String normalized = Objects.requireNonNull(resourcePath, "resourcePath").trim();
|
||||
if (normalized.isEmpty()) {
|
||||
throw new IllegalArgumentException("resourcePath cannot be blank");
|
||||
}
|
||||
return normalized.startsWith("/") ? normalized : "/" + normalized;
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,10 +124,12 @@ public final class EditorWorkspace extends Workspace {
|
||||
}
|
||||
|
||||
final var fileBuffer = activeFile.orElseThrow();
|
||||
final EditorDocumentPresentation presentation = presentationRegistry.resolve(fileBuffer.typeId());
|
||||
final LspAnalyzeDocumentResult analysis = fileBuffer.frontendDocument()
|
||||
? prometeuLspService.analyzeDocument(new LspAnalyzeDocumentRequest(fileBuffer.path()))
|
||||
: null;
|
||||
final EditorDocumentPresentation presentation = presentationRegistry.resolve(
|
||||
fileBuffer.typeId(),
|
||||
analysis == null ? null : analysis.semanticPresentation());
|
||||
final EditorDocumentHighlightingResult highlighting = EditorDocumentHighlightingRouter.route(
|
||||
fileBuffer,
|
||||
presentation,
|
||||
|
||||
@ -28,7 +28,7 @@ public final class EditorDocumentSemanticHighlighting {
|
||||
builder.add(Collections.emptyList(), start - cursor);
|
||||
}
|
||||
if (end > start) {
|
||||
builder.add(List.of("editor-syntax-" + highlight.semanticKey()), end - start);
|
||||
builder.add(List.of("editor-semantic-" + highlight.semanticKey()), end - start);
|
||||
cursor = end;
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,10 +708,6 @@
|
||||
-fx-fill: #eef3f8;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-fe .text {
|
||||
-fx-fill: #f2f6fb;
|
||||
}
|
||||
|
||||
.editor-workspace-command-bar {
|
||||
-fx-padding: 10 12 0 12;
|
||||
-fx-alignment: center-left;
|
||||
@ -880,12 +876,6 @@
|
||||
-fx-text-fill: #c5d2de;
|
||||
}
|
||||
|
||||
.editor-workspace-status-chip-type-fe {
|
||||
-fx-background-color: #271a35;
|
||||
-fx-border-color: #8d67c7;
|
||||
-fx-text-fill: #f3ecff;
|
||||
}
|
||||
|
||||
.editor-workspace-status-chip-position {
|
||||
-fx-min-width: 44;
|
||||
-fx-pref-width: 44;
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
.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;
|
||||
}
|
||||
@ -12,6 +12,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
final class EditorDocumentHighlightingRouterTest {
|
||||
@Test
|
||||
@ -29,18 +30,21 @@ final class EditorDocumentHighlightingRouterTest {
|
||||
|
||||
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||
new LspSessionStateDTO(true, List.of("highlight")),
|
||||
new LspSemanticPresentationDTO(List.of(), List.of()),
|
||||
new LspSemanticPresentationDTO(
|
||||
List.of("pbs-keyword"),
|
||||
List.of("/themes/pbs/semantic-highlighting.css")),
|
||||
List.of(),
|
||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "fe-keyword")),
|
||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "pbs-keyword")),
|
||||
List.of(),
|
||||
List.of());
|
||||
|
||||
final EditorDocumentHighlightingResult result = EditorDocumentHighlightingRouter.route(
|
||||
fileBuffer,
|
||||
registry.resolve("pbs"),
|
||||
registry.resolve("pbs", analysis.semanticPresentation()),
|
||||
analysis);
|
||||
|
||||
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
|
||||
assertEquals(List.of("editor-semantic-pbs-keyword"), List.copyOf(result.styleSpans().getStyleSpan(0).getStyle()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -71,4 +75,36 @@ final class EditorDocumentHighlightingRouterTest {
|
||||
|
||||
assertEquals(EditorDocumentHighlightOwner.LOCAL, result.owner());
|
||||
}
|
||||
|
||||
@Test
|
||||
void frontendDocumentsDegradeToLocalWhenSemanticPresentationResourcesAreUnavailable() {
|
||||
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 LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||
new LspSessionStateDTO(true, List.of("highlight")),
|
||||
new LspSemanticPresentationDTO(
|
||||
List.of("pbs-keyword"),
|
||||
List.of("/themes/pbs/missing.css")),
|
||||
List.of(),
|
||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "pbs-keyword")),
|
||||
List.of(),
|
||||
List.of());
|
||||
|
||||
final EditorDocumentHighlightingResult result = EditorDocumentHighlightingRouter.route(
|
||||
fileBuffer,
|
||||
registry.resolve("pbs", analysis.semanticPresentation()),
|
||||
analysis);
|
||||
|
||||
assertEquals(EditorDocumentHighlightOwner.LOCAL, result.owner());
|
||||
assertTrue(result.styleSpans().getStyleSpan(0).getStyle().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,39 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
final class EditorDocumentPresentationRegistryTest {
|
||||
private final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
|
||||
|
||||
@Test
|
||||
void resolvesFrontendTypeIdsToFrontendPresentation() {
|
||||
assertEquals("fe", registry.resolve("pbs").styleKey());
|
||||
final EditorDocumentPresentation presentation = registry.resolve(
|
||||
"pbs",
|
||||
new LspSemanticPresentationDTO(
|
||||
java.util.List.of("pbs-keyword"),
|
||||
java.util.List.of("/themes/pbs/semantic-highlighting.css")));
|
||||
|
||||
assertEquals("pbs", presentation.styleKey());
|
||||
assertEquals(java.util.List.of("pbs-keyword"), presentation.semanticKeys());
|
||||
assertEquals(1, presentation.stylesheetUrls().size());
|
||||
assertTrue(presentation.stylesheetUrls().getFirst().endsWith("/themes/pbs/semantic-highlighting.css"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingFrontendResourcesDegradeToPlainFrontendPresentation() {
|
||||
final EditorDocumentPresentation presentation = registry.resolve(
|
||||
"pbs",
|
||||
new LspSemanticPresentationDTO(
|
||||
java.util.List.of("pbs-keyword"),
|
||||
java.util.List.of("/themes/pbs/missing.css")));
|
||||
|
||||
assertEquals("pbs", presentation.styleKey());
|
||||
assertEquals(java.util.List.of("pbs-keyword"), presentation.semanticKeys());
|
||||
assertTrue(presentation.stylesheetUrls().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user