From 770aa6a38710b63f962e6ce25ff52e18cb7d3460 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 4 Apr 2026 12:42:14 +0100 Subject: [PATCH] editor with write capability --- .../studio/compiler/pbs/PbsSemanticKind.java | 1 + .../themes/pbs/semantic-highlighting.css | 4 ++ .../p/studio/lsp/models/SemanticIndex.java | 43 +++++++++++++++++-- .../EditorDocumentPresentationRegistry.java | 5 +-- .../workspaces/editor/EditorTabStrip.java | 20 +++++++-- .../EditorDocumentSyntaxHighlighting.java | 18 ++------ .../EditorDocumentSyntaxHighlightingPbs.java | 32 -------------- .../resources/themes/default-prometeu.css | 34 +++++++++++++-- ...ditorDocumentPresentationRegistryTest.java | 3 +- 9 files changed, 97 insertions(+), 63 deletions(-) delete mode 100644 prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlightingPbs.java diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsSemanticKind.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsSemanticKind.java index 5322be97..0c577e4a 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsSemanticKind.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsSemanticKind.java @@ -10,6 +10,7 @@ public enum PbsSemanticKind { STRING("pbs-string"), NUMBER("pbs-number"), LITERAL("pbs-literal"), + LIFECYCLE("pbs-lifecycle"), KEYWORD("pbs-keyword"), OPERATOR("pbs-operator"), PUNCTUATION("pbs-punctuation"), diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css index cf0681c8..801b2730 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css @@ -14,6 +14,10 @@ -fx-fill: #569cd6; } +.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-lifecycle { + -fx-fill: #ef50c0; +} + .editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-function { -fx-fill: #f2c14e; } diff --git a/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/models/SemanticIndex.java b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/models/SemanticIndex.java index 50fa3a81..1649d505 100644 --- a/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/models/SemanticIndex.java +++ b/prometeu-lsp/prometeu-lsp-v1/src/main/java/p/studio/lsp/models/SemanticIndex.java @@ -104,11 +104,12 @@ public final class SemanticIndex { final List tokens, final Map visibleKindsByName) { final List highlights = new ArrayList<>(); - for (final PbsToken token : tokens) { + for (int index = 0; index < tokens.size(); index++) { + final PbsToken token = tokens.get(index); if (token.kind() == PbsTokenKind.EOF) { continue; } - final String semanticKey = semanticKey(token, visibleKindsByName); + final String semanticKey = semanticKey(tokens, index, visibleKindsByName); if (semanticKey == null || semanticKey.isBlank()) { continue; } @@ -120,14 +121,50 @@ public final class SemanticIndex { } private String semanticKey( - final PbsToken token, + final List tokens, + final int tokenIndex, final Map visibleKindsByName) { + final PbsToken token = tokens.get(tokenIndex); + if (isLifecycleAttributeToken(tokens, tokenIndex)) { + return PbsSemanticKind.LIFECYCLE.semanticKey(); + } final PbsSemanticKind semanticKind = token.kind() == p.studio.compiler.pbs.lexer.PbsTokenKind.IDENTIFIER ? semanticKindForIdentifier(token.lexeme(), visibleKindsByName) : PbsSemanticKind.forToken(token); return semanticKind == null ? null : semanticKind.semanticKey(); } + private boolean isLifecycleAttributeToken(final List tokens, final int tokenIndex) { + if (tokenIndex < 0 || tokenIndex >= tokens.size()) { + return false; + } + final PbsToken token = tokens.get(tokenIndex); + return switch (token.kind()) { + case LEFT_BRACKET -> hasLifecycleAttributeWindow(tokens, tokenIndex, tokenIndex + 1, tokenIndex + 2); + case IDENTIFIER -> hasLifecycleAttributeWindow(tokens, tokenIndex - 1, tokenIndex, tokenIndex + 1); + case RIGHT_BRACKET -> hasLifecycleAttributeWindow(tokens, tokenIndex - 2, tokenIndex - 1, tokenIndex); + default -> false; + }; + } + + private boolean hasLifecycleAttributeWindow( + final List tokens, + final int leftBracketIndex, + final int identifierIndex, + final int rightBracketIndex) { + if (leftBracketIndex < 0 || rightBracketIndex >= tokens.size()) { + return false; + } + return tokens.get(leftBracketIndex).kind() == PbsTokenKind.LEFT_BRACKET + && tokens.get(identifierIndex).kind() == PbsTokenKind.IDENTIFIER + && isLifecycleAttributeName(tokens.get(identifierIndex).lexeme()) + && tokens.get(rightBracketIndex).kind() == PbsTokenKind.RIGHT_BRACKET; + } + + private boolean isLifecycleAttributeName(final String lexeme) { + return "Init".equals(lexeme) || "Frame".equals(lexeme); + } + private PbsSemanticKind semanticKindForIdentifier( final String lexeme, final Map visibleKindsByName) { diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistry.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistry.java index f50976be..846b280c 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistry.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistry.java @@ -109,10 +109,7 @@ final class EditorDocumentPresentationRegistry { } private static EditorDocumentSyntaxHighlighting frontendSyntaxHighlighting(final String normalizedTypeId) { - return switch (normalizedTypeId) { - case "pbs" -> EditorDocumentSyntaxHighlighting.pbs(); - default -> EditorDocumentSyntaxHighlighting.plainText(); - }; + return EditorDocumentSyntaxHighlighting.plainText(); } private static String normalizeResourcePath(final String resourcePath) { diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java index 668ac2a3..84dc590e 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java @@ -93,6 +93,7 @@ public final class EditorTabStrip extends HBox { final var fileBuffer = openFiles.get(index); final var tabButton = new Button(fileBuffer.tabLabel()); final var tabLabel = new Label(fileBuffer.tabLabel()); + final var dirtyIndicator = new StackPane(); final var closeChip = new StackPane(); final var closeIcon = new SVGPath(); final var tabContent = new HBox(); @@ -107,6 +108,7 @@ public final class EditorTabStrip extends HBox { : "editor-workspace-tab-button-editable"); tabLabel.getStyleClass().add("editor-workspace-tab-label"); tabLabel.setTextOverrun(OverrunStyle.ELLIPSIS); + dirtyIndicator.getStyleClass().add("editor-workspace-tab-dirty-indicator"); closeChip.getStyleClass().add("editor-workspace-tab-close-chip"); closeIcon.setContent("M 3 3 L 9 9 M 9 3 L 3 9"); closeIcon.getStyleClass().add("editor-workspace-tab-close-icon"); @@ -115,10 +117,12 @@ public final class EditorTabStrip extends HBox { tabContent.getStyleClass().add("editor-workspace-tab-content"); tabContent.setAlignment(Pos.CENTER_LEFT); HBox.setHgrow(tabLabel, Priority.ALWAYS); - tabContent.getChildren().addAll(tabLabel, closeChip); + dirtyIndicator.setManaged(fileBuffer.dirty()); + dirtyIndicator.setVisible(fileBuffer.dirty()); + tabContent.getChildren().addAll(tabLabel, dirtyIndicator, closeChip); tabButton.setText(null); tabButton.setGraphic(tabContent); - applyTabMetrics(tabButton, tabLabel, closeChip); + applyTabMetrics(tabButton, tabLabel, dirtyIndicator, closeChip); if (fileBuffer.path().equals(activePath)) { tabButton.getStyleClass().add("editor-workspace-tab-button-active"); } @@ -180,7 +184,11 @@ public final class EditorTabStrip extends HBox { return Math.max(1, (int) Math.floor(usableWidth / TAB_WIDTH_HINT)); } - private void applyTabMetrics(final Button button, final Label tabLabel, final StackPane closeChip) { + private void applyTabMetrics( + final Button button, + final Label tabLabel, + final StackPane dirtyIndicator, + final StackPane closeChip) { button.setMinWidth(TAB_WIDTH_HINT); button.setPrefWidth(TAB_WIDTH_HINT); button.setMaxWidth(TAB_WIDTH_HINT); @@ -191,6 +199,12 @@ public final class EditorTabStrip extends HBox { tabLabel.setMinWidth(0); tabLabel.setPrefWidth(TAB_WIDTH_HINT - 30); tabLabel.setMaxWidth(Double.MAX_VALUE); + dirtyIndicator.setMinWidth(8); + dirtyIndicator.setPrefWidth(8); + dirtyIndicator.setMaxWidth(8); + dirtyIndicator.setMinHeight(8); + dirtyIndicator.setPrefHeight(8); + dirtyIndicator.setMaxHeight(8); closeChip.setMinWidth(14); closeChip.setPrefWidth(14); closeChip.setMaxWidth(14); diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlighting.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlighting.java index 50af86cd..92271b9b 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlighting.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlighting.java @@ -24,21 +24,9 @@ public record EditorDocumentSyntaxHighlighting( } } - public static EditorDocumentSyntaxHighlighting plainText() { - return EditorDocumentSyntaxHighlightingPlainText.PLAIN_TEXT; - } - - public static EditorDocumentSyntaxHighlighting json() { - return EditorDocumentSyntaxHighlightingJson.JSON; - } - - public static EditorDocumentSyntaxHighlighting bash() { - return EditorDocumentSyntaxHighlightingBash.BASH; - } - - public static EditorDocumentSyntaxHighlighting pbs() { - return EditorDocumentSyntaxHighlightingPbs.PBS; - } + public static EditorDocumentSyntaxHighlighting plainText() { return EditorDocumentSyntaxHighlightingPlainText.PLAIN_TEXT; } + public static EditorDocumentSyntaxHighlighting json() { return EditorDocumentSyntaxHighlightingJson.JSON; } + public static EditorDocumentSyntaxHighlighting bash() { return EditorDocumentSyntaxHighlightingBash.BASH; } public StyleSpans> highlight(final String content) { final StyleSpansBuilder> builder = new StyleSpansBuilder<>(); diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlightingPbs.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlightingPbs.java deleted file mode 100644 index 4377318d..00000000 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/syntaxhighlight/EditorDocumentSyntaxHighlightingPbs.java +++ /dev/null @@ -1,32 +0,0 @@ -package p.studio.workspaces.editor.syntaxhighlight; - -import java.util.List; -import java.util.regex.Pattern; - -public final class EditorDocumentSyntaxHighlightingPbs { - public static final EditorDocumentSyntaxHighlighting PBS = new EditorDocumentSyntaxHighlighting( - Pattern.compile( - "(?//[^\\n\\r]*)" - + "|(?\"(?:\\\\.|[^\"\\\\])*\")" - + "|(?\\b\\d+(?:\\.\\d+)?b?\\b)" - + "|(?\\b(?:true|false|none)\\b)" - + "|(?\\b(?: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)\\b)" - + "|(?\\b[A-Za-z_][A-Za-z0-9_]*\\b(?=\\s*\\())" - + "|(?\\+\\=|\\-\\=|\\*\\=|/\\=|%\\=|\\=\\=|!\\=|<\\=|>\\=|&&|\\|\\||\\->|[+\\-*/%!=<>?])" - + "|(?\\.|\\.\\.|[(){}\\[\\],:;@])"), - List.of( - new EditorDocumentHighlightToken("COMMENT", "editor-semantic-pbs-comment"), - new EditorDocumentHighlightToken("STRING", "editor-semantic-pbs-string"), - new EditorDocumentHighlightToken("NUMBER", "editor-semantic-pbs-number"), - new EditorDocumentHighlightToken("LITERAL", "editor-semantic-pbs-literal"), - new EditorDocumentHighlightToken("KEYWORD", "editor-semantic-pbs-keyword"), - new EditorDocumentHighlightToken("FUNCTION", "editor-semantic-pbs-function"), - new EditorDocumentHighlightToken("OPERATOR", "editor-semantic-pbs-operator"), - new EditorDocumentHighlightToken("PUNCTUATION", "editor-semantic-pbs-punctuation"))); - - private EditorDocumentSyntaxHighlightingPbs() { - } -} diff --git a/prometeu-studio/src/main/resources/themes/default-prometeu.css b/prometeu-studio/src/main/resources/themes/default-prometeu.css index 4a634a1d..1b05e790 100644 --- a/prometeu-studio/src/main/resources/themes/default-prometeu.css +++ b/prometeu-studio/src/main/resources/themes/default-prometeu.css @@ -592,6 +592,13 @@ -fx-font-size: 12px; } +.editor-workspace-tab-dirty-indicator { + -fx-background-color: #d9e3ef; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-opacity: 0.9; +} + .editor-workspace-tab-close-chip { -fx-alignment: center; -fx-background-color: #131820; @@ -627,6 +634,10 @@ -fx-text-fill: #d9dee5; } +.editor-workspace-tab-button-read-only .editor-workspace-tab-dirty-indicator { + -fx-background-color: #d9dee5; +} + .editor-workspace-tab-button-read-only .editor-workspace-tab-close-icon { -fx-stroke: #d9dee5; } @@ -646,6 +657,10 @@ -fx-text-fill: #eff4fa; } +.editor-workspace-tab-button-read-only:hover .editor-workspace-tab-dirty-indicator { + -fx-background-color: #eff4fa; +} + .editor-workspace-tab-button-read-only:hover .editor-workspace-tab-close-chip { -fx-background-color: #1c232c; -fx-border-color: #6b7989; @@ -688,6 +703,10 @@ -fx-text-fill: #e8f6eb; } +.editor-workspace-tab-button-editable .editor-workspace-tab-dirty-indicator { + -fx-background-color: #b9f0c7; +} + .editor-workspace-tab-button-editable .editor-workspace-tab-close-icon { -fx-stroke: #e8f6eb; } @@ -707,6 +726,10 @@ -fx-text-fill: #f4fff5; } +.editor-workspace-tab-button-editable:hover .editor-workspace-tab-dirty-indicator { + -fx-background-color: #f4fff5; +} + .editor-workspace-tab-button-editable:hover .editor-workspace-tab-close-chip { -fx-background-color: #192720; -fx-border-color: #789886; @@ -739,6 +762,10 @@ -fx-font-weight: bold; } +.editor-workspace-tab-button-active .editor-workspace-tab-dirty-indicator { + -fx-background-color: #ffffff; +} + .editor-workspace-tab-button-active .editor-workspace-tab-close-icon { -fx-stroke: #ffffff; } @@ -841,10 +868,9 @@ } .editor-workspace-command-button { - -fx-min-height: 34; - -fx-pref-height: 34; - -fx-max-height: 34; - -fx-padding: 0 14 0 14; + -fx-alignment: center; + -fx-pref-width: 118px; + -fx-min-width: 118px; } .editor-workspace-warning { diff --git a/prometeu-studio/src/test/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistryTest.java b/prometeu-studio/src/test/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistryTest.java index 458e9f4d..13450c3b 100644 --- a/prometeu-studio/src/test/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistryTest.java +++ b/prometeu-studio/src/test/java/p/studio/workspaces/editor/EditorDocumentPresentationRegistryTest.java @@ -4,7 +4,6 @@ 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; final class EditorDocumentPresentationRegistryTest { @@ -35,7 +34,7 @@ final class EditorDocumentPresentationRegistryTest { assertEquals("pbs", presentation.styleKey()); assertEquals(java.util.List.of("pbs-keyword"), presentation.semanticKeys()); assertEquals(1, presentation.stylesheetUrls().size()); - assertFalse(presentation.highlight("fn main() -> void {}").getStyleSpan(0).getStyle().isEmpty()); + assertTrue(presentation.highlight("fn main() -> void {}").getStyleSpan(0).getStyle().isEmpty()); } @Test