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;
|
||||
|
||||
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 java.util.List;
|
||||
@ -12,5 +14,8 @@ public class PBSDefinitions {
|
||||
.allowedExtensions(ReadOnlySet.from("pbs", "barrel"))
|
||||
.sourceRoots(ReadOnlySet.from("src"))
|
||||
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
|
||||
.semanticPresentation(new FrontendSemanticPresentationSpec(
|
||||
PbsSemanticKind.semanticKeys(),
|
||||
List.of("/themes/pbs/semantic-highlighting.css")))
|
||||
.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;
|
||||
@Builder.Default
|
||||
private final List<Stdlib> stdlibVersions = List.of();
|
||||
@Builder.Default
|
||||
private final FrontendSemanticPresentationSpec semanticPresentation = FrontendSemanticPresentationSpec.empty();
|
||||
|
||||
public String toString() {
|
||||
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.LspHighlightSpanDTO;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
import p.studio.lsp.dtos.LspSessionStateDTO;
|
||||
import p.studio.lsp.dtos.LspSymbolDTO;
|
||||
|
||||
@ -10,6 +11,7 @@ import java.util.Objects;
|
||||
|
||||
public record LspAnalyzeDocumentResult(
|
||||
LspSessionStateDTO sessionState,
|
||||
LspSemanticPresentationDTO semanticPresentation,
|
||||
List<LspDiagnosticDTO> diagnostics,
|
||||
List<LspHighlightSpanDTO> semanticHighlights,
|
||||
List<LspSymbolDTO> documentSymbols,
|
||||
@ -17,6 +19,7 @@ public record LspAnalyzeDocumentResult(
|
||||
|
||||
public LspAnalyzeDocumentResult {
|
||||
Objects.requireNonNull(sessionState, "sessionState");
|
||||
Objects.requireNonNull(semanticPresentation, "semanticPresentation");
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
|
||||
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
|
||||
|
||||
@ -16,6 +16,7 @@ class LspSemanticAnalyseService {
|
||||
final var normalizedRequestedDocument = normalize(request.documentPath());
|
||||
return new LspAnalyzeDocumentResult(
|
||||
new LspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
|
||||
session.semanticPresentation(),
|
||||
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
|
||||
session.semanticHighlightsByDocument().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.BuilderPipelineContext;
|
||||
import p.studio.compiler.models.FrontendSpec;
|
||||
import p.studio.compiler.models.FrontendSemanticPresentationSpec;
|
||||
import p.studio.compiler.models.SourceHandle;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
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.lsp.dtos.LspDiagnosticDTO;
|
||||
import p.studio.lsp.dtos.LspRangeDTO;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
import p.studio.lsp.messages.*;
|
||||
import p.studio.lsp.models.AnalysisRuntimeSnapshot;
|
||||
import p.studio.lsp.models.SemanticIndex;
|
||||
@ -137,6 +139,7 @@ final class LspSemanticReadPhase {
|
||||
if (snapshot.fileTable() == null) {
|
||||
return new SemanticSession(
|
||||
normalize(requestedDocumentPath),
|
||||
semanticPresentation(snapshot.frontendSpec()),
|
||||
diagnosticsByDocument,
|
||||
Map.of(),
|
||||
Map.of(),
|
||||
@ -157,6 +160,7 @@ final class LspSemanticReadPhase {
|
||||
}
|
||||
return new SemanticSession(
|
||||
normalize(requestedDocumentPath),
|
||||
semanticPresentation(snapshot.frontendSpec()),
|
||||
diagnosticsByDocument,
|
||||
semanticIndex.semanticHighlightsByDocument(),
|
||||
semanticIndex.documentSymbolsByDocument(),
|
||||
@ -169,6 +173,11 @@ final class LspSemanticReadPhase {
|
||||
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(
|
||||
final List<BuildingIssue> issues,
|
||||
final AnalysisSnapshot snapshot,
|
||||
|
||||
@ -3,6 +3,7 @@ package p.studio.lsp.messages;
|
||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
import p.studio.lsp.dtos.LspSymbolDTO;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@ -11,6 +12,7 @@ import java.util.Map;
|
||||
|
||||
public record SemanticSession(
|
||||
Path requestedDocumentPath,
|
||||
LspSemanticPresentationDTO semanticPresentation,
|
||||
Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument,
|
||||
Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument,
|
||||
Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package p.studio.lsp.models;
|
||||
|
||||
import p.studio.compiler.pbs.PbsSemanticKind;
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||
@ -71,39 +72,25 @@ public final class SemanticIndex {
|
||||
private String semanticKey(
|
||||
final PbsToken token,
|
||||
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
||||
return switch (token.kind()) {
|
||||
case COMMENT -> "fe-comment";
|
||||
case STRING_LITERAL -> "fe-string";
|
||||
case INT_LITERAL, FLOAT_LITERAL, BOUNDED_LITERAL -> "fe-number";
|
||||
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;
|
||||
};
|
||||
final PbsSemanticKind semanticKind = token.kind() == p.studio.compiler.pbs.lexer.PbsTokenKind.IDENTIFIER
|
||||
? semanticKindForIdentifier(token.lexeme(), indexedSymbolsByName)
|
||||
: PbsSemanticKind.forToken(token);
|
||||
return semanticKind == null ? null : semanticKind.semanticKey();
|
||||
}
|
||||
|
||||
private String semanticKeyForIdentifier(
|
||||
private PbsSemanticKind semanticKindForIdentifier(
|
||||
final String lexeme,
|
||||
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
|
||||
final List<LspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
|
||||
if (symbols.isEmpty()) {
|
||||
return "fe-identifier";
|
||||
return PbsSemanticKind.IDENTIFIER;
|
||||
}
|
||||
final LspSymbolKind kind = symbols.getFirst().kind();
|
||||
return switch (kind) {
|
||||
case FUNCTION, METHOD, CALLBACK -> "fe-callable";
|
||||
case STRUCT, CONTRACT, HOST, BUILTIN_TYPE, SERVICE, ERROR, ENUM -> "fe-type";
|
||||
case GLOBAL, CONST -> "fe-binding";
|
||||
default -> "fe-identifier";
|
||||
case FUNCTION, METHOD, CALLBACK -> PbsSemanticKind.FUNCTION;
|
||||
case STRUCT, CONTRACT, HOST, BUILTIN_TYPE, SERVICE, ERROR, ENUM -> PbsSemanticKind.TYPE;
|
||||
case GLOBAL, CONST -> PbsSemanticKind.BINDING;
|
||||
default -> PbsSemanticKind.IDENTIFIER;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,13 @@ final class LspServiceImplTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private static final String OVERLAY_SOURCE = """
|
||||
fn helper_call() -> void
|
||||
{
|
||||
helper();
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
void analyzeDocumentUsesVfsOverlayForRequestedDocumentAndFilesystemFallbackForClosedFiles() throws Exception {
|
||||
final Path projectRoot = createProject();
|
||||
@ -31,18 +38,17 @@ final class LspServiceImplTest {
|
||||
Files.writeString(helperFile, "fn helper() -> void {}\n");
|
||||
|
||||
final VfsProjectDocument delegate = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
|
||||
final String overlaySource = """
|
||||
fn helper_call() -> void
|
||||
{
|
||||
helper();
|
||||
}
|
||||
""";
|
||||
final LspService service = new LspServiceImpl(
|
||||
new LspProjectContext("Example", "pbs", projectRoot),
|
||||
new OverlayVfsProjectDocument(delegate, mainFile, overlaySource));
|
||||
new OverlayVfsProjectDocument(delegate, mainFile, OVERLAY_SOURCE));
|
||||
|
||||
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(
|
||||
analysis.documentSymbols().stream().anyMatch(symbol -> symbol.name().equals("helper_call")),
|
||||
analysis.toString());
|
||||
@ -51,7 +57,7 @@ final class LspServiceImplTest {
|
||||
symbol.name().equals("helper") && symbol.documentPath().equals(normalize(helperFile))),
|
||||
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 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 final VfsProjectDocument delegate;
|
||||
private final Path overlayPath;
|
||||
|
||||
@ -2,6 +2,7 @@ package p.studio.workspaces.editor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.dtos.LspHighlightSpanDTO;
|
||||
import p.studio.lsp.dtos.LspSemanticPresentationDTO;
|
||||
import p.studio.lsp.dtos.LspRangeDTO;
|
||||
import p.studio.lsp.dtos.LspSessionStateDTO;
|
||||
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
|
||||
@ -28,6 +29,7 @@ final class EditorDocumentHighlightingRouterTest {
|
||||
|
||||
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||
new LspSessionStateDTO(true, List.of("highlight")),
|
||||
new LspSemanticPresentationDTO(List.of(), List.of()),
|
||||
List.of(),
|
||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 2), "fe-keyword")),
|
||||
List.of(),
|
||||
@ -56,6 +58,7 @@ final class EditorDocumentHighlightingRouterTest {
|
||||
|
||||
final LspAnalyzeDocumentResult analysis = new LspAnalyzeDocumentResult(
|
||||
new LspSessionStateDTO(true, List.of("highlight")),
|
||||
new LspSemanticPresentationDTO(List.of(), List.of()),
|
||||
List.of(),
|
||||
List.of(new LspHighlightSpanDTO(new LspRangeDTO(0, 1), "fe-punctuation")),
|
||||
List.of(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user