editor with write capability
This commit is contained in:
parent
40a4d656ca
commit
770aa6a387
@ -10,6 +10,7 @@ public enum PbsSemanticKind {
|
|||||||
STRING("pbs-string"),
|
STRING("pbs-string"),
|
||||||
NUMBER("pbs-number"),
|
NUMBER("pbs-number"),
|
||||||
LITERAL("pbs-literal"),
|
LITERAL("pbs-literal"),
|
||||||
|
LIFECYCLE("pbs-lifecycle"),
|
||||||
KEYWORD("pbs-keyword"),
|
KEYWORD("pbs-keyword"),
|
||||||
OPERATOR("pbs-operator"),
|
OPERATOR("pbs-operator"),
|
||||||
PUNCTUATION("pbs-punctuation"),
|
PUNCTUATION("pbs-punctuation"),
|
||||||
|
|||||||
@ -14,6 +14,10 @@
|
|||||||
-fx-fill: #569cd6;
|
-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 {
|
.editor-workspace-code-area-type-pbs .text.editor-semantic-pbs-function {
|
||||||
-fx-fill: #f2c14e;
|
-fx-fill: #f2c14e;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,11 +104,12 @@ public final class SemanticIndex {
|
|||||||
final List<PbsToken> tokens,
|
final List<PbsToken> tokens,
|
||||||
final Map<String, LspSymbolKind> visibleKindsByName) {
|
final Map<String, LspSymbolKind> visibleKindsByName) {
|
||||||
final List<LspHighlightSpanDTO> highlights = new ArrayList<>();
|
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) {
|
if (token.kind() == PbsTokenKind.EOF) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final String semanticKey = semanticKey(token, visibleKindsByName);
|
final String semanticKey = semanticKey(tokens, index, visibleKindsByName);
|
||||||
if (semanticKey == null || semanticKey.isBlank()) {
|
if (semanticKey == null || semanticKey.isBlank()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -120,14 +121,50 @@ public final class SemanticIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String semanticKey(
|
private String semanticKey(
|
||||||
final PbsToken token,
|
final List<PbsToken> tokens,
|
||||||
|
final int tokenIndex,
|
||||||
final Map<String, LspSymbolKind> visibleKindsByName) {
|
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
|
final PbsSemanticKind semanticKind = token.kind() == p.studio.compiler.pbs.lexer.PbsTokenKind.IDENTIFIER
|
||||||
? semanticKindForIdentifier(token.lexeme(), visibleKindsByName)
|
? semanticKindForIdentifier(token.lexeme(), visibleKindsByName)
|
||||||
: PbsSemanticKind.forToken(token);
|
: PbsSemanticKind.forToken(token);
|
||||||
return semanticKind == null ? null : semanticKind.semanticKey();
|
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(
|
private PbsSemanticKind semanticKindForIdentifier(
|
||||||
final String lexeme,
|
final String lexeme,
|
||||||
final Map<String, LspSymbolKind> visibleKindsByName) {
|
final Map<String, LspSymbolKind> visibleKindsByName) {
|
||||||
|
|||||||
@ -109,10 +109,7 @@ final class EditorDocumentPresentationRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static EditorDocumentSyntaxHighlighting frontendSyntaxHighlighting(final String normalizedTypeId) {
|
private static EditorDocumentSyntaxHighlighting frontendSyntaxHighlighting(final String normalizedTypeId) {
|
||||||
return switch (normalizedTypeId) {
|
return EditorDocumentSyntaxHighlighting.plainText();
|
||||||
case "pbs" -> EditorDocumentSyntaxHighlighting.pbs();
|
|
||||||
default -> EditorDocumentSyntaxHighlighting.plainText();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String normalizeResourcePath(final String resourcePath) {
|
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 fileBuffer = openFiles.get(index);
|
||||||
final var tabButton = new Button(fileBuffer.tabLabel());
|
final var tabButton = new Button(fileBuffer.tabLabel());
|
||||||
final var tabLabel = new Label(fileBuffer.tabLabel());
|
final var tabLabel = new Label(fileBuffer.tabLabel());
|
||||||
|
final var dirtyIndicator = new StackPane();
|
||||||
final var closeChip = new StackPane();
|
final var closeChip = new StackPane();
|
||||||
final var closeIcon = new SVGPath();
|
final var closeIcon = new SVGPath();
|
||||||
final var tabContent = new HBox();
|
final var tabContent = new HBox();
|
||||||
@ -107,6 +108,7 @@ public final class EditorTabStrip extends HBox {
|
|||||||
: "editor-workspace-tab-button-editable");
|
: "editor-workspace-tab-button-editable");
|
||||||
tabLabel.getStyleClass().add("editor-workspace-tab-label");
|
tabLabel.getStyleClass().add("editor-workspace-tab-label");
|
||||||
tabLabel.setTextOverrun(OverrunStyle.ELLIPSIS);
|
tabLabel.setTextOverrun(OverrunStyle.ELLIPSIS);
|
||||||
|
dirtyIndicator.getStyleClass().add("editor-workspace-tab-dirty-indicator");
|
||||||
closeChip.getStyleClass().add("editor-workspace-tab-close-chip");
|
closeChip.getStyleClass().add("editor-workspace-tab-close-chip");
|
||||||
closeIcon.setContent("M 3 3 L 9 9 M 9 3 L 3 9");
|
closeIcon.setContent("M 3 3 L 9 9 M 9 3 L 3 9");
|
||||||
closeIcon.getStyleClass().add("editor-workspace-tab-close-icon");
|
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.getStyleClass().add("editor-workspace-tab-content");
|
||||||
tabContent.setAlignment(Pos.CENTER_LEFT);
|
tabContent.setAlignment(Pos.CENTER_LEFT);
|
||||||
HBox.setHgrow(tabLabel, Priority.ALWAYS);
|
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.setText(null);
|
||||||
tabButton.setGraphic(tabContent);
|
tabButton.setGraphic(tabContent);
|
||||||
applyTabMetrics(tabButton, tabLabel, closeChip);
|
applyTabMetrics(tabButton, tabLabel, dirtyIndicator, closeChip);
|
||||||
if (fileBuffer.path().equals(activePath)) {
|
if (fileBuffer.path().equals(activePath)) {
|
||||||
tabButton.getStyleClass().add("editor-workspace-tab-button-active");
|
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));
|
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.setMinWidth(TAB_WIDTH_HINT);
|
||||||
button.setPrefWidth(TAB_WIDTH_HINT);
|
button.setPrefWidth(TAB_WIDTH_HINT);
|
||||||
button.setMaxWidth(TAB_WIDTH_HINT);
|
button.setMaxWidth(TAB_WIDTH_HINT);
|
||||||
@ -191,6 +199,12 @@ public final class EditorTabStrip extends HBox {
|
|||||||
tabLabel.setMinWidth(0);
|
tabLabel.setMinWidth(0);
|
||||||
tabLabel.setPrefWidth(TAB_WIDTH_HINT - 30);
|
tabLabel.setPrefWidth(TAB_WIDTH_HINT - 30);
|
||||||
tabLabel.setMaxWidth(Double.MAX_VALUE);
|
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.setMinWidth(14);
|
||||||
closeChip.setPrefWidth(14);
|
closeChip.setPrefWidth(14);
|
||||||
closeChip.setMaxWidth(14);
|
closeChip.setMaxWidth(14);
|
||||||
|
|||||||
@ -24,21 +24,9 @@ public record EditorDocumentSyntaxHighlighting(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EditorDocumentSyntaxHighlighting plainText() {
|
public static EditorDocumentSyntaxHighlighting plainText() { return EditorDocumentSyntaxHighlightingPlainText.PLAIN_TEXT; }
|
||||||
return EditorDocumentSyntaxHighlightingPlainText.PLAIN_TEXT;
|
public static EditorDocumentSyntaxHighlighting json() { return EditorDocumentSyntaxHighlightingJson.JSON; }
|
||||||
}
|
public static EditorDocumentSyntaxHighlighting bash() { return EditorDocumentSyntaxHighlightingBash.BASH; }
|
||||||
|
|
||||||
public static EditorDocumentSyntaxHighlighting json() {
|
|
||||||
return EditorDocumentSyntaxHighlightingJson.JSON;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EditorDocumentSyntaxHighlighting bash() {
|
|
||||||
return EditorDocumentSyntaxHighlightingBash.BASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EditorDocumentSyntaxHighlighting pbs() {
|
|
||||||
return EditorDocumentSyntaxHighlightingPbs.PBS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StyleSpans<Collection<String>> highlight(final String content) {
|
public StyleSpans<Collection<String>> highlight(final String content) {
|
||||||
final StyleSpansBuilder<Collection<String>> builder = new StyleSpansBuilder<>();
|
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;
|
-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 {
|
.editor-workspace-tab-close-chip {
|
||||||
-fx-alignment: center;
|
-fx-alignment: center;
|
||||||
-fx-background-color: #131820;
|
-fx-background-color: #131820;
|
||||||
@ -627,6 +634,10 @@
|
|||||||
-fx-text-fill: #d9dee5;
|
-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 {
|
.editor-workspace-tab-button-read-only .editor-workspace-tab-close-icon {
|
||||||
-fx-stroke: #d9dee5;
|
-fx-stroke: #d9dee5;
|
||||||
}
|
}
|
||||||
@ -646,6 +657,10 @@
|
|||||||
-fx-text-fill: #eff4fa;
|
-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 {
|
.editor-workspace-tab-button-read-only:hover .editor-workspace-tab-close-chip {
|
||||||
-fx-background-color: #1c232c;
|
-fx-background-color: #1c232c;
|
||||||
-fx-border-color: #6b7989;
|
-fx-border-color: #6b7989;
|
||||||
@ -688,6 +703,10 @@
|
|||||||
-fx-text-fill: #e8f6eb;
|
-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 {
|
.editor-workspace-tab-button-editable .editor-workspace-tab-close-icon {
|
||||||
-fx-stroke: #e8f6eb;
|
-fx-stroke: #e8f6eb;
|
||||||
}
|
}
|
||||||
@ -707,6 +726,10 @@
|
|||||||
-fx-text-fill: #f4fff5;
|
-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 {
|
.editor-workspace-tab-button-editable:hover .editor-workspace-tab-close-chip {
|
||||||
-fx-background-color: #192720;
|
-fx-background-color: #192720;
|
||||||
-fx-border-color: #789886;
|
-fx-border-color: #789886;
|
||||||
@ -739,6 +762,10 @@
|
|||||||
-fx-font-weight: bold;
|
-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 {
|
.editor-workspace-tab-button-active .editor-workspace-tab-close-icon {
|
||||||
-fx-stroke: #ffffff;
|
-fx-stroke: #ffffff;
|
||||||
}
|
}
|
||||||
@ -841,10 +868,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-workspace-command-button {
|
.editor-workspace-command-button {
|
||||||
-fx-min-height: 34;
|
-fx-alignment: center;
|
||||||
-fx-pref-height: 34;
|
-fx-pref-width: 118px;
|
||||||
-fx-max-height: 34;
|
-fx-min-width: 118px;
|
||||||
-fx-padding: 0 14 0 14;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-workspace-warning {
|
.editor-workspace-warning {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test;
|
|||||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
final class EditorDocumentPresentationRegistryTest {
|
final class EditorDocumentPresentationRegistryTest {
|
||||||
@ -35,7 +34,7 @@ final class EditorDocumentPresentationRegistryTest {
|
|||||||
assertEquals("pbs", presentation.styleKey());
|
assertEquals("pbs", presentation.styleKey());
|
||||||
assertEquals(java.util.List.of("pbs-keyword"), presentation.semanticKeys());
|
assertEquals(java.util.List.of("pbs-keyword"), presentation.semanticKeys());
|
||||||
assertEquals(1, presentation.stylesheetUrls().size());
|
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
|
@Test
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user