PBS highlight colors
This commit is contained in:
parent
5bbd753ba0
commit
f368ed94d2
@ -574,7 +574,7 @@ final class PbsDeclarationParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PbsToken consumeCallableName() {
|
private PbsToken consumeCallableName() {
|
||||||
if (cursor.check(PbsTokenKind.IDENTIFIER) || cursor.check(PbsTokenKind.ERROR)) {
|
if (cursor.check(PbsTokenKind.IDENTIFIER)) {
|
||||||
return cursor.advance();
|
return cursor.advance();
|
||||||
}
|
}
|
||||||
final var token = cursor.peek();
|
final var token = cursor.peek();
|
||||||
|
|||||||
@ -39,7 +39,7 @@ final class PbsExprParserContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PbsToken consumeMemberName(final String message) {
|
PbsToken consumeMemberName(final String message) {
|
||||||
if (cursor.check(PbsTokenKind.IDENTIFIER) || cursor.check(PbsTokenKind.ERROR)) {
|
if (cursor.check(PbsTokenKind.IDENTIFIER)) {
|
||||||
return cursor.advance();
|
return cursor.advance();
|
||||||
}
|
}
|
||||||
final var token = cursor.peek();
|
final var token = cursor.peek();
|
||||||
|
|||||||
@ -30,7 +30,7 @@ declare service Log
|
|||||||
LowLog.write(3, msg);
|
LowLog.write(3, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(msg: str) -> void
|
fn failure(msg: str) -> void
|
||||||
{
|
{
|
||||||
LowLog.write(4, msg);
|
LowLog.write(4, msg);
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ declare service Log
|
|||||||
LowLog.write_tag(3, tag, msg);
|
LowLog.write_tag(3, tag, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(tag: int, msg: str) -> void
|
fn failure(tag: int, msg: str) -> void
|
||||||
{
|
{
|
||||||
LowLog.write_tag(4, tag, msg);
|
LowLog.write_tag(4, tag, msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,7 +173,7 @@ class PbsParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldParseServiceAndMemberNamesUsingErrorKeyword() {
|
void shouldRejectErrorKeywordAsCallableAndMemberName() {
|
||||||
final var source = """
|
final var source = """
|
||||||
declare service Log {
|
declare service Log {
|
||||||
fn error(msg: str) -> void { return; }
|
fn error(msg: str) -> void { return; }
|
||||||
@ -181,20 +181,17 @@ class PbsParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run() -> void {
|
fn run() -> void {
|
||||||
Log.error("oops");
|
Log.failure("oops");
|
||||||
Log.error(7, "oops");
|
Log.failure(7, "oops");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
final var diagnostics = DiagnosticSink.empty();
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
final var fileId = new FileId(0);
|
final var fileId = new FileId(0);
|
||||||
|
|
||||||
final PbsAst.File ast = PbsParser.parse(PbsLexer.lex(source, fileId, diagnostics), fileId, diagnostics);
|
PbsParser.parse(PbsLexer.lex(source, fileId, diagnostics), fileId, diagnostics);
|
||||||
|
|
||||||
assertTrue(diagnostics.isEmpty(), "Parser should accept 'error' as callable/member name");
|
assertFalse(diagnostics.isEmpty(), "Parser should reject reserved keyword 'error' as callable/member name");
|
||||||
final var service = assertInstanceOf(PbsAst.ServiceDecl.class, ast.topDecls().getFirst());
|
|
||||||
assertEquals(2, service.methods().size());
|
|
||||||
assertEquals("error", service.methods().getFirst().name());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -219,6 +219,7 @@ final class LspServiceImplTest {
|
|||||||
pub fn helper() -> void;
|
pub fn helper() -> void;
|
||||||
pub fn helper_result() -> result<MyError> int;
|
pub fn helper_result() -> result<MyError> int;
|
||||||
pub error MyError;
|
pub error MyError;
|
||||||
|
pub service Log;
|
||||||
""");
|
""");
|
||||||
Files.writeString(src.resolve("helper.pbs"), "fn helper() -> void {}\n");
|
Files.writeString(src.resolve("helper.pbs"), "fn helper() -> void {}\n");
|
||||||
Files.writeString(src.resolve("helper_result.pbs"), """
|
Files.writeString(src.resolve("helper_result.pbs"), """
|
||||||
@ -230,6 +231,11 @@ final class LspServiceImplTest {
|
|||||||
return ok(1);
|
return ok(1);
|
||||||
}
|
}
|
||||||
""");
|
""");
|
||||||
|
Files.writeString(src.resolve("log.pbs"), """
|
||||||
|
declare service Log {
|
||||||
|
fn failure(message: string) -> void;
|
||||||
|
}
|
||||||
|
""");
|
||||||
return tempDir;
|
return tempDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,16 +11,19 @@ final class EditorDocumentHighlightingRouter {
|
|||||||
final EditorOpenFileBuffer fileBuffer,
|
final EditorOpenFileBuffer fileBuffer,
|
||||||
final EditorDocumentPresentation presentation,
|
final EditorDocumentPresentation presentation,
|
||||||
final LspAnalyzeDocumentResult analysis) {
|
final LspAnalyzeDocumentResult analysis) {
|
||||||
|
final var localHighlighting = presentation.highlight(fileBuffer.content());
|
||||||
if (fileBuffer.frontendDocument()
|
if (fileBuffer.frontendDocument()
|
||||||
&& analysis != null
|
&& analysis != null
|
||||||
&& presentation.supportsSemanticHighlighting()
|
&& presentation.supportsSemanticHighlighting()
|
||||||
&& !analysis.semanticHighlights().isEmpty()) {
|
&& !analysis.semanticHighlights().isEmpty()) {
|
||||||
return new EditorDocumentHighlightingResult(
|
return new EditorDocumentHighlightingResult(
|
||||||
EditorDocumentHighlightOwner.LSP,
|
EditorDocumentHighlightOwner.LSP,
|
||||||
EditorDocumentSemanticHighlighting.highlight(fileBuffer.content(), analysis.semanticHighlights()));
|
EditorDocumentSemanticHighlighting.overlay(
|
||||||
|
localHighlighting,
|
||||||
|
EditorDocumentSemanticHighlighting.highlight(fileBuffer.content(), analysis.semanticHighlights())));
|
||||||
}
|
}
|
||||||
return new EditorDocumentHighlightingResult(
|
return new EditorDocumentHighlightingResult(
|
||||||
EditorDocumentHighlightOwner.LOCAL,
|
EditorDocumentHighlightOwner.LOCAL,
|
||||||
presentation.highlight(fileBuffer.content()));
|
localHighlighting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.workspaces.editor;
|
package p.studio.workspaces.editor;
|
||||||
|
|
||||||
import p.studio.compiler.FrontendRegistryService;
|
import p.studio.compiler.FrontendRegistryService;
|
||||||
|
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
|
||||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
import p.studio.vfs.messages.VfsDocumentTypeIds;
|
import p.studio.vfs.messages.VfsDocumentTypeIds;
|
||||||
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
|
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
|
||||||
@ -55,21 +56,19 @@ final class EditorDocumentPresentationRegistry {
|
|||||||
private EditorDocumentPresentation frontendPresentation(
|
private EditorDocumentPresentation frontendPresentation(
|
||||||
final String normalizedTypeId,
|
final String normalizedTypeId,
|
||||||
final LspSemanticPresentationDTO semanticPresentation) {
|
final LspSemanticPresentationDTO semanticPresentation) {
|
||||||
if (semanticPresentation == null) {
|
final var frontendSpec = FrontendRegistryService.getFrontendSpec(normalizedTypeId).orElseThrow();
|
||||||
return new EditorDocumentPresentation(
|
final FrontendSemanticPresentationSpec frontendPresentationSpec = frontendSpec.getSemanticPresentation();
|
||||||
normalizedTypeId,
|
final java.util.List<String> stylesheetUrls = resolveFrontendStylesheets(
|
||||||
java.util.List.of(),
|
semanticPresentation == null ? java.util.List.of() : semanticPresentation.resources(),
|
||||||
java.util.List.of(),
|
frontendPresentationSpec.resources());
|
||||||
EditorDocumentSyntaxHighlighting.plainText());
|
final java.util.List<String> semanticKeys = semanticPresentation == null
|
||||||
}
|
? frontendPresentationSpec.semanticKeys()
|
||||||
|
: semanticPresentation.semanticKeys();
|
||||||
return new EditorDocumentPresentation(
|
return new EditorDocumentPresentation(
|
||||||
normalizedTypeId,
|
normalizedTypeId,
|
||||||
semanticPresentation.resources().stream()
|
stylesheetUrls,
|
||||||
.map(EditorDocumentPresentationRegistry::resourceStylesheet)
|
semanticKeys,
|
||||||
.flatMap(Optional::stream)
|
frontendSyntaxHighlighting(normalizedTypeId));
|
||||||
.toList(),
|
|
||||||
semanticPresentation.semanticKeys(),
|
|
||||||
EditorDocumentSyntaxHighlighting.plainText());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalize(final String typeId) {
|
private String normalize(final String typeId) {
|
||||||
@ -93,6 +92,29 @@ final class EditorDocumentPresentationRegistry {
|
|||||||
return Optional.of(resource.toExternalForm());
|
return Optional.of(resource.toExternalForm());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static java.util.List<String> resolveFrontendStylesheets(
|
||||||
|
final java.util.List<String> primaryResources,
|
||||||
|
final java.util.List<String> fallbackResources) {
|
||||||
|
final java.util.List<String> primary = primaryResources.stream()
|
||||||
|
.map(EditorDocumentPresentationRegistry::resourceStylesheet)
|
||||||
|
.flatMap(Optional::stream)
|
||||||
|
.toList();
|
||||||
|
if (!primary.isEmpty()) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
return fallbackResources.stream()
|
||||||
|
.map(EditorDocumentPresentationRegistry::resourceStylesheet)
|
||||||
|
.flatMap(Optional::stream)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EditorDocumentSyntaxHighlighting frontendSyntaxHighlighting(final String normalizedTypeId) {
|
||||||
|
return switch (normalizedTypeId) {
|
||||||
|
case "pbs" -> EditorDocumentSyntaxHighlighting.pbs();
|
||||||
|
default -> EditorDocumentSyntaxHighlighting.plainText();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static String normalizeResourcePath(final String resourcePath) {
|
private static String normalizeResourcePath(final String resourcePath) {
|
||||||
final String normalized = Objects.requireNonNull(resourcePath, "resourcePath").trim();
|
final String normalized = Objects.requireNonNull(resourcePath, "resourcePath").trim();
|
||||||
if (normalized.isEmpty()) {
|
if (normalized.isEmpty()) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class EditorDocumentSemanticHighlighting {
|
public final class EditorDocumentSemanticHighlighting {
|
||||||
@ -40,4 +41,24 @@ public final class EditorDocumentSemanticHighlighting {
|
|||||||
}
|
}
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StyleSpans<Collection<String>> overlay(
|
||||||
|
final StyleSpans<Collection<String>> baseHighlighting,
|
||||||
|
final StyleSpans<Collection<String>> semanticHighlighting) {
|
||||||
|
return baseHighlighting.overlay(semanticHighlighting, EditorDocumentSemanticHighlighting::mergeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<String> mergeStyles(
|
||||||
|
final Collection<String> baseStyles,
|
||||||
|
final Collection<String> semanticStyles) {
|
||||||
|
if (baseStyles.isEmpty()) {
|
||||||
|
return semanticStyles;
|
||||||
|
}
|
||||||
|
if (semanticStyles.isEmpty()) {
|
||||||
|
return baseStyles;
|
||||||
|
}
|
||||||
|
final LinkedHashSet<String> merged = new LinkedHashSet<>(baseStyles);
|
||||||
|
merged.addAll(semanticStyles);
|
||||||
|
return List.copyOf(merged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,10 @@ public record EditorDocumentSyntaxHighlighting(
|
|||||||
return EditorDocumentSyntaxHighlightingBash.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<>();
|
||||||
if (tokenPattern == null) {
|
if (tokenPattern == null) {
|
||||||
|
|||||||
@ -46,7 +46,8 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
analysis);
|
analysis);
|
||||||
|
|
||||||
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
|
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
|
||||||
assertEquals(List.of("editor-semantic-pbs-keyword"), List.copyOf(result.styleSpans().getStyleSpan(0).getStyle()));
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-keyword"));
|
||||||
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-function"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -80,7 +81,7 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void frontendDocumentsDegradeToLocalWhenSemanticPresentationResourcesAreUnavailable() {
|
void frontendDocumentsFallbackToLocalPbsSyntaxWhenSemanticPresentationResourcesAreUnavailable() {
|
||||||
final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
|
final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
|
||||||
final EditorOpenFileBuffer fileBuffer = new EditorOpenFileBuffer(
|
final EditorOpenFileBuffer fileBuffer = new EditorOpenFileBuffer(
|
||||||
Path.of("/tmp/example/src/main.pbs"),
|
Path.of("/tmp/example/src/main.pbs"),
|
||||||
@ -108,8 +109,9 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
registry.resolve("pbs", analysis.semanticPresentation()),
|
registry.resolve("pbs", analysis.semanticPresentation()),
|
||||||
analysis);
|
analysis);
|
||||||
|
|
||||||
assertEquals(EditorDocumentHighlightOwner.LOCAL, result.owner());
|
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
|
||||||
assertTrue(result.styleSpans().getStyleSpan(0).getStyle().isEmpty());
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-keyword"));
|
||||||
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-function"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -145,6 +147,41 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-service"));
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-service"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void frontendDocumentsKeepLocalSyntaxForRangesWithoutSemanticCoverage() {
|
||||||
|
final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
|
||||||
|
final EditorOpenFileBuffer fileBuffer = new EditorOpenFileBuffer(
|
||||||
|
Path.of("/tmp/example/src/main.pbs"),
|
||||||
|
"main.pbs",
|
||||||
|
"pbs",
|
||||||
|
"fn main() -> void { Game.tick(1); }",
|
||||||
|
"LF",
|
||||||
|
true,
|
||||||
|
VfsDocumentAccessMode.READ_ONLY,
|
||||||
|
false);
|
||||||
|
|
||||||
|
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||||
|
new LspSessionStateDTO(true, List.of("highlight")),
|
||||||
|
new LspSemanticPresentationDTO(
|
||||||
|
List.of("pbs-service"),
|
||||||
|
List.of("/themes/pbs/semantic-highlighting.css")),
|
||||||
|
List.of(),
|
||||||
|
List.of(new LspHighlightSpanDTO(new LspRangeDTO(19, 23), "pbs-service")),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of());
|
||||||
|
|
||||||
|
final EditorDocumentHighlightingResult result = EditorDocumentHighlightingRouter.route(
|
||||||
|
fileBuffer,
|
||||||
|
registry.resolve("pbs", analysis.semanticPresentation()),
|
||||||
|
analysis);
|
||||||
|
|
||||||
|
assertEquals(EditorDocumentHighlightOwner.LSP, result.owner());
|
||||||
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-keyword"));
|
||||||
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-service"));
|
||||||
|
assertTrue(containsStyle(result.styleSpans(), "editor-semantic-pbs-function"));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean containsStyle(
|
private boolean containsStyle(
|
||||||
final org.fxmisc.richtext.model.StyleSpans<Collection<String>> styleSpans,
|
final org.fxmisc.richtext.model.StyleSpans<Collection<String>> styleSpans,
|
||||||
final String styleClass) {
|
final String styleClass) {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ 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 {
|
||||||
@ -24,7 +25,7 @@ final class EditorDocumentPresentationRegistryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void missingFrontendResourcesDegradeToPlainFrontendPresentation() {
|
void missingFrontendResourcesFallbackToFrontendOwnedPresentationResources() {
|
||||||
final EditorDocumentPresentation presentation = registry.resolve(
|
final EditorDocumentPresentation presentation = registry.resolve(
|
||||||
"pbs",
|
"pbs",
|
||||||
new LspSemanticPresentationDTO(
|
new LspSemanticPresentationDTO(
|
||||||
@ -33,7 +34,8 @@ 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());
|
||||||
assertTrue(presentation.stylesheetUrls().isEmpty());
|
assertEquals(1, presentation.stylesheetUrls().size());
|
||||||
|
assertFalse(presentation.highlight("fn main() -> void {}").getStyleSpan(0).getStyle().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -28,7 +28,7 @@ fn frame() -> void
|
|||||||
if (loading_handle == -1) {
|
if (loading_handle == -1) {
|
||||||
let t = Assets.load(assets.ui.atlas2, 3);
|
let t = Assets.load(assets.ui.atlas2, 3);
|
||||||
if (t.status != 0) {
|
if (t.status != 0) {
|
||||||
Log.error("load failed");
|
Log.failure("load failed");
|
||||||
} else {
|
} else {
|
||||||
loading_handle = t.loading_handle;
|
loading_handle = t.loading_handle;
|
||||||
Log.info("state: loading");
|
Log.info("state: loading");
|
||||||
@ -38,12 +38,12 @@ fn frame() -> void
|
|||||||
if (s == 2) {
|
if (s == 2) {
|
||||||
let commit_status = Assets.commit(loading_handle);
|
let commit_status = Assets.commit(loading_handle);
|
||||||
if (commit_status != 0) {
|
if (commit_status != 0) {
|
||||||
Log.error("commit failed");
|
Log.failure("commit failed");
|
||||||
}
|
}
|
||||||
} else if (s == 3) {
|
} else if (s == 3) {
|
||||||
let sprite_status = Gfx.set_sprite(3, 10, 150, 150, 0, 0, true, false, false, 1);
|
let sprite_status = Gfx.set_sprite(3, 10, 150, 150, 0, 0, true, false, false, 1);
|
||||||
if (sprite_status != 0) {
|
if (sprite_status != 0) {
|
||||||
Log.error("set_sprite failed");
|
Log.failure("set_sprite failed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.info("state: waiting");
|
Log.info("state: waiting");
|
||||||
@ -100,7 +100,7 @@ fn frame() -> void
|
|||||||
}
|
}
|
||||||
else if (total == 50)
|
else if (total == 50)
|
||||||
{
|
{
|
||||||
Log.error("50 is the magic number!");
|
Log.failure("50 is the magic number!");
|
||||||
}
|
}
|
||||||
else if (total == 15)
|
else if (total == 15)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user