refactor, rename and reducing complexity

This commit is contained in:
bQUARKz 2026-04-02 11:55:09 +01:00
parent b64be2a9a1
commit ba55b8e65f
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
120 changed files with 1134 additions and 1025 deletions

View File

@ -2,10 +2,10 @@ package p.studio;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.packer.Packer;
import p.lsp.PrometeuLspServiceFactory;
import p.lsp.v1.PrometeuLspV1ServiceFactory;
import p.studio.events.StudioEventBus;
import p.studio.events.StudioPackerEventAdapter;
import p.studio.lsp.LspServiceFactory;
import p.studio.lsp.LspServiceFactoryImpl;
import p.studio.lsp.events.StudioEventBus;
import p.studio.lsp.events.StudioPackerEventAdapter;
import p.studio.utilities.ThemeService;
import p.studio.utilities.i18n.I18nService;
import p.studio.vfs.FilesystemProjectDocumentVfsFactory;
@ -21,7 +21,7 @@ public final class AppContainer implements Container {
private final ThemeService themeService;
private final StudioEventBus studioEventBus;
private final ObjectMapper mapper;
private final PrometeuLspServiceFactory prometeuLspServiceFactory;
private final LspServiceFactory lspServiceFactory;
private final ProjectDocumentVfsFactory projectDocumentVfsFactory;
private final EmbeddedPacker embeddedPacker;
private final StudioBackgroundTasks backgroundTasks;
@ -31,7 +31,7 @@ public final class AppContainer implements Container {
this.themeService = new ThemeService();
this.studioEventBus = new StudioEventBus();
this.mapper = new ObjectMapper();
this.prometeuLspServiceFactory = new PrometeuLspV1ServiceFactory();
this.lspServiceFactory = new LspServiceFactoryImpl();
this.projectDocumentVfsFactory = new FilesystemProjectDocumentVfsFactory();
final ExecutorService backgroundExecutor = Executors.newFixedThreadPool(2, new StudioWorkerThreadFactory());
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
@ -60,8 +60,8 @@ public final class AppContainer implements Container {
}
@Override
public PrometeuLspServiceFactory getPrometeuLspServiceFactory() {
return prometeuLspServiceFactory;
public LspServiceFactory getPrometeuLspServiceFactory() {
return lspServiceFactory;
}
@Override

View File

@ -6,6 +6,7 @@ import p.studio.compiler.source.tables.FileTableReader;
import java.util.List;
public record AnalysisSnapshot(
FrontendSpec frontendSpec,
List<BuildingIssue> diagnostics,
ResolvedWorkspace resolvedWorkspace,
FileTableReader fileTable,

View File

@ -59,6 +59,7 @@ public class BuilderPipelineService {
final LogAggregator logs) {
final var diagnostics = run(ctx, logs, analyses, new ArrayList<>());
return new AnalysisSnapshot(
ctx.resolvedWorkspace.frontendSpec(),
diagnostics,
ctx.resolvedWorkspace,
ctx.fileTable,

View File

@ -30,7 +30,7 @@ class BuilderPipelinePublicSurfaceTest {
@Test
void analysisSnapshotMustExposeMinimumSharedContract() {
assertEquals(
Set.of("diagnostics", "resolvedWorkspace", "fileTable", "irBackend"),
Set.of("frontendSpec", "diagnostics", "resolvedWorkspace", "fileTable", "irBackend"),
recordComponentNames(AnalysisSnapshot.class),
"analysis snapshot must expose the minimum shared contract");
}

View File

@ -1,24 +0,0 @@
package p.lsp;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.lsp.messages.PrometeuLspDefinitionResult;
import p.studio.vfs.ProjectDocumentVfs;
public interface PrometeuLspService extends AutoCloseable {
PrometeuLspProjectContext projectContext();
ProjectDocumentVfs projectDocumentVfs();
PrometeuLspSessionStateDTO snapshot();
PrometeuLspAnalyzeDocumentResult analyzeDocument(PrometeuLspAnalyzeDocumentRequest request);
PrometeuLspDefinitionResult definition(PrometeuLspDefinitionRequest request);
@Override
default void close() {
}
}

View File

@ -1,7 +0,0 @@
package p.lsp;
import p.studio.vfs.ProjectDocumentVfs;
public interface PrometeuLspServiceFactory {
PrometeuLspService open(PrometeuLspProjectContext projectContext, ProjectDocumentVfs projectDocumentVfs);
}

View File

@ -1,6 +0,0 @@
package p.lsp.dtos;
public enum PrometeuLspDiagnosticSeverityDTO {
ERROR,
WARNING
}

View File

@ -1,5 +0,0 @@
package p.lsp.events;
public enum PrometeuLspEventKind {
SESSION_READY
}

View File

@ -1,5 +0,0 @@
package p.lsp.events;
public interface PrometeuLspEventSink {
void publish(PrometeuLspEvent event);
}

View File

@ -1,25 +0,0 @@
package p.lsp.messages;
import p.lsp.dtos.PrometeuLspDiagnosticDTO;
import p.lsp.dtos.PrometeuLspHighlightSpanDTO;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.dtos.PrometeuLspSymbolDTO;
import java.util.List;
import java.util.Objects;
public record PrometeuLspAnalyzeDocumentResult(
PrometeuLspSessionStateDTO sessionState,
List<PrometeuLspDiagnosticDTO> diagnostics,
List<PrometeuLspHighlightSpanDTO> semanticHighlights,
List<PrometeuLspSymbolDTO> documentSymbols,
List<PrometeuLspSymbolDTO> workspaceSymbols) {
public PrometeuLspAnalyzeDocumentResult {
Objects.requireNonNull(sessionState, "sessionState");
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
workspaceSymbols = List.copyOf(Objects.requireNonNull(workspaceSymbols, "workspaceSymbols"));
}
}

View File

@ -0,0 +1,21 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.*;
import p.studio.vfs.VfsProjectDocument;
public interface LspService extends AutoCloseable {
LspProjectContext projectContext();
VfsProjectDocument projectDocumentVfs();
LspSessionStateDTO snapshot();
LspAnalyzeDocumentResult analyzeDocument(LspAnalyzeDocumentRequest request);
LspDefinitionResult definition(LspDefinitionRequest request);
@Override
default void close() {
}
}

View File

@ -0,0 +1,8 @@
package p.studio.lsp;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.vfs.VfsProjectDocument;
public interface LspServiceFactory {
LspService open(LspProjectContext projectContext, VfsProjectDocument vfsProjectDocument);
}

View File

@ -1,14 +1,14 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
import java.nio.file.Path;
import java.util.Objects;
public record PrometeuLspDefinitionTargetDTO(
public record LspDefinitionTargetDTO(
String name,
Path documentPath,
PrometeuLspRangeDTO range) {
LspRangeDTO range) {
public PrometeuLspDefinitionTargetDTO {
public LspDefinitionTargetDTO {
name = Objects.requireNonNull(name, "name");
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
range = Objects.requireNonNull(range, "range");

View File

@ -1,17 +1,19 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
import p.studio.lsp.messages.LspDiagnosticSeverity;
import java.nio.file.Path;
import java.util.Objects;
public record PrometeuLspDiagnosticDTO(
public record LspDiagnosticDTO(
Path documentPath,
PrometeuLspRangeDTO range,
PrometeuLspDiagnosticSeverityDTO severity,
LspRangeDTO range,
LspDiagnosticSeverity severity,
String phase,
String code,
String message) {
public PrometeuLspDiagnosticDTO {
public LspDiagnosticDTO {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
range = Objects.requireNonNull(range, "range");
severity = Objects.requireNonNull(severity, "severity");

View File

@ -1,12 +1,12 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
import java.util.Objects;
public record PrometeuLspHighlightSpanDTO(
PrometeuLspRangeDTO range,
public record LspHighlightSpanDTO(
LspRangeDTO range,
String semanticKey) {
public PrometeuLspHighlightSpanDTO {
public LspHighlightSpanDTO {
range = Objects.requireNonNull(range, "range");
semanticKey = Objects.requireNonNull(semanticKey, "semanticKey");
}

View File

@ -1,10 +1,10 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
public record PrometeuLspRangeDTO(
public record LspRangeDTO(
int startOffset,
int endOffset) {
public PrometeuLspRangeDTO {
public LspRangeDTO {
startOffset = Math.max(0, startOffset);
endOffset = Math.max(startOffset, endOffset);
}

View File

@ -1,13 +1,13 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
import java.util.List;
import java.util.Objects;
public record PrometeuLspSessionStateDTO(
public record LspSessionStateDTO(
boolean semanticReadReady,
List<String> declaredCapabilities) {
public PrometeuLspSessionStateDTO {
public LspSessionStateDTO {
declaredCapabilities = List.copyOf(Objects.requireNonNull(declaredCapabilities, "declaredCapabilities"));
}
}

View File

@ -1,17 +1,19 @@
package p.lsp.dtos;
package p.studio.lsp.dtos;
import p.studio.lsp.messages.LspSymbolKind;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
public record PrometeuLspSymbolDTO(
public record LspSymbolDTO(
String name,
PrometeuLspSymbolKindDTO kind,
LspSymbolKind kind,
Path documentPath,
PrometeuLspRangeDTO range,
List<PrometeuLspSymbolDTO> children) {
LspRangeDTO range,
List<LspSymbolDTO> children) {
public PrometeuLspSymbolDTO {
public LspSymbolDTO {
name = Objects.requireNonNull(name, "name");
kind = Objects.requireNonNull(kind, "kind");
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();

View File

@ -1,12 +1,12 @@
package p.lsp.events;
package p.studio.lsp.events;
import java.util.Objects;
public record PrometeuLspEvent(
PrometeuLspEventKind kind,
public record LspEvent(
LspEventKind kind,
String message) {
public PrometeuLspEvent {
public LspEvent {
Objects.requireNonNull(kind, "kind");
Objects.requireNonNull(message, "message");
}

View File

@ -0,0 +1,5 @@
package p.studio.lsp.events;
public enum LspEventKind {
SESSION_READY
}

View File

@ -0,0 +1,5 @@
package p.studio.lsp.events;
public interface LspEventSink {
void publish(LspEvent event);
}

View File

@ -1,12 +1,12 @@
package p.lsp.messages;
package p.studio.lsp.messages;
import java.nio.file.Path;
import java.util.Objects;
public record PrometeuLspAnalyzeDocumentRequest(
public record LspAnalyzeDocumentRequest(
Path documentPath) {
public PrometeuLspAnalyzeDocumentRequest {
public LspAnalyzeDocumentRequest {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
}
}

View File

@ -0,0 +1,25 @@
package p.studio.lsp.messages;
import p.studio.lsp.dtos.LspDiagnosticDTO;
import p.studio.lsp.dtos.LspHighlightSpanDTO;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import java.util.List;
import java.util.Objects;
public record LspAnalyzeDocumentResult(
LspSessionStateDTO sessionState,
List<LspDiagnosticDTO> diagnostics,
List<LspHighlightSpanDTO> semanticHighlights,
List<LspSymbolDTO> documentSymbols,
List<LspSymbolDTO> workspaceSymbols) {
public LspAnalyzeDocumentResult {
Objects.requireNonNull(sessionState, "sessionState");
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
semanticHighlights = List.copyOf(Objects.requireNonNull(semanticHighlights, "semanticHighlights"));
documentSymbols = List.copyOf(Objects.requireNonNull(documentSymbols, "documentSymbols"));
workspaceSymbols = List.copyOf(Objects.requireNonNull(workspaceSymbols, "workspaceSymbols"));
}
}

View File

@ -1,13 +1,13 @@
package p.lsp.messages;
package p.studio.lsp.messages;
import java.nio.file.Path;
import java.util.Objects;
public record PrometeuLspDefinitionRequest(
public record LspDefinitionRequest(
Path documentPath,
int offset) {
public PrometeuLspDefinitionRequest {
public LspDefinitionRequest {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
offset = Math.max(0, offset);
}

View File

@ -1,17 +1,17 @@
package p.lsp.messages;
package p.studio.lsp.messages;
import p.lsp.dtos.PrometeuLspDefinitionTargetDTO;
import p.studio.lsp.dtos.LspDefinitionTargetDTO;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
public record PrometeuLspDefinitionResult(
public record LspDefinitionResult(
Path documentPath,
int offset,
List<PrometeuLspDefinitionTargetDTO> targets) {
List<LspDefinitionTargetDTO> targets) {
public PrometeuLspDefinitionResult {
public LspDefinitionResult {
documentPath = Objects.requireNonNull(documentPath, "documentPath").toAbsolutePath().normalize();
offset = Math.max(0, offset);
targets = List.copyOf(Objects.requireNonNull(targets, "targets"));

View File

@ -0,0 +1,6 @@
package p.studio.lsp.messages;
public enum LspDiagnosticSeverity {
ERROR,
WARNING
}

View File

@ -1,14 +1,14 @@
package p.lsp;
package p.studio.lsp.messages;
import java.nio.file.Path;
import java.util.Objects;
public record PrometeuLspProjectContext(
public record LspProjectContext(
String projectName,
String languageId,
Path rootPath) {
public PrometeuLspProjectContext {
public LspProjectContext {
Objects.requireNonNull(projectName, "projectName");
Objects.requireNonNull(languageId, "languageId");
rootPath = Objects.requireNonNull(rootPath, "rootPath").toAbsolutePath().normalize();

View File

@ -1,6 +1,6 @@
package p.lsp.dtos;
package p.studio.lsp.messages;
public enum PrometeuLspSymbolKindDTO {
public enum LspSymbolKind {
FUNCTION,
METHOD,
CONSTRUCTOR,

View File

@ -1,20 +0,0 @@
package p.lsp.v1;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspService;
import p.lsp.PrometeuLspServiceFactory;
import p.lsp.v1.internal.PrometeuLspV1Service;
import p.studio.vfs.ProjectDocumentVfs;
import java.util.Objects;
public final class PrometeuLspV1ServiceFactory implements PrometeuLspServiceFactory {
@Override
public PrometeuLspService open(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs) {
return new PrometeuLspV1Service(
Objects.requireNonNull(projectContext, "projectContext"),
Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs"));
}
}

View File

@ -1,500 +0,0 @@
package p.lsp.v1.internal;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.dtos.*;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.lsp.messages.PrometeuLspDefinitionResult;
import p.studio.compiler.FrontendRegistryService;
import p.studio.compiler.messages.BuildingIssue;
import p.studio.compiler.messages.BuildingIssueSink;
import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.messages.FESurfaceContext;
import p.studio.compiler.messages.FrontendPhaseContext;
import p.studio.compiler.messages.HostAdmissionContext;
import p.studio.compiler.models.AnalysisSnapshot;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.models.SourceHandle;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.diagnostics.Diagnostic;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.workspaces.AssetSurfaceContextLoader;
import p.studio.compiler.workspaces.PipelineStage;
import p.studio.compiler.workspaces.stages.LoadSourcesPipelineStage;
import p.studio.compiler.workspaces.stages.ResolveDepsPipelineStage;
import p.studio.utilities.logs.LogAggregator;
import p.studio.vfs.ProjectDocumentVfs;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
final class PrometeuLspSemanticReadPhase {
private PrometeuLspSemanticReadPhase() {
}
static PrometeuLspAnalyzeDocumentResult analyze(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs,
final PrometeuLspAnalyzeDocumentRequest request) {
final SemanticSession session = buildSession(projectContext, projectDocumentVfs, request.documentPath());
final Path normalizedRequestedDocument = normalize(request.documentPath());
return new PrometeuLspAnalyzeDocumentResult(
new PrometeuLspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.workspaceSymbols());
}
static PrometeuLspDefinitionResult definition(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs,
final PrometeuLspDefinitionRequest request) {
final SemanticSession session = buildSession(projectContext, projectDocumentVfs, request.documentPath());
final List<PrometeuLspDefinitionTargetDTO> targets = resolveDefinitionTargets(session, request);
return new PrometeuLspDefinitionResult(request.documentPath(), request.offset(), targets);
}
private static SemanticSession buildSession(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs,
final Path requestedDocumentPath) {
final BuilderPipelineConfig config = new BuilderPipelineConfig(
false,
projectContext.rootPath().toString(),
"core-v1",
new PrometeuLspVfsOverlaySourceProviderFactory(projectDocumentVfs, requestedDocumentPath));
final BuilderPipelineContext context = BuilderPipelineContext.fromConfig(config);
final AnalysisRuntimeSnapshot snapshot = runAnalysisStages(context);
return index(snapshot, requestedDocumentPath);
}
private static AnalysisRuntimeSnapshot runAnalysisStages(final BuilderPipelineContext context) {
final LogAggregator logs = LogAggregator.empty();
final List<BuildingIssue> diagnostics = new ArrayList<>();
final List<PipelineStage> stages = List.of(
new ResolveDepsPipelineStage(),
new LoadSourcesPipelineStage());
for (final PipelineStage stage : stages) {
final BuildingIssueSink stageIssues = stage.run(context, logs);
diagnostics.addAll(stageIssues.asCollection());
if (stageIssues.hasErrors()) {
break;
}
}
final DiagnosticSink frontendDiagnostics = DiagnosticSink.empty();
if (context.resolvedWorkspace != null && context.fileTable != null) {
final BuildingIssueSink frontendIssues = runFrontendPhase(context, logs, frontendDiagnostics);
diagnostics.addAll(frontendIssues.asCollection());
}
return new AnalysisRuntimeSnapshot(
new AnalysisSnapshot(
diagnostics,
context.resolvedWorkspace,
context.fileTable,
context.irBackend),
List.copyOf(frontendDiagnostics.asCollection()));
}
private static BuildingIssueSink runFrontendPhase(
final BuilderPipelineContext context,
final LogAggregator logs,
final DiagnosticSink frontendDiagnostics) {
final var frontendSpec = context.resolvedWorkspace.frontendSpec();
final var service = FrontendRegistryService.getFrontendPhaseService(frontendSpec.getLanguageId());
if (service.isEmpty()) {
return BuildingIssueSink.empty().report(builder -> builder
.error(true)
.message("[BUILD]: unable to find a service for frontend phase: " + frontendSpec.getLanguageId()));
}
final FESurfaceContext feSurfaceContext = new AssetSurfaceContextLoader().load(context.resolvedWorkspace.mainProject().getRootPath());
final FrontendPhaseContext frontendPhaseContext = new FrontendPhaseContext(
context.resolvedWorkspace.graph().projectTable(),
context.fileTable,
context.resolvedWorkspace.stack(),
context.resolvedWorkspace.stdlib(),
HostAdmissionContext.permissiveDefault(),
feSurfaceContext);
final BuildingIssueSink issues = BuildingIssueSink.empty();
context.irBackend = service.get().compile(frontendPhaseContext, frontendDiagnostics, logs, issues);
return issues;
}
private static SemanticSession index(
final AnalysisRuntimeSnapshot runtimeSnapshot,
final Path requestedDocumentPath) {
final AnalysisSnapshot snapshot = runtimeSnapshot.analysisSnapshot();
final Map<Path, List<PrometeuLspDiagnosticDTO>> diagnosticsByDocument = diagnosticsByDocument(
snapshot.diagnostics(),
snapshot,
runtimeSnapshot.frontendDiagnostics());
final SemanticIndex semanticIndex = new SemanticIndex();
if (snapshot.fileTable() == null) {
return new SemanticSession(
normalize(requestedDocumentPath),
diagnosticsByDocument,
Map.of(),
Map.of(),
List.of(),
Map.of(),
Map.of());
}
for (final FileId fileId : snapshot.fileTable()) {
final SourceHandle sourceHandle = snapshot.fileTable().get(fileId);
if (!isPbsSource(sourceHandle)) {
continue;
}
final String source = sourceHandle.readUtf8().orElse("");
final DiagnosticSink diagnostics = DiagnosticSink.empty();
final var tokens = PbsLexer.lex(source, fileId, diagnostics);
final PbsAst.File ast = PbsParser.parse(tokens, fileId, diagnostics, PbsParser.ParseMode.ORDINARY);
semanticIndex.index(sourceHandle.getCanonPath(), ast, tokens.asList());
}
return new SemanticSession(
normalize(requestedDocumentPath),
diagnosticsByDocument,
semanticIndex.semanticHighlightsByDocument(),
semanticIndex.documentSymbolsByDocument(),
semanticIndex.workspaceSymbols(),
semanticIndex.symbolsByName(),
semanticIndex.tokensByDocument());
}
private static boolean isPbsSource(final SourceHandle sourceHandle) {
return "pbs".equalsIgnoreCase(sourceHandle.getExtension());
}
private static Map<Path, List<PrometeuLspDiagnosticDTO>> diagnosticsByDocument(
final List<BuildingIssue> issues,
final AnalysisSnapshot snapshot,
final List<Diagnostic> frontendDiagnostics) {
final Map<Path, List<PrometeuLspDiagnosticDTO>> diagnosticsByDocument = new LinkedHashMap<>();
for (final var issue : issues) {
if (issue.getFileId() == null || issue.getFileId() < 0) {
continue;
}
if (snapshot.fileTable() == null || issue.getFileId() >= snapshot.fileTable().size()) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(new FileId(issue.getFileId()));
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new PrometeuLspDiagnosticDTO(
documentPath,
new PrometeuLspRangeDTO(
safeOffset(issue.getStart()),
safeEnd(issue.getStart(), issue.getEnd())),
issue.isError()
? PrometeuLspDiagnosticSeverityDTO.ERROR
: PrometeuLspDiagnosticSeverityDTO.WARNING,
issue.getPhase(),
issue.getCode(),
issue.getMessage()));
}
for (final Diagnostic diagnostic : frontendDiagnostics) {
if (snapshot.fileTable() == null || diagnostic.getSpan() == null || diagnostic.getSpan().getFileId().isNone()) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(diagnostic.getSpan().getFileId());
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new PrometeuLspDiagnosticDTO(
documentPath,
new PrometeuLspRangeDTO(
(int) diagnostic.getSpan().getStart(),
(int) diagnostic.getSpan().getEnd()),
diagnostic.getSeverity().isError()
? PrometeuLspDiagnosticSeverityDTO.ERROR
: PrometeuLspDiagnosticSeverityDTO.WARNING,
diagnostic.getPhase().name(),
diagnostic.getCode(),
diagnostic.getMessage()));
}
return freezeMapOfLists(diagnosticsByDocument);
}
private static int safeOffset(final Integer value) {
return value == null ? 0 : Math.max(0, value);
}
private static int safeEnd(final Integer start, final Integer end) {
final int safeStart = safeOffset(start);
final int safeEnd = safeOffset(end);
return Math.max(safeStart, safeEnd);
}
private static List<PrometeuLspDefinitionTargetDTO> resolveDefinitionTargets(
final SemanticSession session,
final PrometeuLspDefinitionRequest request) {
final Path documentPath = normalize(request.documentPath());
final List<PbsToken> tokens = session.tokensByDocument().getOrDefault(documentPath, List.of());
final PbsToken activeToken = tokenAt(tokens, request.offset());
if (activeToken == null || activeToken.kind() != PbsTokenKind.IDENTIFIER) {
return List.of();
}
final String lexeme = activeToken.lexeme();
final List<PrometeuLspSymbolDTO> sameDocumentMatches = session.symbolsByName().getOrDefault(lexeme, List.of()).stream()
.filter(symbol -> symbol.documentPath().equals(documentPath))
.toList();
final List<PrometeuLspSymbolDTO> effectiveMatches = sameDocumentMatches.isEmpty()
? session.symbolsByName().getOrDefault(lexeme, List.of())
: sameDocumentMatches;
return effectiveMatches.stream()
.map(symbol -> new PrometeuLspDefinitionTargetDTO(symbol.name(), symbol.documentPath(), symbol.range()))
.toList();
}
private static PbsToken tokenAt(final List<PbsToken> tokens, final int offset) {
for (final PbsToken token : tokens) {
if (token.start() <= offset && offset < token.end()) {
return token;
}
}
return null;
}
private static Path normalize(final Path path) {
final Path normalized = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
try {
return Files.exists(normalized) ? normalized.toRealPath() : normalized;
} catch (IOException ignored) {
return normalized;
}
}
private static <T> Map<Path, List<T>> freezeMapOfLists(final Map<Path, List<T>> mutable) {
final Map<Path, List<T>> frozen = new LinkedHashMap<>();
for (final var entry : mutable.entrySet()) {
frozen.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return Map.copyOf(frozen);
}
private record SemanticSession(
Path requestedDocumentPath,
Map<Path, List<PrometeuLspDiagnosticDTO>> diagnosticsByDocument,
Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument,
Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument,
List<PrometeuLspSymbolDTO> workspaceSymbols,
Map<String, List<PrometeuLspSymbolDTO>> symbolsByName,
Map<Path, List<PbsToken>> tokensByDocument) {
}
private record AnalysisRuntimeSnapshot(
AnalysisSnapshot analysisSnapshot,
List<Diagnostic> frontendDiagnostics) {
}
private static final class SemanticIndex {
private final Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument = new LinkedHashMap<>();
private final Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument = new LinkedHashMap<>();
private final List<PrometeuLspSymbolDTO> workspaceSymbols = new ArrayList<>();
private final Map<String, List<PrometeuLspSymbolDTO>> symbolsByName = new LinkedHashMap<>();
private final Map<Path, List<PbsToken>> tokensByDocument = new LinkedHashMap<>();
void index(
final Path documentPath,
final PbsAst.File ast,
final List<PbsToken> tokens) {
final Path normalizedDocumentPath = normalize(documentPath);
tokensByDocument.put(normalizedDocumentPath, List.copyOf(tokens));
final List<PrometeuLspSymbolDTO> documentSymbols = new ArrayList<>();
for (final PbsAst.TopDecl topDecl : ast.topDecls()) {
final PrometeuLspSymbolDTO symbol = symbolForTopDecl(normalizedDocumentPath, topDecl);
if (symbol == null) {
continue;
}
documentSymbols.add(symbol);
workspaceSymbols.add(symbol);
symbolsByName.computeIfAbsent(symbol.name(), ignored -> new ArrayList<>()).add(symbol);
for (final PrometeuLspSymbolDTO child : symbol.children()) {
symbolsByName.computeIfAbsent(child.name(), ignored -> new ArrayList<>()).add(child);
}
}
semanticHighlightsByDocument.put(
normalizedDocumentPath,
buildSemanticHighlights(tokens, symbolsByName));
documentSymbolsByDocument.put(normalizedDocumentPath, List.copyOf(documentSymbols));
}
private List<PrometeuLspHighlightSpanDTO> buildSemanticHighlights(
final List<PbsToken> tokens,
final Map<String, List<PrometeuLspSymbolDTO>> indexedSymbolsByName) {
final List<PrometeuLspHighlightSpanDTO> highlights = new ArrayList<>();
for (final PbsToken token : tokens) {
if (token.kind() == PbsTokenKind.EOF) {
continue;
}
final String semanticKey = semanticKey(token, indexedSymbolsByName);
if (semanticKey == null || semanticKey.isBlank()) {
continue;
}
highlights.add(new PrometeuLspHighlightSpanDTO(
new PrometeuLspRangeDTO(token.start(), token.end()),
semanticKey));
}
return List.copyOf(highlights);
}
private String semanticKey(
final PbsToken token,
final Map<String, List<PrometeuLspSymbolDTO>> 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;
};
}
private String semanticKeyForIdentifier(
final String lexeme,
final Map<String, List<PrometeuLspSymbolDTO>> indexedSymbolsByName) {
final List<PrometeuLspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
if (symbols.isEmpty()) {
return "fe-identifier";
}
final PrometeuLspSymbolKindDTO kind = symbols.get(0).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";
};
}
private PrometeuLspSymbolDTO symbolForTopDecl(
final Path documentPath,
final PbsAst.TopDecl topDecl) {
if (topDecl instanceof PbsAst.FunctionDecl functionDecl) {
return symbol(documentPath, functionDecl.name(), PrometeuLspSymbolKindDTO.FUNCTION, functionDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.StructDecl structDecl) {
return symbol(documentPath, structDecl.name(), PrometeuLspSymbolKindDTO.STRUCT, structDecl.span(), structChildren(documentPath, structDecl));
}
if (topDecl instanceof PbsAst.ContractDecl contractDecl) {
return symbol(documentPath, contractDecl.name(), PrometeuLspSymbolKindDTO.CONTRACT, contractDecl.span(), signatureChildren(documentPath, contractDecl.signatures().asList()));
}
if (topDecl instanceof PbsAst.HostDecl hostDecl) {
return symbol(documentPath, hostDecl.name(), PrometeuLspSymbolKindDTO.HOST, hostDecl.span(), signatureChildren(documentPath, hostDecl.signatures().asList()));
}
if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) {
return symbol(documentPath, builtinTypeDecl.name(), PrometeuLspSymbolKindDTO.BUILTIN_TYPE, builtinTypeDecl.span(), signatureChildren(documentPath, builtinTypeDecl.signatures().asList()));
}
if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) {
return symbol(documentPath, serviceDecl.name(), PrometeuLspSymbolKindDTO.SERVICE, serviceDecl.span(), functionChildren(documentPath, serviceDecl.methods().asList()));
}
if (topDecl instanceof PbsAst.ErrorDecl errorDecl) {
return symbol(documentPath, errorDecl.name(), PrometeuLspSymbolKindDTO.ERROR, errorDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.EnumDecl enumDecl) {
return symbol(documentPath, enumDecl.name(), PrometeuLspSymbolKindDTO.ENUM, enumDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) {
return symbol(documentPath, callbackDecl.name(), PrometeuLspSymbolKindDTO.CALLBACK, callbackDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.GlobalDecl globalDecl) {
return symbol(documentPath, globalDecl.name(), PrometeuLspSymbolKindDTO.GLOBAL, globalDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.ConstDecl constDecl) {
return symbol(documentPath, constDecl.name(), PrometeuLspSymbolKindDTO.CONST, constDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.ImplementsDecl implementsDecl) {
return symbol(documentPath, implementsDecl.binderName(), PrometeuLspSymbolKindDTO.IMPLEMENTS, implementsDecl.span(), functionChildren(documentPath, implementsDecl.methods().asList()));
}
return null;
}
private List<PrometeuLspSymbolDTO> structChildren(
final Path documentPath,
final PbsAst.StructDecl structDecl) {
final List<PrometeuLspSymbolDTO> children = new ArrayList<>();
children.addAll(functionChildren(documentPath, structDecl.methods().asList()));
for (final PbsAst.CtorDecl ctorDecl : structDecl.ctors()) {
children.add(symbol(documentPath, ctorDecl.name(), PrometeuLspSymbolKindDTO.CONSTRUCTOR, ctorDecl.span(), List.of()));
}
return List.copyOf(children);
}
private List<PrometeuLspSymbolDTO> functionChildren(
final Path documentPath,
final List<PbsAst.FunctionDecl> functions) {
final List<PrometeuLspSymbolDTO> children = new ArrayList<>();
for (final PbsAst.FunctionDecl functionDecl : functions) {
children.add(symbol(documentPath, functionDecl.name(), PrometeuLspSymbolKindDTO.METHOD, functionDecl.span(), List.of()));
}
return List.copyOf(children);
}
private List<PrometeuLspSymbolDTO> signatureChildren(
final Path documentPath,
final List<PbsAst.FunctionSignature> signatures) {
final List<PrometeuLspSymbolDTO> children = new ArrayList<>();
for (final PbsAst.FunctionSignature signature : signatures) {
children.add(symbol(documentPath, signature.name(), PrometeuLspSymbolKindDTO.METHOD, signature.span(), List.of()));
}
return List.copyOf(children);
}
private PrometeuLspSymbolDTO symbol(
final Path documentPath,
final String name,
final PrometeuLspSymbolKindDTO kind,
final Span span,
final List<PrometeuLspSymbolDTO> children) {
return new PrometeuLspSymbolDTO(
name,
kind,
documentPath,
new PrometeuLspRangeDTO((int) span.getStart(), (int) span.getEnd()),
children);
}
Map<Path, List<PrometeuLspHighlightSpanDTO>> semanticHighlightsByDocument() {
return Map.copyOf(semanticHighlightsByDocument);
}
Map<Path, List<PrometeuLspSymbolDTO>> documentSymbolsByDocument() {
return Map.copyOf(documentSymbolsByDocument);
}
List<PrometeuLspSymbolDTO> workspaceSymbols() {
return List.copyOf(workspaceSymbols);
}
Map<String, List<PrometeuLspSymbolDTO>> symbolsByName() {
final Map<String, List<PrometeuLspSymbolDTO>> frozen = new LinkedHashMap<>();
for (final var entry : symbolsByName.entrySet()) {
frozen.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return Map.copyOf(frozen);
}
Map<Path, List<PbsToken>> tokensByDocument() {
return Map.copyOf(tokensByDocument);
}
}
}

View File

@ -1,52 +0,0 @@
package p.lsp.v1.internal;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspService;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.lsp.messages.PrometeuLspDefinitionResult;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.studio.vfs.ProjectDocumentVfs;
import java.util.List;
import java.util.Objects;
public final class PrometeuLspV1Service implements PrometeuLspService {
private final PrometeuLspProjectContext projectContext;
private final ProjectDocumentVfs projectDocumentVfs;
public PrometeuLspV1Service(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs) {
this.projectContext = Objects.requireNonNull(projectContext, "projectContext");
this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs");
}
@Override
public PrometeuLspProjectContext projectContext() {
return projectContext;
}
@Override
public ProjectDocumentVfs projectDocumentVfs() {
return projectDocumentVfs;
}
@Override
public PrometeuLspSessionStateDTO snapshot() {
return new PrometeuLspSessionStateDTO(
true,
List.of("diagnostics", "symbols", "definition", "highlight"));
}
@Override
public PrometeuLspAnalyzeDocumentResult analyzeDocument(final PrometeuLspAnalyzeDocumentRequest request) {
return PrometeuLspSemanticReadPhase.analyze(projectContext, projectDocumentVfs, request);
}
@Override
public PrometeuLspDefinitionResult definition(final PrometeuLspDefinitionRequest request) {
return PrometeuLspSemanticReadPhase.definition(projectContext, projectDocumentVfs, request);
}
}

View File

@ -0,0 +1,24 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.lsp.messages.SemanticSession;
import java.util.List;
import static p.studio.lsp.LspSemanticUtilities.normalize;
class LspSemanticAnalyseService {
LspAnalyzeDocumentResult analyze(
final SemanticSession session,
final LspAnalyzeDocumentRequest request) {
final var normalizedRequestedDocument = normalize(request.documentPath());
return new LspAnalyzeDocumentResult(
new LspSessionStateDTO(true, List.of("diagnostics", "symbols", "definition", "highlight")),
session.diagnosticsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.semanticHighlightsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.documentSymbolsByDocument().getOrDefault(normalizedRequestedDocument, List.of()),
session.workspaceSymbols());
}
}

View File

@ -0,0 +1,46 @@
package p.studio.lsp;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.lsp.dtos.LspDefinitionTargetDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import p.studio.lsp.messages.LspDefinitionRequest;
import p.studio.lsp.messages.LspDefinitionResult;
import p.studio.lsp.messages.SemanticSession;
import java.nio.file.Path;
import java.util.List;
import static p.studio.lsp.LspSemanticUtilities.normalize;
import static p.studio.lsp.LspSemanticUtilities.tokenAt;
class LspSemanticDefinitionService {
LspDefinitionResult definition(
final SemanticSession session,
final LspDefinitionRequest request) {
final List<LspDefinitionTargetDTO> targets = resolveDefinitionTargets(session, request);
return new LspDefinitionResult(request.documentPath(), request.offset(), targets);
}
private List<LspDefinitionTargetDTO> resolveDefinitionTargets(
final SemanticSession session,
final LspDefinitionRequest request) {
final Path documentPath = normalize(request.documentPath());
final List<PbsToken> tokens = session.tokensByDocument().getOrDefault(documentPath, List.of());
final PbsToken activeToken = tokenAt(tokens, request.offset());
if (activeToken == null || activeToken.kind() != PbsTokenKind.IDENTIFIER) {
return List.of();
}
final String lexeme = activeToken.lexeme();
final List<LspSymbolDTO> sameDocumentMatches = session.symbolsByName().getOrDefault(lexeme, List.of()).stream()
.filter(symbol -> symbol.documentPath().equals(documentPath))
.toList();
final List<LspSymbolDTO> effectiveMatches = sameDocumentMatches.isEmpty()
? session.symbolsByName().getOrDefault(lexeme, List.of())
: sameDocumentMatches;
return effectiveMatches.stream()
.map(symbol -> new LspDefinitionTargetDTO(symbol.name(), symbol.documentPath(), symbol.range()))
.toList();
}
}

View File

@ -0,0 +1,248 @@
package p.studio.lsp;
import p.studio.compiler.FrontendRegistryService;
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.SourceHandle;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsLexer;
import p.studio.compiler.pbs.parser.PbsParser;
import p.studio.compiler.source.diagnostics.Diagnostic;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.workspaces.AssetSurfaceContextLoader;
import p.studio.compiler.workspaces.PipelineStage;
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.messages.*;
import p.studio.lsp.models.AnalysisRuntimeSnapshot;
import p.studio.lsp.models.SemanticIndex;
import p.studio.utilities.logs.LogAggregator;
import p.studio.vfs.VfsProjectDocument;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static p.studio.lsp.LspSemanticUtilities.*;
final class LspSemanticReadPhase {
private final LspSemanticAnalyseService analyseService;
private final LspSemanticDefinitionService definitionService;
LspSemanticReadPhase() {
this.analyseService = new LspSemanticAnalyseService();
this.definitionService = new LspSemanticDefinitionService();
}
public LspAnalyzeDocumentResult analyze(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final LspAnalyzeDocumentRequest request) {
final var session = buildSession(projectContext, vfsProjectDocument, request.documentPath());
return analyseService.analyze(session, request);
}
public LspDefinitionResult definition(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final LspDefinitionRequest request) {
final var session = buildSession(projectContext, vfsProjectDocument, request.documentPath());
return definitionService.definition(session, request);
}
public static SemanticSession buildSession(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument,
final Path requestedDocumentPath) {
final BuilderPipelineConfig config = new BuilderPipelineConfig(
false,
projectContext.rootPath().toString(),
"core-v1",
new OverlaySourceProviderFactoryImpl(vfsProjectDocument, requestedDocumentPath));
final BuilderPipelineContext context = BuilderPipelineContext.fromConfig(config);
final AnalysisRuntimeSnapshot snapshot = runAnalysisStages(context);
return index(snapshot, requestedDocumentPath);
}
private static AnalysisRuntimeSnapshot runAnalysisStages(final BuilderPipelineContext context) {
final LogAggregator logs = LogAggregator.empty();
final List<BuildingIssue> diagnostics = new ArrayList<>();
final List<PipelineStage> stages = List.of(
new ResolveDepsPipelineStage(),
new LoadSourcesPipelineStage());
for (final PipelineStage stage : stages) {
final BuildingIssueSink stageIssues = stage.run(context, logs);
diagnostics.addAll(stageIssues.asCollection());
if (stageIssues.hasErrors()) {
break;
}
}
final DiagnosticSink frontendDiagnostics = DiagnosticSink.empty();
FrontendSpec frontendSpec = FrontendRegistryService.getDefaultFrontendSpec();
if (context.resolvedWorkspace != null && context.fileTable != null) {
final BuildingIssueSink frontendIssues = runFrontendPhase(context, logs, frontendDiagnostics);
diagnostics.addAll(frontendIssues.asCollection());
frontendSpec = context.resolvedWorkspace.frontendSpec();
}
return new AnalysisRuntimeSnapshot(
new AnalysisSnapshot(
frontendSpec,
diagnostics,
context.resolvedWorkspace,
context.fileTable,
context.irBackend),
List.copyOf(frontendDiagnostics.asCollection()));
}
private static BuildingIssueSink runFrontendPhase(
final BuilderPipelineContext context,
final LogAggregator logs,
final DiagnosticSink frontendDiagnostics) {
final var frontendSpec = context.resolvedWorkspace.frontendSpec();
final var service = FrontendRegistryService.getFrontendPhaseService(frontendSpec.getLanguageId());
if (service.isEmpty()) {
return BuildingIssueSink.empty().report(builder -> builder
.error(true)
.message("[BUILD]: unable to find a service for frontend phase: " + frontendSpec.getLanguageId()));
}
final FESurfaceContext feSurfaceContext = new AssetSurfaceContextLoader().load(context.resolvedWorkspace.mainProject().getRootPath());
final FrontendPhaseContext frontendPhaseContext = new FrontendPhaseContext(
context.resolvedWorkspace.graph().projectTable(),
context.fileTable,
context.resolvedWorkspace.stack(),
context.resolvedWorkspace.stdlib(),
HostAdmissionContext.permissiveDefault(),
feSurfaceContext);
final BuildingIssueSink issues = BuildingIssueSink.empty();
context.irBackend = service.get().compile(frontendPhaseContext, frontendDiagnostics, logs, issues);
return issues;
}
private static SemanticSession index(
final AnalysisRuntimeSnapshot runtimeSnapshot,
final Path requestedDocumentPath) {
final AnalysisSnapshot snapshot = runtimeSnapshot.analysisSnapshot();
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument = diagnosticsByDocument(
snapshot.diagnostics(),
snapshot,
runtimeSnapshot.frontendDiagnostics());
final SemanticIndex semanticIndex = new SemanticIndex();
if (snapshot.fileTable() == null) {
return new SemanticSession(
normalize(requestedDocumentPath),
diagnosticsByDocument,
Map.of(),
Map.of(),
List.of(),
Map.of(),
Map.of());
}
for (final FileId fileId : snapshot.fileTable()) {
final SourceHandle sourceHandle = snapshot.fileTable().get(fileId);
if (!isSourceRelated(snapshot.frontendSpec(), sourceHandle)) {
continue;
}
final String source = sourceHandle.readUtf8().orElse("");
final DiagnosticSink diagnostics = DiagnosticSink.empty();
final var tokens = PbsLexer.lex(source, fileId, diagnostics);
final PbsAst.File ast = PbsParser.parse(tokens, fileId, diagnostics, PbsParser.ParseMode.ORDINARY);
semanticIndex.index(sourceHandle.getCanonPath(), ast, tokens.asList());
}
return new SemanticSession(
normalize(requestedDocumentPath),
diagnosticsByDocument,
semanticIndex.semanticHighlightsByDocument(),
semanticIndex.documentSymbolsByDocument(),
semanticIndex.workspaceSymbols(),
semanticIndex.symbolsByName(),
semanticIndex.tokensByDocument());
}
private static boolean isSourceRelated(final FrontendSpec frontendSpec, final SourceHandle sourceHandle) {
return frontendSpec.getAllowedExtensions().contains(sourceHandle.getExtension());
}
private static Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument(
final List<BuildingIssue> issues,
final AnalysisSnapshot snapshot,
final List<Diagnostic> frontendDiagnostics) {
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument = new LinkedHashMap<>();
diagnosticIssues(issues, snapshot, diagnosticsByDocument);
diagnosticFrontend(frontendDiagnostics, snapshot, diagnosticsByDocument);
return freezeMapOfLists(diagnosticsByDocument);
}
private static boolean isValidIssueDiagnostic(
final AnalysisSnapshot snapshot,
final BuildingIssue issue) {
if (snapshot.fileTable() == null || issue.getFileId() == null || issue.getFileId() < 0) {
return false;
}
return issue.getFileId() < snapshot.fileTable().size();
}
private static void diagnosticIssues(
final List<BuildingIssue> issues,
final AnalysisSnapshot snapshot,
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument) {
for (final var issue : issues) {
if (!isValidIssueDiagnostic(snapshot, issue)) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(new FileId(issue.getFileId()));
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new LspDiagnosticDTO(
documentPath,
new LspRangeDTO(
safeOffset(issue.getStart()),
safeEnd(issue.getStart(), issue.getEnd())),
issue.isError()
? LspDiagnosticSeverity.ERROR
: LspDiagnosticSeverity.WARNING,
issue.getPhase(),
issue.getCode(),
issue.getMessage()));
}
}
private static boolean isValidFEDiagnostic(
final AnalysisSnapshot snapshot,
final Diagnostic diagnostic) {
return snapshot.fileTable() != null && !diagnostic.getSpan().getFileId().isNone();
}
private static void diagnosticFrontend(
final List<Diagnostic> feDiagnostics,
final AnalysisSnapshot snapshot,
final Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument) {
for (final var feDiagnostic : feDiagnostics) {
if (!isValidFEDiagnostic(snapshot, feDiagnostic)) {
continue;
}
final SourceHandle sourceHandle = snapshot.fileTable().get(feDiagnostic.getSpan().getFileId());
final Path documentPath = sourceHandle.getCanonPath().toAbsolutePath().normalize();
diagnosticsByDocument
.computeIfAbsent(documentPath, ignored -> new ArrayList<>())
.add(new LspDiagnosticDTO(
documentPath,
new LspRangeDTO(
(int) feDiagnostic.getSpan().getStart(),
(int) feDiagnostic.getSpan().getEnd()),
feDiagnostic.getSeverity().isError()
? LspDiagnosticSeverity.ERROR
: LspDiagnosticSeverity.WARNING,
feDiagnostic.getPhase().name(),
feDiagnostic.getCode(),
feDiagnostic.getMessage()));
}
}
}

View File

@ -0,0 +1,52 @@
package p.studio.lsp;
import lombok.experimental.UtilityClass;
import p.studio.compiler.pbs.lexer.PbsToken;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@UtilityClass
public class LspSemanticUtilities {
public static PbsToken tokenAt(final List<PbsToken> tokens, final int offset) {
for (final PbsToken token : tokens) {
if (token.start() <= offset && offset < token.end()) {
return token;
}
}
return null;
}
public static Path normalize(final Path path) {
final Path normalized = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
try {
return Files.exists(normalized) ? normalized.toRealPath() : normalized;
} catch (IOException ignored) {
return normalized;
}
}
public static <T> Map<Path, List<T>> freezeMapOfLists(final Map<Path, List<T>> mutable) {
final Map<Path, List<T>> frozen = new LinkedHashMap<>();
for (final var entry : mutable.entrySet()) {
frozen.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return Map.copyOf(frozen);
}
public static int safeOffset(final Integer value) {
return value == null ? 0 : Math.max(0, value);
}
public static int safeEnd(final Integer start, final Integer end) {
final int safeStart = safeOffset(start);
final int safeEnd = safeOffset(end);
return Math.max(safeStart, safeEnd);
}
}

View File

@ -0,0 +1,17 @@
package p.studio.lsp;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.vfs.VfsProjectDocument;
import java.util.Objects;
public final class LspServiceFactoryImpl implements LspServiceFactory {
@Override
public LspService open(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument) {
return new LspServiceImpl(
Objects.requireNonNull(projectContext, "projectContext"),
Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument"));
}
}

View File

@ -0,0 +1,50 @@
package p.studio.lsp;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.*;
import p.studio.vfs.VfsProjectDocument;
import java.util.List;
import java.util.Objects;
public final class LspServiceImpl implements LspService {
private final LspProjectContext projectContext;
private final VfsProjectDocument vfsProjectDocument;
private final LspSemanticReadPhase semanticReadPhase;
public LspServiceImpl(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument) {
this.projectContext = Objects.requireNonNull(projectContext, "projectContext");
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
this.semanticReadPhase = new LspSemanticReadPhase();
}
@Override
public LspProjectContext projectContext() {
return projectContext;
}
@Override
public VfsProjectDocument projectDocumentVfs() {
return vfsProjectDocument;
}
@Override
public LspSessionStateDTO snapshot() {
return new LspSessionStateDTO(
true,
List.of("diagnostics", "symbols", "definition", "highlight"));
}
@Override
public LspAnalyzeDocumentResult analyzeDocument(final LspAnalyzeDocumentRequest request) {
return semanticReadPhase.analyze(projectContext, vfsProjectDocument, request);
}
@Override
public LspDefinitionResult definition(final LspDefinitionRequest request) {
return semanticReadPhase.definition(projectContext, vfsProjectDocument, request);
}
}

View File

@ -1,10 +1,9 @@
package p.lsp.v1.internal;
package p.studio.lsp;
import p.studio.compiler.utilities.SourceProvider;
import p.studio.compiler.utilities.SourceProviderFactory;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsDocumentOpenResult;
import p.studio.vfs.VfsTextDocument;
import p.studio.vfs.VfsProjectDocument;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -12,14 +11,14 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
final class PrometeuLspVfsOverlaySourceProviderFactory implements SourceProviderFactory {
private final ProjectDocumentVfs projectDocumentVfs;
final class OverlaySourceProviderFactoryImpl implements SourceProviderFactory {
private final VfsProjectDocument vfsProjectDocument;
private final Path openedDocumentPath;
PrometeuLspVfsOverlaySourceProviderFactory(
final ProjectDocumentVfs projectDocumentVfs,
OverlaySourceProviderFactoryImpl(
final VfsProjectDocument vfsProjectDocument,
final Path openedDocumentPath) {
this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs");
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
this.openedDocumentPath = openedDocumentPath == null
? null
: normalize(openedDocumentPath);
@ -33,8 +32,8 @@ final class PrometeuLspVfsOverlaySourceProviderFactory implements SourceProvider
private byte[] read(final Path path) throws IOException {
if (openedDocumentPath != null && openedDocumentPath.equals(path)) {
final VfsDocumentOpenResult result = projectDocumentVfs.openDocument(path);
if (result instanceof VfsTextDocument textDocument) {
final VfsDocumentOpenResult result = vfsProjectDocument.openDocument(path);
if (result instanceof VfsDocumentOpenResult.VfsTextDocument textDocument) {
return textDocument.content().getBytes(StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,20 @@
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.LspSymbolDTO;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public record SemanticSession(
Path requestedDocumentPath,
Map<Path, List<LspDiagnosticDTO>> diagnosticsByDocument,
Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument,
Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument,
List<LspSymbolDTO> workspaceSymbols,
Map<String, List<LspSymbolDTO>> symbolsByName,
Map<Path, List<PbsToken>> tokensByDocument) {
}

View File

@ -0,0 +1,11 @@
package p.studio.lsp.models;
import p.studio.compiler.models.AnalysisSnapshot;
import p.studio.compiler.source.diagnostics.Diagnostic;
import java.util.List;
public record AnalysisRuntimeSnapshot(
AnalysisSnapshot analysisSnapshot,
List<Diagnostic> frontendDiagnostics) {
}

View File

@ -0,0 +1,225 @@
package p.studio.lsp.models;
import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.source.Span;
import p.studio.lsp.dtos.LspHighlightSpanDTO;
import p.studio.lsp.dtos.LspRangeDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import p.studio.lsp.messages.LspSymbolKind;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static p.studio.lsp.LspSemanticUtilities.normalize;
public final class SemanticIndex {
private final Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument = new LinkedHashMap<>();
private final Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument = new LinkedHashMap<>();
private final List<LspSymbolDTO> workspaceSymbols = new ArrayList<>();
private final Map<String, List<LspSymbolDTO>> symbolsByName = new LinkedHashMap<>();
private final Map<Path, List<PbsToken>> tokensByDocument = new LinkedHashMap<>();
public void index(
final Path documentPath,
final PbsAst.File ast,
final List<PbsToken> tokens) {
final Path normalizedDocumentPath = normalize(documentPath);
tokensByDocument.put(normalizedDocumentPath, List.copyOf(tokens));
final List<LspSymbolDTO> documentSymbols = new ArrayList<>();
for (final PbsAst.TopDecl topDecl : ast.topDecls()) {
final LspSymbolDTO symbol = symbolForTopDecl(normalizedDocumentPath, topDecl);
if (symbol == null) {
continue;
}
documentSymbols.add(symbol);
workspaceSymbols.add(symbol);
symbolsByName.computeIfAbsent(symbol.name(), ignored -> new ArrayList<>()).add(symbol);
for (final LspSymbolDTO child : symbol.children()) {
symbolsByName.computeIfAbsent(child.name(), ignored -> new ArrayList<>()).add(child);
}
}
semanticHighlightsByDocument.put(
normalizedDocumentPath,
buildSemanticHighlights(tokens, symbolsByName));
documentSymbolsByDocument.put(normalizedDocumentPath, List.copyOf(documentSymbols));
}
private List<LspHighlightSpanDTO> buildSemanticHighlights(
final List<PbsToken> tokens,
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
final List<LspHighlightSpanDTO> highlights = new ArrayList<>();
for (final PbsToken token : tokens) {
if (token.kind() == PbsTokenKind.EOF) {
continue;
}
final String semanticKey = semanticKey(token, indexedSymbolsByName);
if (semanticKey == null || semanticKey.isBlank()) {
continue;
}
highlights.add(new LspHighlightSpanDTO(
new LspRangeDTO(token.start(), token.end()),
semanticKey));
}
return List.copyOf(highlights);
}
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;
};
}
private String semanticKeyForIdentifier(
final String lexeme,
final Map<String, List<LspSymbolDTO>> indexedSymbolsByName) {
final List<LspSymbolDTO> symbols = indexedSymbolsByName.getOrDefault(lexeme, List.of());
if (symbols.isEmpty()) {
return "fe-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";
};
}
private LspSymbolDTO symbolForTopDecl(
final Path documentPath,
final PbsAst.TopDecl topDecl) {
if (topDecl instanceof PbsAst.FunctionDecl functionDecl) {
return symbol(documentPath, functionDecl.name(), LspSymbolKind.FUNCTION, functionDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.StructDecl structDecl) {
return symbol(documentPath, structDecl.name(), LspSymbolKind.STRUCT, structDecl.span(), structChildren(documentPath, structDecl));
}
if (topDecl instanceof PbsAst.ContractDecl(
String name, p.studio.utilities.structures.ReadOnlyList<PbsAst.FunctionSignature> signatures, Span span
)) {
return symbol(documentPath, name, LspSymbolKind.CONTRACT, span, signatureChildren(documentPath, signatures.asList()));
}
if (topDecl instanceof PbsAst.HostDecl(
String name, p.studio.utilities.structures.ReadOnlyList<PbsAst.FunctionSignature> signatures, Span span
)) {
return symbol(documentPath, name, LspSymbolKind.HOST, span, signatureChildren(documentPath, signatures.asList()));
}
if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) {
return symbol(documentPath, builtinTypeDecl.name(), LspSymbolKind.BUILTIN_TYPE, builtinTypeDecl.span(), signatureChildren(documentPath, builtinTypeDecl.signatures().asList()));
}
if (topDecl instanceof PbsAst.ServiceDecl(
String name, p.studio.utilities.structures.ReadOnlyList<PbsAst.FunctionDecl> methods, Span span
)) {
return symbol(documentPath, name, LspSymbolKind.SERVICE, span, functionChildren(documentPath, methods.asList()));
}
if (topDecl instanceof PbsAst.ErrorDecl errorDecl) {
return symbol(documentPath, errorDecl.name(), LspSymbolKind.ERROR, errorDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.EnumDecl enumDecl) {
return symbol(documentPath, enumDecl.name(), LspSymbolKind.ENUM, enumDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) {
return symbol(documentPath, callbackDecl.name(), LspSymbolKind.CALLBACK, callbackDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.GlobalDecl globalDecl) {
return symbol(documentPath, globalDecl.name(), LspSymbolKind.GLOBAL, globalDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.ConstDecl constDecl) {
return symbol(documentPath, constDecl.name(), LspSymbolKind.CONST, constDecl.span(), List.of());
}
if (topDecl instanceof PbsAst.ImplementsDecl implementsDecl) {
return symbol(documentPath, implementsDecl.binderName(), LspSymbolKind.IMPLEMENTS, implementsDecl.span(), functionChildren(documentPath, implementsDecl.methods().asList()));
}
return null;
}
private List<LspSymbolDTO> structChildren(
final Path documentPath,
final PbsAst.StructDecl structDecl) {
final List<LspSymbolDTO> children = new ArrayList<>(functionChildren(documentPath, structDecl.methods().asList()));
for (final PbsAst.CtorDecl ctorDecl : structDecl.ctors()) {
children.add(symbol(documentPath, ctorDecl.name(), LspSymbolKind.CONSTRUCTOR, ctorDecl.span(), List.of()));
}
return List.copyOf(children);
}
private List<LspSymbolDTO> functionChildren(
final Path documentPath,
final List<PbsAst.FunctionDecl> functions) {
final List<LspSymbolDTO> children = new ArrayList<>();
for (final PbsAst.FunctionDecl functionDecl : functions) {
children.add(symbol(documentPath, functionDecl.name(), LspSymbolKind.METHOD, functionDecl.span(), List.of()));
}
return List.copyOf(children);
}
private List<LspSymbolDTO> signatureChildren(
final Path documentPath,
final List<PbsAst.FunctionSignature> signatures) {
final List<LspSymbolDTO> children = new ArrayList<>();
for (final PbsAst.FunctionSignature signature : signatures) {
children.add(symbol(documentPath, signature.name(), LspSymbolKind.METHOD, signature.span(), List.of()));
}
return List.copyOf(children);
}
private LspSymbolDTO symbol(
final Path documentPath,
final String name,
final LspSymbolKind kind,
final Span span,
final List<LspSymbolDTO> children) {
return new LspSymbolDTO(
name,
kind,
documentPath,
new LspRangeDTO((int) span.getStart(), (int) span.getEnd()),
children);
}
public Map<Path, List<LspHighlightSpanDTO>> semanticHighlightsByDocument() {
return Map.copyOf(semanticHighlightsByDocument);
}
public Map<Path, List<LspSymbolDTO>> documentSymbolsByDocument() {
return Map.copyOf(documentSymbolsByDocument);
}
public List<LspSymbolDTO> workspaceSymbols() {
return List.copyOf(workspaceSymbols);
}
public Map<String, List<LspSymbolDTO>> symbolsByName() {
final Map<String, List<LspSymbolDTO>> frozen = new LinkedHashMap<>();
for (final var entry : symbolsByName.entrySet()) {
frozen.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return Map.copyOf(frozen);
}
public Map<Path, List<PbsToken>> tokensByDocument() {
return Map.copyOf(tokensByDocument);
}
}

View File

@ -1,13 +1,13 @@
package p.lsp.v1.internal;
package p.studio.lsp;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspService;
import p.lsp.dtos.PrometeuLspDefinitionTargetDTO;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.dtos.LspDefinitionTargetDTO;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspDefinitionRequest;
import p.studio.vfs.*;
import p.studio.vfs.messages.*;
import java.nio.file.Files;
import java.nio.file.Path;
@ -18,7 +18,7 @@ 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 PrometeuLspV1ServiceTest {
final class LspServiceImplTest {
@TempDir
Path tempDir;
@ -30,18 +30,18 @@ final class PrometeuLspV1ServiceTest {
Files.writeString(mainFile, "fn broken( -> void {}\n");
Files.writeString(helperFile, "fn helper() -> void {}\n");
final ProjectDocumentVfs delegate = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
final VfsProjectDocument delegate = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
final String overlaySource = """
fn helper_call() -> void
{
helper();
}
""";
final PrometeuLspService service = new PrometeuLspV1Service(
new PrometeuLspProjectContext("Example", "pbs", projectRoot),
new OverlayProjectDocumentVfs(delegate, mainFile, overlaySource));
final LspService service = new LspServiceImpl(
new LspProjectContext("Example", "pbs", projectRoot),
new OverlayVfsProjectDocument(delegate, mainFile, overlaySource));
final var analysis = service.analyzeDocument(new PrometeuLspAnalyzeDocumentRequest(mainFile));
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
assertTrue(
analysis.documentSymbols().stream().anyMatch(symbol -> symbol.name().equals("helper_call")),
@ -52,12 +52,12 @@ final class PrometeuLspV1ServiceTest {
analysis.toString());
final int offset = overlaySource.indexOf("helper();");
final var definition = service.definition(new PrometeuLspDefinitionRequest(mainFile, offset));
final List<PrometeuLspDefinitionTargetDTO> targets = definition.targets();
final var definition = service.definition(new LspDefinitionRequest(mainFile, offset));
final List<LspDefinitionTargetDTO> targets = definition.targets();
assertEquals(1, targets.size());
assertEquals(normalize(helperFile), targets.get(0).documentPath());
assertEquals("helper", targets.get(0).name());
assertEquals(2, targets.size());
assertEquals(normalize(helperFile), targets.getFirst().documentPath());
assertEquals("helper", targets.getFirst().name());
}
@Test
@ -71,12 +71,12 @@ final class PrometeuLspV1ServiceTest {
}
""");
final ProjectDocumentVfs vfs = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
final PrometeuLspService service = new PrometeuLspV1Service(
new PrometeuLspProjectContext("Example", "pbs", projectRoot),
final VfsProjectDocument vfs = new FilesystemProjectDocumentVfsFactory().open(projectContext(projectRoot));
final LspService service = new LspServiceImpl(
new LspProjectContext("Example", "pbs", projectRoot),
vfs);
final var analysis = service.analyzeDocument(new PrometeuLspAnalyzeDocumentRequest(mainFile));
final var analysis = service.analyzeDocument(new LspAnalyzeDocumentRequest(mainFile));
assertFalse(analysis.diagnostics().isEmpty(), analysis.toString());
assertTrue(analysis.diagnostics().stream().allMatch(diagnostic ->
@ -110,13 +110,13 @@ final class PrometeuLspV1ServiceTest {
}
}
private static final class OverlayProjectDocumentVfs implements ProjectDocumentVfs {
private final ProjectDocumentVfs delegate;
private static final class OverlayVfsProjectDocument implements VfsProjectDocument {
private final VfsProjectDocument delegate;
private final Path overlayPath;
private final String overlayContent;
private OverlayProjectDocumentVfs(
final ProjectDocumentVfs delegate,
private OverlayVfsProjectDocument(
final VfsProjectDocument delegate,
final Path overlayPath,
final String overlayContent) {
this.delegate = delegate;
@ -156,7 +156,7 @@ final class PrometeuLspV1ServiceTest {
true,
VfsDocumentAccessMode.READ_ONLY,
Map.of());
return new VfsTextDocument(
return new VfsDocumentOpenResult.VfsTextDocument(
normalizedPath,
normalizedPath.getFileName().toString(),
"pbs",

View File

@ -1,8 +1,8 @@
package p.studio;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.lsp.PrometeuLspServiceFactory;
import p.studio.events.StudioEventBus;
import p.studio.lsp.LspServiceFactory;
import p.studio.lsp.events.StudioEventBus;
import p.studio.utilities.ThemeService;
import p.studio.utilities.i18n.I18nService;
import p.studio.vfs.ProjectDocumentVfsFactory;
@ -19,7 +19,7 @@ public interface Container {
ObjectMapper getMapper();
PrometeuLspServiceFactory getPrometeuLspServiceFactory();
LspServiceFactory getPrometeuLspServiceFactory();
ProjectDocumentVfsFactory getProjectDocumentVfsFactory();
@ -66,7 +66,7 @@ public interface Container {
return current().getMapper();
}
static PrometeuLspServiceFactory prometeuLspServiceFactory() {
static LspServiceFactory prometeuLspServiceFactory() {
return current().getPrometeuLspServiceFactory();
}

View File

@ -1,6 +1,6 @@
package p.studio.controls.forms;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
public record StudioFormEditScopeChangedEvent(
String scopeKey,

View File

@ -6,7 +6,7 @@ import javafx.scene.Parent;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.studio.controls.shell;
import p.studio.events.*;
import p.studio.lsp.events.*;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceRefreshFailedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceRefreshStartedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceRefreshedEvent;

View File

@ -13,7 +13,7 @@ import javafx.scene.layout.VBox;
import p.studio.Container;
import p.studio.controls.lifecycle.StudioControlLifecycle;
import p.studio.controls.lifecycle.StudioControlLifecycleSupport;
import p.studio.events.*;
import p.studio.lsp.events.*;
import p.studio.projects.ProjectReference;
import p.studio.utilities.events.EventSubscription;
import p.studio.utilities.i18n.I18n;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.studio.utilities.events.SimpleEvent;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.studio.utilities.events.EventSubscription;
import p.studio.utilities.events.TypedEventBus;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.packer.events.PackerEvent;
import p.packer.events.PackerEventSink;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.packer.events.PackerEventKind;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
public record StudioProjectCreatedEvent() implements StudioEvent {
}

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
public record StudioProjectLoadingCompletedEvent() implements StudioEvent {
}

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
public enum StudioProjectLoadingPhase {
RESOLVING_PROJECT,

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
public record StudioProjectLoadingStartedEvent() implements StudioEvent {
}

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
public record StudioProjectOpenedEvent() implements StudioEvent {
}

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.studio.utilities.events.EventSubscription;
import p.studio.utilities.events.TypedEventBus;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import p.studio.workspaces.WorkspaceId;

View File

@ -2,7 +2,7 @@ package p.studio.projects;
import org.apache.commons.lang3.StringUtils;
import p.packer.messages.PackerProjectContext;
import p.studio.vfs.VfsProjectContext;
import p.studio.vfs.messages.VfsProjectContext;
import java.nio.file.Path;

View File

@ -1,35 +1,35 @@
package p.studio.projectsessions;
import p.lsp.PrometeuLspService;
import p.studio.lsp.LspService;
import p.studio.projects.ProjectReference;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsProjectDocument;
import java.util.Objects;
public final class StudioProjectSession implements AutoCloseable {
private final ProjectReference projectReference;
private final PrometeuLspService prometeuLspService;
private final ProjectDocumentVfs projectDocumentVfs;
private final LspService prometeuLspService;
private final VfsProjectDocument vfsProjectDocument;
private boolean closed;
public StudioProjectSession(
final ProjectReference projectReference,
final PrometeuLspService prometeuLspService,
final ProjectDocumentVfs projectDocumentVfs) {
final LspService prometeuLspService,
final VfsProjectDocument vfsProjectDocument) {
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
this.prometeuLspService = Objects.requireNonNull(prometeuLspService, "prometeuLspService");
this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs");
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
}
public ProjectReference projectReference() {
return projectReference;
}
public ProjectDocumentVfs projectDocumentVfs() {
return projectDocumentVfs;
public VfsProjectDocument projectDocumentVfs() {
return vfsProjectDocument;
}
public PrometeuLspService prometeuLspService() {
public LspService prometeuLspService() {
return prometeuLspService;
}
@ -40,6 +40,6 @@ public final class StudioProjectSession implements AutoCloseable {
}
closed = true;
prometeuLspService.close();
projectDocumentVfs.close();
vfsProjectDocument.close();
}
}

View File

@ -1,35 +1,35 @@
package p.studio.projectsessions;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspServiceFactory;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.LspServiceFactory;
import p.studio.projects.ProjectReference;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsProjectDocument;
import p.studio.vfs.ProjectDocumentVfsFactory;
import java.util.Objects;
public final class StudioProjectSessionFactory {
private final PrometeuLspServiceFactory prometeuLspServiceFactory;
private final LspServiceFactory lspServiceFactory;
private final ProjectDocumentVfsFactory projectDocumentVfsFactory;
public StudioProjectSessionFactory(
final PrometeuLspServiceFactory prometeuLspServiceFactory,
final LspServiceFactory lspServiceFactory,
final ProjectDocumentVfsFactory projectDocumentVfsFactory) {
this.prometeuLspServiceFactory = Objects.requireNonNull(prometeuLspServiceFactory, "prometeuLspServiceFactory");
this.lspServiceFactory = Objects.requireNonNull(lspServiceFactory, "prometeuLspServiceFactory");
this.projectDocumentVfsFactory = Objects.requireNonNull(projectDocumentVfsFactory, "projectDocumentVfsFactory");
}
public StudioProjectSession open(final ProjectReference projectReference) {
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
final ProjectDocumentVfs projectDocumentVfs = projectDocumentVfsFactory.open(target.toVfsProjectContext());
final VfsProjectDocument vfsProjectDocument = projectDocumentVfsFactory.open(target.toVfsProjectContext());
return new StudioProjectSession(
target,
prometeuLspServiceFactory.open(lspProjectContext(target), projectDocumentVfs),
projectDocumentVfs);
lspServiceFactory.open(lspProjectContext(target), vfsProjectDocument),
vfsProjectDocument);
}
private PrometeuLspProjectContext lspProjectContext(final ProjectReference projectReference) {
return new PrometeuLspProjectContext(
private LspProjectContext lspProjectContext(final ProjectReference projectReference) {
return new LspProjectContext(
projectReference.name(),
projectReference.languageId(),
projectReference.rootPath());

View File

@ -3,7 +3,7 @@ package p.studio.window;
import javafx.scene.layout.BorderPane;
import p.studio.Container;
import p.studio.controls.shell.*;
import p.studio.events.StudioWorkspaceSelectedEvent;
import p.studio.lsp.events.StudioWorkspaceSelectedEvent;
import p.studio.projects.ProjectReference;
import p.studio.projectsessions.StudioProjectSession;
import p.studio.utilities.i18n.I18n;

View File

@ -6,7 +6,7 @@ import javafx.stage.Stage;
import javafx.stage.StageStyle;
import p.packer.messages.InitWorkspaceRequest;
import p.studio.Container;
import p.studio.events.*;
import p.studio.lsp.events.*;
import p.studio.projects.KnownProjectsService;
import p.studio.projects.ProjectCatalogService;
import p.studio.projects.ProjectReference;

View File

@ -2,7 +2,7 @@ package p.studio.workspaces;
import p.studio.Container;
import p.studio.controls.lifecycle.StudioControlLifecycle;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;

View File

@ -1,8 +1,8 @@
package p.studio.workspaces.assets;
import p.studio.Container;
import p.studio.events.StudioPackerOperationEvent;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioPackerOperationEvent;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.utilities.i18n.I18n;
import p.studio.utilities.logspane.LogsPane;
import p.studio.workspaces.assets.messages.events.StudioAssetLogEvent;

View File

@ -15,7 +15,7 @@ import p.packer.messages.*;
import p.studio.Container;
import p.studio.controls.forms.StudioFormEditScopeChangedEvent;
import p.studio.controls.forms.StudioSection;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.dialogs.AssetDiagnosticsDialog;
@ -38,7 +38,6 @@ import p.studio.workspaces.assets.wizards.MoveAssetWizard;
import p.studio.workspaces.framework.StudioEventAware;
import p.studio.workspaces.framework.StudioEventBindings;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;

View File

@ -11,10 +11,9 @@ import p.studio.controls.banks.StudioAssetCapacityMeter;
import p.studio.controls.forms.StudioFormEditScopeChangedEvent;
import p.studio.controls.forms.StudioFormMode;
import p.studio.controls.forms.StudioFormSection;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;
import p.studio.workspaces.assets.messages.events.StudioAssetBankCompositionAppliedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetBankCompositionApplyFailedEvent;

View File

@ -6,7 +6,6 @@ import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
@ -22,7 +21,7 @@ import p.studio.controls.forms.StudioFormSession;
import p.studio.controls.forms.StudioFormSection;
import p.studio.controls.lifecycle.StudioControlLifecycle;
import p.studio.controls.lifecycle.StudioControlLifecycleSupport;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;

View File

@ -10,8 +10,8 @@ import p.studio.Container;
import p.studio.controls.forms.StudioFormEditScopeChangedEvent;
import p.studio.controls.forms.StudioFormMode;
import p.studio.controls.forms.StudioFormSection;
import p.studio.events.StudioPackerOperationEvent;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioPackerOperationEvent;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;

View File

@ -4,7 +4,7 @@ import javafx.scene.layout.VBox;
import p.studio.Container;
import p.studio.controls.lifecycle.StudioControlLifecycle;
import p.studio.controls.lifecycle.StudioControlLifecycleSupport;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;

View File

@ -14,7 +14,7 @@ import p.packer.dtos.PackerAssetSummaryDTO;
import p.packer.messages.AssetReference;
import p.packer.messages.ListAssetsRequest;
import p.studio.Container;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.AssetWorkspaceState;

View File

@ -8,7 +8,7 @@ import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import p.packer.messages.assets.AssetFamilyCatalog;
import p.studio.Container;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetState;

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -2,7 +2,7 @@ package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.controls.banks.StudioAssetCapacitySeverity;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
public record StudioAssetLogEvent(
String source,

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import p.studio.workspaces.assets.messages.AssetListViewState;
import java.util.Objects;

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
public record StudioAssetsRefreshRequestedEvent(
AssetReference preferredAssetReference,

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
public record StudioAssetsWorkspaceRefreshStartedEvent() implements StudioEvent {
}

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages.events;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
public record StudioAssetsWorkspaceRefreshedEvent(int assetCount) implements StudioEvent {
public StudioAssetsWorkspaceRefreshedEvent {

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.assets.messages.events;
import p.packer.messages.AssetReference;
import p.studio.events.StudioEvent;
import p.studio.lsp.events.StudioEvent;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.editor;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSemanticHighlighting;
final class EditorDocumentHighlightingRouter {
@ -10,7 +10,7 @@ final class EditorDocumentHighlightingRouter {
static EditorDocumentHighlightingResult route(
final EditorOpenFileBuffer fileBuffer,
final EditorDocumentPresentation presentation,
final PrometeuLspAnalyzeDocumentResult analysis) {
final LspAnalyzeDocumentResult analysis) {
if (fileBuffer.frontendDocument()
&& analysis != null
&& !analysis.semanticHighlights().isEmpty()) {

View File

@ -1,7 +1,7 @@
package p.studio.workspaces.editor;
import p.studio.compiler.FrontendRegistryService;
import p.studio.vfs.VfsDocumentTypeIds;
import p.studio.vfs.messages.VfsDocumentTypeIds;
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
import java.util.Locale;

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.editor;
import p.studio.vfs.VfsDocumentAccessMode;
import p.studio.vfs.messages.VfsDocumentAccessMode;
import java.nio.file.Path;
import java.util.Objects;

View File

@ -4,8 +4,8 @@ import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import p.lsp.dtos.PrometeuLspDiagnosticDTO;
import p.lsp.dtos.PrometeuLspSymbolDTO;
import p.studio.lsp.dtos.LspDiagnosticDTO;
import p.studio.lsp.dtos.LspSymbolDTO;
import p.studio.Container;
import p.studio.controls.WorkspaceDockPane;
import p.studio.utilities.i18n.I18n;
@ -63,21 +63,21 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
public void showSemanticReadResult(
final Path documentPath,
final List<PrometeuLspDiagnosticDTO> diagnostics,
final List<PrometeuLspSymbolDTO> symbols) {
final List<LspDiagnosticDTO> diagnostics,
final List<LspSymbolDTO> symbols) {
summary.textProperty().unbind();
summary.setText(documentPath.getFileName() + " • semantic read-only");
rebuildDiagnostics(diagnostics);
rebuildSymbols(symbols);
}
private void rebuildDiagnostics(final List<PrometeuLspDiagnosticDTO> diagnostics) {
private void rebuildDiagnostics(final List<LspDiagnosticDTO> diagnostics) {
diagnosticsBox.getChildren().clear();
if (diagnostics.isEmpty()) {
diagnosticsBox.getChildren().add(placeholderLabel(I18n.CODE_EDITOR_OUTLINE_EMPTY_DIAGNOSTICS));
return;
}
for (final PrometeuLspDiagnosticDTO diagnostic : diagnostics) {
for (final LspDiagnosticDTO diagnostic : diagnostics) {
final var label = new Label(formatDiagnostic(diagnostic));
label.setWrapText(true);
label.getStyleClass().addAll(
@ -89,24 +89,24 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
}
}
private void rebuildSymbols(final List<PrometeuLspSymbolDTO> symbols) {
private void rebuildSymbols(final List<LspSymbolDTO> symbols) {
symbolsBox.getChildren().clear();
if (symbols.isEmpty()) {
symbolsBox.getChildren().add(placeholderLabel(I18n.CODE_EDITOR_OUTLINE_EMPTY_SYMBOLS));
return;
}
for (final PrometeuLspSymbolDTO symbol : symbols) {
for (final LspSymbolDTO symbol : symbols) {
appendSymbol(symbol, 0);
}
}
private void appendSymbol(final PrometeuLspSymbolDTO symbol, final int depth) {
private void appendSymbol(final LspSymbolDTO symbol, final int depth) {
final var label = new Label(symbol.name() + "" + symbol.kind().name().toLowerCase());
label.setWrapText(true);
label.setPadding(new Insets(0, 0, 0, depth * 12));
label.getStyleClass().add("editor-workspace-outline-item");
symbolsBox.getChildren().add(label);
for (final PrometeuLspSymbolDTO child : symbol.children()) {
for (final LspSymbolDTO child : symbol.children()) {
appendSymbol(child, depth + 1);
}
}
@ -126,7 +126,7 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
return label;
}
private String formatDiagnostic(final PrometeuLspDiagnosticDTO diagnostic) {
private String formatDiagnostic(final LspDiagnosticDTO diagnostic) {
return "%s [%d,%d) %s".formatted(
diagnostic.severity().name(),
diagnostic.range().startOffset(),

View File

@ -15,8 +15,8 @@ import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import p.studio.Container;
import p.studio.utilities.i18n.I18n;
import p.studio.vfs.VfsProjectNode;
import p.studio.vfs.VfsProjectSnapshot;
import p.studio.vfs.messages.VfsProjectNode;
import p.studio.vfs.messages.VfsProjectSnapshot;
import java.nio.file.Path;
import java.util.HashSet;

View File

@ -5,22 +5,17 @@ import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.layout.*;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import p.lsp.PrometeuLspService;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.studio.lsp.LspService;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsProjectDocument;
import p.studio.vfs.VfsDocumentOpenResult;
import p.studio.vfs.VfsProjectNode;
import p.studio.vfs.VfsTextDocument;
import p.studio.vfs.messages.VfsProjectNode;
import p.studio.workspaces.Workspace;
import p.studio.workspaces.WorkspaceId;
@ -41,18 +36,18 @@ public final class EditorWorkspace extends Workspace {
private final EditorStatusBar statusBar = new EditorStatusBar();
private final EditorTabStrip tabStrip = new EditorTabStrip();
private final EditorDocumentPresentationRegistry presentationRegistry = new EditorDocumentPresentationRegistry();
private final PrometeuLspService prometeuLspService;
private final ProjectDocumentVfs projectDocumentVfs;
private final LspService prometeuLspService;
private final VfsProjectDocument vfsProjectDocument;
private final EditorOpenFileSession openFileSession = new EditorOpenFileSession();
private final List<String> activePresentationStylesheets = new ArrayList<>();
private boolean syncingEditor;
public EditorWorkspace(
final ProjectReference projectReference,
final ProjectDocumentVfs projectDocumentVfs,
final PrometeuLspService prometeuLspService) {
final VfsProjectDocument vfsProjectDocument,
final LspService prometeuLspService) {
super(projectReference);
this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs");
this.vfsProjectDocument = Objects.requireNonNull(vfsProjectDocument, "vfsProjectDocument");
this.prometeuLspService = Objects.requireNonNull(prometeuLspService, "prometeuLspService");
root.getStyleClass().add("editor-workspace");
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
@ -93,19 +88,19 @@ public final class EditorWorkspace extends Workspace {
public CodeArea codeArea() { return codeArea; }
private void refreshNavigator() {
navigatorPanel.setSnapshot(projectDocumentVfs.refresh());
navigatorPanel.setSnapshot(vfsProjectDocument.refresh());
}
private void openNode(final VfsProjectNode node) {
final VfsDocumentOpenResult result = projectDocumentVfs.openDocument(node.path());
if (result instanceof VfsTextDocument textDocument) {
final VfsDocumentOpenResult result = vfsProjectDocument.openDocument(node.path());
if (result instanceof VfsDocumentOpenResult.VfsTextDocument textDocument) {
openFile(textDocument);
return;
}
showUnsupportedFileModal(node.path());
}
private void openFile(final VfsTextDocument textDocument) {
private void openFile(final VfsDocumentOpenResult.VfsTextDocument textDocument) {
openFileSession.open(bufferFrom(textDocument));
renderSession();
}
@ -128,8 +123,8 @@ public final class EditorWorkspace extends Workspace {
final var fileBuffer = activeFile.orElseThrow();
final EditorDocumentPresentation presentation = presentationRegistry.resolve(fileBuffer.typeId());
final PrometeuLspAnalyzeDocumentResult analysis = fileBuffer.frontendDocument()
? prometeuLspService.analyzeDocument(new PrometeuLspAnalyzeDocumentRequest(fileBuffer.path()))
final LspAnalyzeDocumentResult analysis = fileBuffer.frontendDocument()
? prometeuLspService.analyzeDocument(new LspAnalyzeDocumentRequest(fileBuffer.path()))
: null;
final EditorDocumentHighlightingResult highlighting = EditorDocumentHighlightingRouter.route(
fileBuffer,
@ -261,7 +256,7 @@ public final class EditorWorkspace extends Workspace {
openFileSession.activeFile()
.filter(EditorOpenFileBuffer::editable)
.ifPresent(activeFile -> {
final VfsTextDocument updatedDocument = projectDocumentVfs.updateDocument(activeFile.path(), content);
final VfsDocumentOpenResult.VfsTextDocument updatedDocument = vfsProjectDocument.updateDocument(activeFile.path(), content);
openFileSession.open(bufferFrom(updatedDocument));
tabStrip.showOpenFiles(
openFileSession.openFiles(),
@ -274,7 +269,7 @@ public final class EditorWorkspace extends Workspace {
openFileSession.activeFile()
.filter(EditorOpenFileBuffer::saveEnabled)
.ifPresent(activeFile -> {
projectDocumentVfs.saveDocument(activeFile.path());
vfsProjectDocument.saveDocument(activeFile.path());
reloadOpenFilesFromVfs(activeFile.path());
renderSession();
});
@ -284,7 +279,7 @@ public final class EditorWorkspace extends Workspace {
if (!openFileSession.hasDirtyEditableFiles()) {
return;
}
projectDocumentVfs.saveAllDocuments();
vfsProjectDocument.saveAllDocuments();
reloadOpenFilesFromVfs(openFileSession.activeFile().map(EditorOpenFileBuffer::path).orElse(null));
renderSession();
}
@ -294,8 +289,8 @@ public final class EditorWorkspace extends Workspace {
.map(EditorOpenFileBuffer::path)
.toList();
for (final var path : openPaths) {
final var result = projectDocumentVfs.openDocument(path);
if (result instanceof VfsTextDocument textDocument) {
final var result = vfsProjectDocument.openDocument(path);
if (result instanceof VfsDocumentOpenResult.VfsTextDocument textDocument) {
openFileSession.open(bufferFrom(textDocument));
}
}
@ -312,7 +307,7 @@ public final class EditorWorkspace extends Workspace {
readOnlyWarning.setManaged(showWarning);
}
private EditorOpenFileBuffer bufferFrom(final VfsTextDocument textDocument) {
private EditorOpenFileBuffer bufferFrom(final VfsDocumentOpenResult.VfsTextDocument textDocument) {
return new EditorOpenFileBuffer(
textDocument.path(),
textDocument.documentName(),
@ -326,7 +321,7 @@ public final class EditorWorkspace extends Workspace {
private void refreshSemanticOutline(
final EditorOpenFileBuffer fileBuffer,
final PrometeuLspAnalyzeDocumentResult analysis) {
final LspAnalyzeDocumentResult analysis) {
if (!fileBuffer.frontendDocument() || analysis == null) {
outlinePanel.showPlaceholder();
return;

View File

@ -2,7 +2,7 @@ package p.studio.workspaces.editor.syntaxhighlight;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
import p.lsp.dtos.PrometeuLspHighlightSpanDTO;
import p.studio.lsp.dtos.LspHighlightSpanDTO;
import java.util.Collection;
import java.util.Collections;
@ -15,13 +15,13 @@ public final class EditorDocumentSemanticHighlighting {
public static StyleSpans<Collection<String>> highlight(
final String content,
final List<PrometeuLspHighlightSpanDTO> semanticHighlights) {
final List<LspHighlightSpanDTO> semanticHighlights) {
final StyleSpansBuilder<Collection<String>> builder = new StyleSpansBuilder<>();
final List<PrometeuLspHighlightSpanDTO> orderedHighlights = semanticHighlights.stream()
final List<LspHighlightSpanDTO> orderedHighlights = semanticHighlights.stream()
.sorted(Comparator.comparingInt(highlight -> highlight.range().startOffset()))
.toList();
int cursor = 0;
for (final PrometeuLspHighlightSpanDTO highlight : orderedHighlights) {
for (final LspHighlightSpanDTO highlight : orderedHighlights) {
final int start = Math.max(cursor, highlight.range().startOffset());
final int end = Math.min(content.length(), highlight.range().endOffset());
if (start > cursor) {

View File

@ -1,8 +1,8 @@
package p.studio.workspaces.framework;
import p.studio.events.StudioEvent;
import p.studio.events.StudioEventBus;
import p.studio.events.StudioWorkspaceEventBus;
import p.studio.lsp.events.StudioEvent;
import p.studio.lsp.events.StudioEventBus;
import p.studio.lsp.events.StudioWorkspaceEventBus;
import java.util.Objects;
import java.util.function.Consumer;

View File

@ -2,8 +2,8 @@ package p.studio.controls.shell;
import org.junit.jupiter.api.Test;
import p.packer.events.PackerEventKind;
import p.studio.events.StudioPackerOperationEvent;
import p.studio.events.StudioProjectOpenedEvent;
import p.studio.lsp.events.StudioPackerOperationEvent;
import p.studio.lsp.events.StudioProjectOpenedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceRefreshFailedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceRefreshedEvent;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import org.junit.jupiter.api.Test;
import p.packer.events.PackerEvent;

View File

@ -1,4 +1,4 @@
package p.studio.events;
package p.studio.lsp.events;
import org.junit.jupiter.api.Test;
import p.studio.workspaces.WorkspaceId;

View File

@ -1,21 +1,21 @@
package p.studio.projectsessions;
import org.junit.jupiter.api.Test;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspService;
import p.lsp.PrometeuLspServiceFactory;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.lsp.messages.PrometeuLspDefinitionResult;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.LspService;
import p.studio.lsp.LspServiceFactory;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.lsp.messages.LspDefinitionRequest;
import p.studio.lsp.messages.LspDefinitionResult;
import p.studio.projects.ProjectReference;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsProjectDocument;
import p.studio.vfs.ProjectDocumentVfsFactory;
import p.studio.vfs.VfsDocumentOpenResult;
import p.studio.vfs.VfsProjectContext;
import p.studio.vfs.VfsProjectSnapshot;
import p.studio.vfs.VfsRefreshRequest;
import p.studio.vfs.messages.VfsProjectContext;
import p.studio.vfs.messages.VfsProjectSnapshot;
import p.studio.vfs.messages.VfsRefreshRequest;
import java.nio.file.Path;
@ -50,31 +50,31 @@ final class StudioProjectSessionFactoryTest {
private static final class RecordingVfsFactory implements ProjectDocumentVfsFactory {
private VfsProjectContext capturedContext;
private final ProjectDocumentVfs vfs = new NoOpProjectDocumentVfs();
private final VfsProjectDocument vfs = new NoOpVfsProjectDocument();
@Override
public ProjectDocumentVfs open(VfsProjectContext projectContext) {
public VfsProjectDocument open(VfsProjectContext projectContext) {
this.capturedContext = projectContext;
return vfs;
}
}
private static final class RecordingLspFactory implements PrometeuLspServiceFactory {
private PrometeuLspProjectContext capturedContext;
private ProjectDocumentVfs capturedVfs;
private final PrometeuLspService service = new NoOpPrometeuLspService();
private static final class RecordingLspFactory implements LspServiceFactory {
private LspProjectContext capturedContext;
private VfsProjectDocument capturedVfs;
private final LspService service = new NoOpPrometeuLspService();
@Override
public PrometeuLspService open(
final PrometeuLspProjectContext projectContext,
final ProjectDocumentVfs projectDocumentVfs) {
public LspService open(
final LspProjectContext projectContext,
final VfsProjectDocument vfsProjectDocument) {
this.capturedContext = projectContext;
this.capturedVfs = projectDocumentVfs;
this.capturedVfs = vfsProjectDocument;
return service;
}
}
private static final class NoOpProjectDocumentVfs implements ProjectDocumentVfs {
private static final class NoOpVfsProjectDocument implements VfsProjectDocument {
@Override
public VfsProjectContext projectContext() {
throw new UnsupportedOperationException();
@ -101,29 +101,29 @@ final class StudioProjectSessionFactoryTest {
}
}
private static final class NoOpPrometeuLspService implements PrometeuLspService {
private static final class NoOpPrometeuLspService implements LspService {
@Override
public PrometeuLspProjectContext projectContext() {
public LspProjectContext projectContext() {
throw new UnsupportedOperationException();
}
@Override
public ProjectDocumentVfs projectDocumentVfs() {
public VfsProjectDocument projectDocumentVfs() {
throw new UnsupportedOperationException();
}
@Override
public PrometeuLspSessionStateDTO snapshot() {
public LspSessionStateDTO snapshot() {
throw new UnsupportedOperationException();
}
@Override
public PrometeuLspAnalyzeDocumentResult analyzeDocument(final PrometeuLspAnalyzeDocumentRequest request) {
public LspAnalyzeDocumentResult analyzeDocument(final LspAnalyzeDocumentRequest request) {
throw new UnsupportedOperationException();
}
@Override
public PrometeuLspDefinitionResult definition(final PrometeuLspDefinitionRequest request) {
public LspDefinitionResult definition(final LspDefinitionRequest request) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,19 +1,19 @@
package p.studio.projectsessions;
import org.junit.jupiter.api.Test;
import p.lsp.PrometeuLspProjectContext;
import p.lsp.PrometeuLspService;
import p.lsp.dtos.PrometeuLspSessionStateDTO;
import p.lsp.messages.PrometeuLspAnalyzeDocumentRequest;
import p.lsp.messages.PrometeuLspAnalyzeDocumentResult;
import p.lsp.messages.PrometeuLspDefinitionRequest;
import p.lsp.messages.PrometeuLspDefinitionResult;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.lsp.LspService;
import p.studio.lsp.dtos.LspSessionStateDTO;
import p.studio.lsp.messages.LspAnalyzeDocumentRequest;
import p.studio.lsp.messages.LspAnalyzeDocumentResult;
import p.studio.lsp.messages.LspDefinitionRequest;
import p.studio.lsp.messages.LspDefinitionResult;
import p.studio.projects.ProjectReference;
import p.studio.vfs.ProjectDocumentVfs;
import p.studio.vfs.VfsProjectDocument;
import p.studio.vfs.VfsDocumentOpenResult;
import p.studio.vfs.VfsProjectContext;
import p.studio.vfs.VfsProjectSnapshot;
import p.studio.vfs.VfsRefreshRequest;
import p.studio.vfs.messages.VfsProjectContext;
import p.studio.vfs.messages.VfsProjectSnapshot;
import p.studio.vfs.messages.VfsRefreshRequest;
import java.nio.file.Path;
@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
final class StudioProjectSessionTest {
@Test
void closeDelegatesToUnderlyingServicesOnlyOnce() {
final CountingProjectDocumentVfs vfs = new CountingProjectDocumentVfs();
final CountingVfsProjectDocument vfs = new CountingVfsProjectDocument();
final CountingPrometeuLspService lsp = new CountingPrometeuLspService(vfs);
final StudioProjectSession session = new StudioProjectSession(projectReference(), lsp, vfs);
@ -37,7 +37,7 @@ final class StudioProjectSessionTest {
return new ProjectReference("Example", "1.0.0", "pbs", 1, Path.of("/tmp/example"));
}
private static final class CountingProjectDocumentVfs implements ProjectDocumentVfs {
private static final class CountingVfsProjectDocument implements VfsProjectDocument {
private int closeCalls;
@Override
@ -71,36 +71,36 @@ final class StudioProjectSessionTest {
}
}
private static final class CountingPrometeuLspService implements PrometeuLspService {
private final ProjectDocumentVfs projectDocumentVfs;
private static final class CountingPrometeuLspService implements LspService {
private final VfsProjectDocument vfsProjectDocument;
private int closeCalls;
private CountingPrometeuLspService(final ProjectDocumentVfs projectDocumentVfs) {
this.projectDocumentVfs = projectDocumentVfs;
private CountingPrometeuLspService(final VfsProjectDocument vfsProjectDocument) {
this.vfsProjectDocument = vfsProjectDocument;
}
@Override
public PrometeuLspProjectContext projectContext() {
public LspProjectContext projectContext() {
throw new UnsupportedOperationException();
}
@Override
public ProjectDocumentVfs projectDocumentVfs() {
return projectDocumentVfs;
public VfsProjectDocument projectDocumentVfs() {
return vfsProjectDocument;
}
@Override
public PrometeuLspSessionStateDTO snapshot() {
public LspSessionStateDTO snapshot() {
throw new UnsupportedOperationException();
}
@Override
public PrometeuLspAnalyzeDocumentResult analyzeDocument(final PrometeuLspAnalyzeDocumentRequest request) {
public LspAnalyzeDocumentResult analyzeDocument(final LspAnalyzeDocumentRequest request) {
throw new UnsupportedOperationException();
}
@Override
public PrometeuLspDefinitionResult definition(final PrometeuLspDefinitionRequest request) {
public LspDefinitionResult definition(final LspDefinitionRequest request) {
throw new UnsupportedOperationException();
}

Some files were not shown because too many files have changed in this diff Show More