implements PLN-0027 frontend semantic presentation contract and lsp descriptor
This commit is contained in:
parent
78758c1023
commit
de9782c16e
@ -1,6 +1,8 @@
|
|||||||
package p.studio.compiler;
|
package p.studio.compiler;
|
||||||
|
|
||||||
import p.studio.compiler.models.FrontendSpec;
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
|
||||||
|
import p.studio.compiler.pbs.PbsSemanticKind;
|
||||||
import p.studio.utilities.structures.ReadOnlySet;
|
import p.studio.utilities.structures.ReadOnlySet;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -12,5 +14,8 @@ public class PBSDefinitions {
|
|||||||
.allowedExtensions(ReadOnlySet.from("pbs", "barrel"))
|
.allowedExtensions(ReadOnlySet.from("pbs", "barrel"))
|
||||||
.sourceRoots(ReadOnlySet.from("src"))
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
|
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
|
||||||
|
.semanticPresentation(new FrontendSemanticPresentationSpec(
|
||||||
|
PbsSemanticKind.semanticKeys(),
|
||||||
|
List.of("/themes/pbs/semantic-highlighting.css")))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
package p.studio.compiler.pbs;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum PbsSemanticKind {
|
||||||
|
COMMENT("pbs-comment"),
|
||||||
|
STRING("pbs-string"),
|
||||||
|
NUMBER("pbs-number"),
|
||||||
|
LITERAL("pbs-literal"),
|
||||||
|
KEYWORD("pbs-keyword"),
|
||||||
|
OPERATOR("pbs-operator"),
|
||||||
|
PUNCTUATION("pbs-punctuation"),
|
||||||
|
FUNCTION("pbs-function"),
|
||||||
|
TYPE("pbs-type"),
|
||||||
|
BINDING("pbs-binding"),
|
||||||
|
IDENTIFIER("pbs-identifier");
|
||||||
|
|
||||||
|
private final String semanticKey;
|
||||||
|
|
||||||
|
PbsSemanticKind(final String semanticKey) {
|
||||||
|
this.semanticKey = semanticKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String semanticKey() {
|
||||||
|
return semanticKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> semanticKeys() {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.map(PbsSemanticKind::semanticKey)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PbsSemanticKind forToken(final PbsToken token) {
|
||||||
|
return switch (token.kind()) {
|
||||||
|
case COMMENT -> COMMENT;
|
||||||
|
case STRING_LITERAL -> STRING;
|
||||||
|
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> NUMBER;
|
||||||
|
case TRUE, FALSE, NONE -> LITERAL;
|
||||||
|
case 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 -> KEYWORD;
|
||||||
|
case PLUS, MINUS, STAR, SLASH, PERCENT, BANG, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL,
|
||||||
|
SLASH_EQUAL, PERCENT_EQUAL, BANG_EQUAL, EQUAL, EQUAL_EQUAL, LESS, LESS_EQUAL,
|
||||||
|
GREATER, GREATER_EQUAL, AND_AND, OR_OR, ARROW, QUESTION -> OPERATOR;
|
||||||
|
case LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, LEFT_BRACKET, RIGHT_BRACKET,
|
||||||
|
COMMA, COLON, SEMICOLON, AT, DOT, DOT_DOT -> PUNCTUATION;
|
||||||
|
case IDENTIFIER, EOF -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
.editor-workspace-code-area-type-pbs {
|
||||||
|
-fx-highlight-fill: #1b3244;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text {
|
||||||
|
-fx-fill: #edf4fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .lineno {
|
||||||
|
-fx-text-fill: #71859a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-keyword {
|
||||||
|
-fx-fill: #8dc7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-function {
|
||||||
|
-fx-fill: #f0cb79;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-type {
|
||||||
|
-fx-fill: #9ddba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-binding {
|
||||||
|
-fx-fill: #ffb1c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-string {
|
||||||
|
-fx-fill: #e2c48c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-number {
|
||||||
|
-fx-fill: #c4e58a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-comment {
|
||||||
|
-fx-fill: #6f8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-literal {
|
||||||
|
-fx-fill: #c8a2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-operator {
|
||||||
|
-fx-fill: #dbe6f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-punctuation {
|
||||||
|
-fx-fill: #adc1d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-pbs .text.editor-syntax-pbs-identifier {
|
||||||
|
-fx-fill: #edf4fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-status-chip-type-pbs {
|
||||||
|
-fx-background-color: #152432;
|
||||||
|
-fx-border-color: #4d8db9;
|
||||||
|
-fx-text-fill: #e8f5ff;
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package p.studio.compiler.pbs;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import p.studio.compiler.PBSDefinitions;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
final class PbsSemanticPresentationContractTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPublishSemanticPresentationContractThroughFrontendSpec() {
|
||||||
|
final var presentation = PBSDefinitions.PBS.getSemanticPresentation();
|
||||||
|
|
||||||
|
assertFalse(presentation.semanticKeys().isEmpty());
|
||||||
|
assertEquals(PbsSemanticKind.semanticKeys(), presentation.semanticKeys());
|
||||||
|
assertEquals(1, presentation.resources().size());
|
||||||
|
assertEquals("/themes/pbs/semantic-highlighting.css", presentation.resources().getFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPublishResolvableSemanticPresentationResources() {
|
||||||
|
final var resourcePath = PBSDefinitions.PBS.getSemanticPresentation().resources().getFirst();
|
||||||
|
final URL resource = PBSDefinitions.class.getResource(resourcePath);
|
||||||
|
|
||||||
|
assertNotNull(resource, resourcePath);
|
||||||
|
assertTrue(resource.toExternalForm().endsWith("themes/pbs/semantic-highlighting.css"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package p.studio.compiler.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record FrontendSemanticPresentationSpec(
|
||||||
|
List<String> semanticKeys,
|
||||||
|
List<String> resources) {
|
||||||
|
|
||||||
|
public FrontendSemanticPresentationSpec {
|
||||||
|
semanticKeys = List.copyOf(Objects.requireNonNull(semanticKeys, "semanticKeys"));
|
||||||
|
resources = List.copyOf(Objects.requireNonNull(resources, "resources"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FrontendSemanticPresentationSpec empty() {
|
||||||
|
return new FrontendSemanticPresentationSpec(List.of(), List.of());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,8 @@ public class FrontendSpec {
|
|||||||
private final boolean caseSensitive;
|
private final boolean caseSensitive;
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private final List<Stdlib> stdlibVersions = List.of();
|
private final List<Stdlib> stdlibVersions = List.of();
|
||||||
|
@Builder.Default
|
||||||
|
private final FrontendSemanticPresentationSpec semanticPresentation = FrontendSemanticPresentationSpec.empty();
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("FrontendSpec(language=%s)", languageId);
|
return String.format("FrontendSpec(language=%s)", languageId);
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package p.studio.lsp.dtos;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record LspSemanticPresentationDTO(
|
||||||
|
List<String> semanticKeys,
|
||||||
|
List<String> resources) {
|
||||||
|
|
||||||
|
public LspSemanticPresentationDTO {
|
||||||
|
semanticKeys = List.copyOf(Objects.requireNonNull(semanticKeys, "semanticKeys"));
|
||||||
|
resources = List.copyOf(Objects.requireNonNull(resources, "resources"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package p.studio.lsp.messages;
|
|||||||
|
|
||||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||||
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
||||||
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
import p.studio.lsp.dtos.LspSessionStateDTO;
|
import p.studio.lsp.dtos.LspSessionStateDTO;
|
||||||
import p.studio.lsp.dtos.LspSymbolDTO;
|
import p.studio.lsp.dtos.LspSymbolDTO;
|
||||||
|
|
||||||
@ -10,6 +11,7 @@ import java.util.Objects;
|
|||||||
|
|
||||||
public record LspAnalyzeDocumentResult(
|
public record LspAnalyzeDocumentResult(
|
||||||
LspSessionStateDTO sessionState,
|
LspSessionStateDTO sessionState,
|
||||||
|
LspSemanticPresentationDTO semanticPresentation,
|
||||||
List<LspDiagnosticDTO> diagnostics,
|
List<LspDiagnosticDTO> diagnostics,
|
||||||
List<LspHighlightSpanDTO> semanticHighlights,
|
List<LspHighlightSpanDTO> semanticHighlights,
|
||||||
List<LspSymbolDTO> documentSymbols,
|
List<LspSymbolDTO> documentSymbols,
|
||||||
@ -17,6 +19,7 @@ public record LspAnalyzeDocumentResult(
|
|||||||
|
|
||||||
public LspAnalyzeDocumentResult {
|
public LspAnalyzeDocumentResult {
|
||||||
Objects.requireNonNull(sessionState, "sessionState");
|
Objects.requireNonNull(sessionState, "sessionState");
|
||||||
|
Objects.requireNonNull(semanticPresentation, "semanticPresentation");
|
||||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||||
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
|
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
|
||||||
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
|
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class LspSemanticAnalyseService {
|
|||||||
final var normalizedRequestedDocument = normalize(request.documentPath());
|
final var normalizedRequestedDocument = normalize(request.documentPath());
|
||||||
return new LspAnalyzeDocumentResult(
|
return new LspAnalyzeDocumentResult(
|
||||||
new LspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
|
new LspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
|
||||||
|
session.semanticPresentation(),
|
||||||
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
||||||
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
||||||
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import p.studio.compiler.messages.*;
|
|||||||
import p.studio.compiler.models.AnalysisSnapshot;
|
import p.studio.compiler.models.AnalysisSnapshot;
|
||||||
import p.studio.compiler.models.BuilderPipelineContext;
|
import p.studio.compiler.models.BuilderPipelineContext;
|
||||||
import p.studio.compiler.models.FrontendSpec;
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
|
||||||
import p.studio.compiler.models.SourceHandle;
|
import p.studio.compiler.models.SourceHandle;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
import p.studio.compiler.pbs.lexer.PbsLexer;
|
import p.studio.compiler.pbs.lexer.PbsLexer;
|
||||||
@ -18,6 +19,7 @@ import p.studio.compiler.workspaces.stages.LoadSourcesPipelineStage;
|
|||||||
import p.studio.compiler.workspaces.stages.ResolveDepsPipelineStage;
|
import p.studio.compiler.workspaces.stages.ResolveDepsPipelineStage;
|
||||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||||
import p.studio.lsp.dtos.LspRangeDTO;
|
import p.studio.lsp.dtos.LspRangeDTO;
|
||||||
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
import p.studio.lsp.messages.*;
|
import p.studio.lsp.messages.*;
|
||||||
import p.studio.lsp.models.AnalysisRuntimeSnapshot;
|
import p.studio.lsp.models.AnalysisRuntimeSnapshot;
|
||||||
import p.studio.lsp.models.SemanticIndex;
|
import p.studio.lsp.models.SemanticIndex;
|
||||||
@ -137,6 +139,7 @@ final class LspSemanticReadPhase {
|
|||||||
if (snapshot.fileTable() == null) {
|
if (snapshot.fileTable() == null) {
|
||||||
return new SemanticSession(
|
return new SemanticSession(
|
||||||
normalize(requestedDocumentPath),
|
normalize(requestedDocumentPath),
|
||||||
|
semanticPresentation(snapshot.frontendSpec()),
|
||||||
diagnosticsByDocument,
|
diagnosticsByDocument,
|
||||||
Map.of(),
|
Map.of(),
|
||||||
Map.of(),
|
Map.of(),
|
||||||
@ -157,6 +160,7 @@ final class LspSemanticReadPhase {
|
|||||||
}
|
}
|
||||||
return new SemanticSession(
|
return new SemanticSession(
|
||||||
normalize(requestedDocumentPath),
|
normalize(requestedDocumentPath),
|
||||||
|
semanticPresentation(snapshot.frontendSpec()),
|
||||||
diagnosticsByDocument,
|
diagnosticsByDocument,
|
||||||
semanticIndex.semanticHighlightsByDocument(),
|
semanticIndex.semanticHighlightsByDocument(),
|
||||||
semanticIndex.documentSymbolsByDocument(),
|
semanticIndex.documentSymbolsByDocument(),
|
||||||
@ -169,6 +173,11 @@ final class LspSemanticReadPhase {
|
|||||||
return frontendSpec.getAllowedExtensions().contains(sourceHandle.getExtension());
|
return frontendSpec.getAllowedExtensions().contains(sourceHandle.getExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LspSemanticPresentationDTO semanticPresentation(final FrontendSpec frontendSpec) {
|
||||||
|
final FrontendSemanticPresentationSpec presentation = frontendSpec.getSemanticPresentation();
|
||||||
|
return new LspSemanticPresentationDTO(presentation.semanticKeys(), presentation.resources());
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument(
|
private static Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument(
|
||||||
final List<BuildingIssue> issues,
|
final List<BuildingIssue> issues,
|
||||||
final AnalysisSnapshot snapshot,
|
final AnalysisSnapshot snapshot,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package p.studio.lsp.messages;
|
|||||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||||
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
||||||
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
import p.studio.lsp.dtos.LspSymbolDTO;
|
import p.studio.lsp.dtos.LspSymbolDTO;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -11,6 +12,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
public record SemanticSession(
|
public record SemanticSession(
|
||||||
Path requestedDocumentPath,
|
Path requestedDocumentPath,
|
||||||
|
LspSemanticPresentationDTO semanticPresentation,
|
||||||
Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument,
|
Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument,
|
||||||
Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument,
|
Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument,
|
||||||
Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument,
|
Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package p.studio.lsp.models;
|
package p.studio.lsp.models;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.PbsSemanticKind;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||||
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||||
@ -71,39 +72,25 @@ public final class SemanticIndex {
|
|||||||
private String semanticKey(
|
private String semanticKey(
|
||||||
final PbsToken token,
|
final PbsToken token,
|
||||||
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
||||||
return switch (token.kind()) {
|
final PbsSemanticKind semanticKind = token.kind() == p.studio.compiler.pbs.lexer.PbsTokenKind.IDENTIFIER
|
||||||
case COMMENT -> "fe-comment";
|
? semanticKindForIdentifier(token.lexeme(), indexedSymbolsByName)
|
||||||
case STRING_LITERAL -> "fe-string";
|
: PbsSemanticKind.forToken(token);
|
||||||
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> "fe-number";
|
return semanticKind == null ? null : semanticKind.semanticKey();
|
||||||
case TRUE, FALSE, NONE -> "fe-literal";
|
|
||||||
case IDENTIFIER -> semanticKeyForIdentifier(token.lexeme(), indexedSymbolsByName);
|
|
||||||
case 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 -> "fe-keyword";
|
|
||||||
case PLUS, MINUS, STAR, SLASH, PERCENT, BANG, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL,
|
|
||||||
SLASH_EQUAL, PERCENT_EQUAL, BANG_EQUAL, EQUAL, EQUAL_EQUAL, LESS, LESS_EQUAL,
|
|
||||||
GREATER, GREATER_EQUAL, AND_AND, OR_OR, ARROW, QUESTION -> "fe-operator";
|
|
||||||
case LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, LEFT_BRACKET, RIGHT_BRACKET,
|
|
||||||
COMMA, COLON, SEMICOLON, AT, DOT, DOT_DOT -> "fe-punctuation";
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String semanticKeyForIdentifier(
|
private PbsSemanticKind semanticKindForIdentifier(
|
||||||
final String lexeme,
|
final String lexeme,
|
||||||
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
||||||
final List<LspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
|
final List<LspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
|
||||||
if (symbols.isEmpty()) {
|
if (symbols.isEmpty()) {
|
||||||
return "fe-identifier";
|
return PbsSemanticKind.IDENTIFIER;
|
||||||
}
|
}
|
||||||
final LspSymbolKind kind = symbols.getFirst().kind();
|
final LspSymbolKind kind = symbols.getFirst().kind();
|
||||||
return switch (kind) {
|
return switch (kind) {
|
||||||
case FUNCTION, METHOD, CALLBACK -> "fe-callable";
|
case FUNCTION, METHOD, CALLBACK -> PbsSemanticKind.FUNCTION;
|
||||||
case STRUCT, CONTRACT, HOST, BUILTIN_TYPE, SERVICE, ERROR, ENUM -> "fe-type";
|
case STRUCT, CONTRACT, HOST, BUILTIN_TYPE, SERVICE, ERROR, ENUM -> PbsSemanticKind.TYPE;
|
||||||
case GLOBAL, CONST -> "fe-binding";
|
case GLOBAL, CONST -> PbsSemanticKind.BINDING;
|
||||||
default -> "fe-identifier";
|
default -> PbsSemanticKind.IDENTIFIER;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,13 @@ final class LspServiceImplTest {
|
|||||||
@TempDir
|
@TempDir
|
||||||
Path tempDir;
|
Path tempDir;
|
||||||
|
|
||||||
|
private static final String OVERLAY_SOURCE = """
|
||||||
|
fn helper_call() -> void
|
||||||
|
{
|
||||||
|
helper();
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void analyzeDocumentUsesVfsOverlayForRequestedDocumentAndFilesystemFallbackForClosedFiles() throws Exception {
|
void analyzeDocumentUsesVfsOverlayForRequestedDocumentAndFilesystemFallbackForClosedFiles() throws Exception {
|
||||||
final Path projectRoot = createProject();
|
final Path projectRoot = createProject();
|
||||||
@ -31,18 +38,17 @@ final class LspServiceImplTest {
|
|||||||
Files.writeString(helperFile, "fn helper() -> void {}\n");
|
Files.writeString(helperFile, "fn helper() -> void {}\n");
|
||||||
|
|
||||||
final VfsProjectDocument delegate = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
|
final VfsProjectDocument delegate = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
|
||||||
final String overlaySource = """
|
|
||||||
fn helper_call() -> void
|
|
||||||
{
|
|
||||||
helper();
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
final LspService service = new LspServiceImpl(
|
final LspService service = new LspServiceImpl(
|
||||||
new LspProjectContext("Example", "pbs", projectRoot),
|
new LspProjectContext("Example", "pbs", projectRoot),
|
||||||
new OverlayVfsProjectDocument(delegate, mainFile, overlaySource));
|
new OverlayVfsProjectDocument(delegate, mainFile, OVERLAY_SOURCE));
|
||||||
|
|
||||||
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
|
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
|
||||||
|
|
||||||
|
assertEquals("pbs-function", semanticKeyForLexeme(analysis, OVERLAY_SOURCE, "helper_call"));
|
||||||
|
assertEquals("pbs-function", semanticKeyForLexeme(analysis, OVERLAY_SOURCE, "helper"));
|
||||||
|
assertEquals(List.of("/themes/pbs/semantic-highlighting.css"), analysis.semanticPresentation().resources());
|
||||||
|
assertTrue(analysis.semanticPresentation().semanticKeys().contains("pbs-function"));
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
analysis.documentSymbols().stream().anyMatch(symbol -> symbol.name().equals("helper_call")),
|
analysis.documentSymbols().stream().anyMatch(symbol -> symbol.name().equals("helper_call")),
|
||||||
analysis.toString());
|
analysis.toString());
|
||||||
@ -51,7 +57,7 @@ final class LspServiceImplTest {
|
|||||||
symbol.name().equals("helper") && symbol.documentPath().equals(normalize(helperFile))),
|
symbol.name().equals("helper") && symbol.documentPath().equals(normalize(helperFile))),
|
||||||
analysis.toString());
|
analysis.toString());
|
||||||
|
|
||||||
final int offset = overlaySource.indexOf("helper();");
|
final int offset = OVERLAY_SOURCE.indexOf("helper();");
|
||||||
final var definition = service.definition(new LspDefinitionRequest(mainFile, offset));
|
final var definition = service.definition(new LspDefinitionRequest(mainFile, offset));
|
||||||
final List<LspDefinitionTargetDTO> targets = definition.targets();
|
final List<LspDefinitionTargetDTO> targets = definition.targets();
|
||||||
|
|
||||||
@ -110,6 +116,27 @@ final class LspServiceImplTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String semanticKeyForLexeme(
|
||||||
|
final p.studio.lsp.messages.LspAnalyzeDocumentResult analysis,
|
||||||
|
final String source,
|
||||||
|
final String lexeme) {
|
||||||
|
return analysis.semanticHighlights().stream()
|
||||||
|
.filter(highlight -> lexeme.equals(spanContent(source, highlight.range().startOffset(), highlight.range().endOffset())))
|
||||||
|
.map(p.studio.lsp.dtos.LspHighlightSpanDTO::semanticKey)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String spanContent(
|
||||||
|
final String source,
|
||||||
|
final int start,
|
||||||
|
final int end) {
|
||||||
|
if (start < 0 || end > source.length() || start >= end) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return source.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
private static final class OverlayVfsProjectDocument implements VfsProjectDocument {
|
private static final class OverlayVfsProjectDocument implements VfsProjectDocument {
|
||||||
private final VfsProjectDocument delegate;
|
private final VfsProjectDocument delegate;
|
||||||
private final Path overlayPath;
|
private final Path overlayPath;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package p.studio.workspaces.editor;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
||||||
|
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||||
import p.studio.lsp.dtos.LspRangeDTO;
|
import p.studio.lsp.dtos.LspRangeDTO;
|
||||||
import p.studio.lsp.dtos.LspSessionStateDTO;
|
import p.studio.lsp.dtos.LspSessionStateDTO;
|
||||||
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
|
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
|
||||||
@ -28,6 +29,7 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
|
|
||||||
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||||
new LspSessionStateDTO(true, List.of("highlight")),
|
new LspSessionStateDTO(true, List.of("highlight")),
|
||||||
|
new LspSemanticPresentationDTO(List.of(), List.of()),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "fe-keyword")),
|
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "fe-keyword")),
|
||||||
List.of(),
|
List.of(),
|
||||||
@ -56,6 +58,7 @@ final class EditorDocumentHighlightingRouterTest {
|
|||||||
|
|
||||||
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||||
new LspSessionStateDTO(true, List.of("highlight")),
|
new LspSessionStateDTO(true, List.of("highlight")),
|
||||||
|
new LspSemanticPresentationDTO(List.of(), List.of()),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 1), "fe-punctuation")),
|
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 1), "fe-punctuation")),
|
||||||
List.of(),
|
List.of(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user