editor with write capability
This commit is contained in:
parent
40a4d656ca
commit
770aa6a387
@ -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"),
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -104,11 +104,12 @@ public final class SemanticIndex {
|
||||
final List<PbsToken> tokens,
|
||||
final Map<String, LspSymbolKind> visibleKindsByName) {
|
||||
final List<LspHighlightSpanDTO> 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<PbsToken> tokens,
|
||||
final int tokenIndex,
|
||||
final Map<String, LspSymbolKind> 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<PbsToken> 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<PbsToken> 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<String, LspSymbolKind> visibleKindsByName) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<Collection<String>> highlight(final String content) {
|
||||
final StyleSpansBuilder<Collection<String>> builder = new StyleSpansBuilder<>();
|
||||
|
||||
@ -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(
|
||||
"(?<COMMENT>//[^\\n\\r]*)"
|
||||
+ "|(?<STRING>\"(?:\\\\.|[^\"\\\\])*\")"
|
||||
+ "|(?<NUMBER>\\b\\d+(?:\\.\\d+)?b?\\b)"
|
||||
+ "|(?<LITERAL>\\b(?:true|false|none)\\b)"
|
||||
+ "|(?<KEYWORD>\\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)"
|
||||
+ "|(?<FUNCTION>\\b[A-Za-z_][A-Za-z0-9_]*\\b(?=\\s*\\())"
|
||||
+ "|(?<OPERATOR>\\+\\=|\\-\\=|\\*\\=|/\\=|%\\=|\\=\\=|!\\=|<\\=|>\\=|&&|\\|\\||\\->|[+\\-*/%!=<>?])"
|
||||
+ "|(?<PUNCTUATION>\\.|\\.\\.|[(){}\\[\\],:;@])"),
|
||||
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() {
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user