clean up and simplify
This commit is contained in:
parent
14d4751f9a
commit
41f2c804a0
@ -6,8 +6,6 @@ import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.events.StudioEventBus;
|
||||
import p.studio.lsp.events.StudioPackerEventAdapter;
|
||||
import p.studio.lsp.services.LspServerLifecycleImpl;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
import p.studio.lsp.services.host.TcpLspServerHostFactory;
|
||||
import p.studio.utilities.ThemeService;
|
||||
import p.studio.utilities.i18n.I18nService;
|
||||
|
||||
@ -34,9 +32,7 @@ public final class AppContainer implements Container {
|
||||
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
|
||||
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
|
||||
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
|
||||
this.lspServerLifecycle = new LspServerLifecycleImpl(
|
||||
new TcpLspServerHostFactory(),
|
||||
new CompilerLanguageServiceBridge());
|
||||
this.lspServerLifecycle = new LspServerLifecycleImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
package p.studio.lsp.api;
|
||||
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
|
||||
public interface LspServerLifecycle {
|
||||
LspServerHandle bootServer(LspServerBootRequest request);
|
||||
void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context);
|
||||
|
||||
void shutdownServer(LspProjectContext project);
|
||||
void shutdownServer(LspProjectContext context);
|
||||
}
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
package p.studio.lsp.messages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record LspServerBootRequest(
|
||||
LspProjectContext project,
|
||||
LspServerConfiguration configuration) {
|
||||
|
||||
public LspServerBootRequest {
|
||||
project = Objects.requireNonNull(project, "project");
|
||||
configuration = Objects.requireNonNull(configuration, "configuration");
|
||||
}
|
||||
|
||||
public static LspServerBootRequest defaults(final LspProjectContext project) {
|
||||
return new LspServerBootRequest(project, LspServerConfiguration.defaults());
|
||||
}
|
||||
}
|
||||
@ -1,29 +1,19 @@
|
||||
package p.studio.lsp.messages;
|
||||
|
||||
import java.util.Objects;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
public record LspServerConfiguration(
|
||||
String host,
|
||||
int port) {
|
||||
@Builder
|
||||
@Getter
|
||||
public class LspServerConfiguration {
|
||||
public static final String DEFAULT_HOST = "127.0.0.1";
|
||||
public static final int DEFAULT_PORT = 7777;
|
||||
|
||||
public LspServerConfiguration {
|
||||
host = requireHost(host);
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("port must be between 0 and 65535");
|
||||
}
|
||||
}
|
||||
@Builder.Default
|
||||
private final LspServerEndpoint endpoint = new LspServerEndpoint(DEFAULT_HOST, DEFAULT_PORT);
|
||||
|
||||
|
||||
public static LspServerConfiguration defaults() {
|
||||
return new LspServerConfiguration(DEFAULT_HOST, DEFAULT_PORT);
|
||||
}
|
||||
|
||||
private static String requireHost(final String value) {
|
||||
final String candidate = Objects.requireNonNull(value, "host").trim();
|
||||
if (candidate.isEmpty()) {
|
||||
throw new IllegalArgumentException("host must not be blank");
|
||||
}
|
||||
return candidate;
|
||||
return LspServerConfiguration.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
package p.studio.lsp.messages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record LspServerHandle(
|
||||
LspProjectContext project,
|
||||
LspServerEndpoint endpoint) {
|
||||
|
||||
public LspServerHandle {
|
||||
project = Objects.requireNonNull(project, "project");
|
||||
endpoint = Objects.requireNonNull(endpoint, "endpoint");
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package p.studio.lsp.api;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class LspApiBoundaryTest {
|
||||
|
||||
@Test
|
||||
void apiSourceMustRemainFreeOfLsp4jAndProtocolPackages() throws IOException {
|
||||
final Path sourceRoot = Path.of("src/main/java");
|
||||
final List<Path> javaFiles;
|
||||
try (var stream = Files.walk(sourceRoot)) {
|
||||
javaFiles = stream
|
||||
.filter(path -> path.toString().endsWith(".java"))
|
||||
.toList();
|
||||
}
|
||||
|
||||
for (final Path javaFile : javaFiles) {
|
||||
final String source = Files.readString(javaFile);
|
||||
assertTrue(!source.contains("org.eclipse.lsp4j"),
|
||||
() -> "LSP4J leaked into lsp-api: " + javaFile);
|
||||
assertTrue(!source.contains(".protocol."),
|
||||
() -> "Protocol package leaked into lsp-api: " + javaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package p.studio.lsp.api;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class LspServerBootRequestTest {
|
||||
|
||||
@Test
|
||||
void defaultsUseDeterministicLoopbackConfiguration() {
|
||||
final LspProjectContext project = new LspProjectContext("demo", "pbs", Path.of("."));
|
||||
|
||||
final LspServerBootRequest request = LspServerBootRequest.defaults(project);
|
||||
|
||||
assertEquals(project, request.project());
|
||||
assertEquals(LspServerConfiguration.DEFAULT_HOST, request.configuration().host());
|
||||
assertEquals(LspServerConfiguration.DEFAULT_PORT, request.configuration().port());
|
||||
}
|
||||
|
||||
@Test
|
||||
void configurationRejectsInvalidValues() {
|
||||
assertThrows(NullPointerException.class, () -> new LspServerConfiguration(null, 7777));
|
||||
assertThrows(IllegalArgumentException.class, () -> new LspServerConfiguration(" ", 7777));
|
||||
assertThrows(IllegalArgumentException.class, () -> new LspServerConfiguration("127.0.0.1", -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectContextNormalizesAndRejectsBlankFields() {
|
||||
final LspProjectContext context = new LspProjectContext(" demo ", " pbs ", Path.of("."));
|
||||
|
||||
assertEquals("demo", context.projectKey());
|
||||
assertEquals("pbs", context.languageId());
|
||||
assertEquals(Path.of(".").toAbsolutePath().normalize(), context.projectRoot());
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> new LspProjectContext(" ", "pbs", Path.of(".")));
|
||||
assertThrows(IllegalArgumentException.class, () -> new LspProjectContext("demo", " ", Path.of(".")));
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,11 @@ import p.studio.lsp.messages.BaselineServerDescription;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
|
||||
public interface LanguageServiceBridge {
|
||||
BaselineServerDescription describeServer(LspProjectContext project);
|
||||
BaselineServerDescription describeServer(LspProjectContext context);
|
||||
|
||||
BaselineDocumentAnalysis analyzeDocument(LspProjectContext project, String documentUri, String text);
|
||||
BaselineDocumentAnalysis analyzeDocument(LspProjectContext context, String documentUri, String text);
|
||||
|
||||
BaselineHover hover(LspProjectContext project, String documentUri, int line, int character);
|
||||
BaselineHover hover(LspProjectContext context, String documentUri, int line, int character);
|
||||
|
||||
String onSave(LspProjectContext project, String documentUri);
|
||||
String onSave(LspProjectContext context, String documentUri);
|
||||
}
|
||||
|
||||
@ -2,43 +2,36 @@ package p.studio.lsp.services;
|
||||
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
import p.studio.lsp.services.host.ProjectScopedLspServerHost;
|
||||
import p.studio.lsp.services.host.ProjectScopedLspServerHostFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public final class LspServerLifecycleImpl implements LspServerLifecycle {
|
||||
private final ProjectScopedLspServerHostFactory serverHostFactory;
|
||||
private final TcpLspServerHostFactory serverHostFactory;
|
||||
private final CompilerLanguageServiceBridge compilerBridge;
|
||||
private final ConcurrentMap<String, ProjectScopedLspServerHost> activeServers;
|
||||
private final ConcurrentMap<String, TcpLspServerHost> activeServers;
|
||||
|
||||
public LspServerLifecycleImpl(
|
||||
final ProjectScopedLspServerHostFactory serverHostFactory,
|
||||
final CompilerLanguageServiceBridge compilerBridge) {
|
||||
this.serverHostFactory = Objects.requireNonNull(serverHostFactory, "serverHostFactory");
|
||||
this.compilerBridge = Objects.requireNonNull(compilerBridge, "compilerBridge");
|
||||
public LspServerLifecycleImpl() {
|
||||
this.compilerBridge = new CompilerLanguageServiceBridge();
|
||||
this.serverHostFactory = new TcpLspServerHostFactory();
|
||||
this.activeServers = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||
final LspServerBootRequest safeRequest = Objects.requireNonNull(request, "request");
|
||||
final ProjectScopedLspServerHost host = serverHostFactory.create(safeRequest, compilerBridge);
|
||||
final ProjectScopedLspServerHost previous = activeServers.putIfAbsent(safeRequest.project().projectKey(), host);
|
||||
public void bootServer(final LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||
final var host = serverHostFactory.create(serverConfiguration, context, compilerBridge);
|
||||
final var previous = activeServers.putIfAbsent(context.projectKey(), host);
|
||||
if (previous != null) {
|
||||
throw new IllegalStateException("LSP server already booted for project " + safeRequest.project().projectKey());
|
||||
throw new IllegalStateException("LSP server already booted for project " + context.projectKey());
|
||||
}
|
||||
|
||||
try {
|
||||
final var endpoint = host.start();
|
||||
return new LspServerHandle(safeRequest.project(), endpoint);
|
||||
host.start();
|
||||
} catch (RuntimeException runtimeException) {
|
||||
activeServers.remove(safeRequest.project().projectKey(), host);
|
||||
activeServers.remove(context.projectKey(), host);
|
||||
closeQuietly(host);
|
||||
throw runtimeException;
|
||||
}
|
||||
@ -46,15 +39,15 @@ public final class LspServerLifecycleImpl implements LspServerLifecycle {
|
||||
|
||||
@Override
|
||||
public void shutdownServer(final LspProjectContext project) {
|
||||
final LspProjectContext safeProject = Objects.requireNonNull(project, "project");
|
||||
final ProjectScopedLspServerHost host = activeServers.remove(safeProject.projectKey());
|
||||
final var safeProject = Objects.requireNonNull(project, "project");
|
||||
final var host = activeServers.remove(safeProject.projectKey());
|
||||
if (host == null) {
|
||||
return;
|
||||
}
|
||||
closeQuietly(host);
|
||||
}
|
||||
|
||||
private static void closeQuietly(final ProjectScopedLspServerHost host) {
|
||||
private static void closeQuietly(final TcpLspServerHost host) {
|
||||
try {
|
||||
host.close();
|
||||
} catch (Exception exception) {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package p.studio.lsp.services.host;
|
||||
package p.studio.lsp.services;
|
||||
|
||||
import org.eclipse.lsp4j.jsonrpc.Launcher;
|
||||
import org.eclipse.lsp4j.launch.LSPLauncher;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerEndpoint;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
import p.studio.lsp.services.protocol.PrometeuLanguageServer;
|
||||
|
||||
@ -16,19 +16,21 @@ import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
||||
private final LspServerBootRequest request;
|
||||
public final class TcpLspServerHost {
|
||||
private final LspServerConfiguration serverConfiguration;
|
||||
private final LspProjectContext context;
|
||||
private final CompilerLanguageServiceBridge compilerBridge;
|
||||
private final ExecutorService clientExecutor;
|
||||
private volatile boolean closed;
|
||||
private volatile ServerSocket serverSocket;
|
||||
private volatile Thread acceptThread;
|
||||
private volatile LspServerEndpoint endpoint;
|
||||
|
||||
public TcpLspServerHost(
|
||||
final LspServerBootRequest request,
|
||||
final LspServerConfiguration serverConfiguration,
|
||||
final LspProjectContext context,
|
||||
final CompilerLanguageServiceBridge compilerBridge) {
|
||||
this.request = Objects.requireNonNull(request, "request");
|
||||
this.serverConfiguration = Objects.requireNonNull(serverConfiguration, "serverConfiguration");
|
||||
this.context = Objects.requireNonNull(context, "context");
|
||||
this.compilerBridge = Objects.requireNonNull(compilerBridge, "compilerBridge");
|
||||
this.clientExecutor = Executors.newCachedThreadPool(runnable -> {
|
||||
final Thread thread = new Thread(runnable);
|
||||
@ -38,20 +40,16 @@ public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized LspServerEndpoint start() {
|
||||
if (endpoint != null) {
|
||||
return endpoint;
|
||||
public synchronized void start() {
|
||||
if (serverSocket != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final InetAddress bindAddress = InetAddress.getByName(request.configuration().host());
|
||||
final ServerSocket socket = new ServerSocket(request.configuration().port(), 50, bindAddress);
|
||||
serverSocket = socket;
|
||||
endpoint = new LspServerEndpoint(bindAddress.getHostAddress(), socket.getLocalPort());
|
||||
final InetAddress bindAddress = InetAddress.getByName(serverConfiguration.getEndpoint().host());
|
||||
serverSocket = new ServerSocket(serverConfiguration.getEndpoint().port(), 50, bindAddress);
|
||||
acceptThread = new Thread(this::acceptLoop, "prometeu-lsp-v1-accept");
|
||||
acceptThread.setDaemon(true);
|
||||
acceptThread.start();
|
||||
return endpoint;
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("failed to start TCP LSP host", exception);
|
||||
}
|
||||
@ -72,7 +70,7 @@ public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
||||
|
||||
private void handleClient(final Socket socket) {
|
||||
try (socket) {
|
||||
final var server = new PrometeuLanguageServer(request.project(), compilerBridge);
|
||||
final var server = new PrometeuLanguageServer(context, compilerBridge);
|
||||
final Launcher<LanguageClient> launcher = LSPLauncher.createServerLauncher(
|
||||
server,
|
||||
socket.getInputStream(),
|
||||
@ -84,7 +82,6 @@ public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws Exception {
|
||||
if (closed) {
|
||||
return;
|
||||
@ -0,0 +1,14 @@
|
||||
package p.studio.lsp.services;
|
||||
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
|
||||
public final class TcpLspServerHostFactory {
|
||||
public TcpLspServerHost create(
|
||||
final LspServerConfiguration serverConfiguration,
|
||||
final LspProjectContext context,
|
||||
final CompilerLanguageServiceBridge compilerBridge) {
|
||||
return new TcpLspServerHost(serverConfiguration, context, compilerBridge);
|
||||
}
|
||||
}
|
||||
@ -4,10 +4,11 @@ import p.studio.compiler.workspaces.BuilderPipelineService;
|
||||
import p.studio.lsp.messages.*;
|
||||
import p.studio.lsp.services.LanguageServiceBridge;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class CompilerLanguageServiceBridge implements LanguageServiceBridge {
|
||||
private final BuilderPipelineService builderPipelineService;
|
||||
public record CompilerLanguageServiceBridge(
|
||||
BuilderPipelineService builderPipelineService) implements LanguageServiceBridge {
|
||||
|
||||
public CompilerLanguageServiceBridge() {
|
||||
this(BuilderPipelineService.INSTANCE);
|
||||
@ -17,13 +18,9 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
||||
this.builderPipelineService = Objects.requireNonNull(builderPipelineService, "builderPipelineService");
|
||||
}
|
||||
|
||||
public BuilderPipelineService builderPipelineService() {
|
||||
return builderPipelineService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaselineServerDescription describeServer(final LspProjectContext project) {
|
||||
Objects.requireNonNull(project, "project");
|
||||
public BaselineServerDescription describeServer(final LspProjectContext context) {
|
||||
Objects.requireNonNull(context, "context");
|
||||
return new BaselineServerDescription(
|
||||
"Prometeu Studio LSP",
|
||||
"0.1.0",
|
||||
@ -32,13 +29,13 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
||||
|
||||
@Override
|
||||
public BaselineDocumentAnalysis analyzeDocument(
|
||||
final LspProjectContext project,
|
||||
final LspProjectContext context,
|
||||
final String documentUri,
|
||||
final String text) {
|
||||
Objects.requireNonNull(project, "project");
|
||||
Objects.requireNonNull(context, "context");
|
||||
// Wave 1 keeps responses intentionally synthetic while hardening the bridge boundary.
|
||||
final String message = "Compiler-backed baseline via " + builderPipelineService.getClass().getSimpleName();
|
||||
return new BaselineDocumentAnalysis(java.util.List.of(new BaselineDocumentIssue(
|
||||
return new BaselineDocumentAnalysis(List.of(new BaselineDocumentIssue(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
@ -50,11 +47,11 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
||||
|
||||
@Override
|
||||
public BaselineHover hover(
|
||||
final LspProjectContext project,
|
||||
final LspProjectContext context,
|
||||
final String documentUri,
|
||||
final int line,
|
||||
final int character) {
|
||||
Objects.requireNonNull(project, "project");
|
||||
Objects.requireNonNull(context, "context");
|
||||
return new BaselineHover("""
|
||||
**Prometeu Studio LSP**
|
||||
|
||||
@ -66,9 +63,9 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
||||
|
||||
@Override
|
||||
public String onSave(
|
||||
final LspProjectContext project,
|
||||
final LspProjectContext context,
|
||||
final String documentUri) {
|
||||
Objects.requireNonNull(project, "project");
|
||||
Objects.requireNonNull(context, "context");
|
||||
return "Prometeu Studio saw save: " + documentUri;
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
package p.studio.lsp.services.host;
|
||||
|
||||
import p.studio.lsp.messages.LspServerEndpoint;
|
||||
|
||||
public interface ProjectScopedLspServerHost extends AutoCloseable {
|
||||
LspServerEndpoint start();
|
||||
|
||||
@Override
|
||||
void close() throws Exception;
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package p.studio.lsp.services.host;
|
||||
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
|
||||
public interface ProjectScopedLspServerHostFactory {
|
||||
ProjectScopedLspServerHost create(
|
||||
LspServerBootRequest request,
|
||||
CompilerLanguageServiceBridge compilerBridge);
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package p.studio.lsp.services.host;
|
||||
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
|
||||
public final class TcpLspServerHostFactory implements ProjectScopedLspServerHostFactory {
|
||||
@Override
|
||||
public ProjectScopedLspServerHost create(
|
||||
final LspServerBootRequest request,
|
||||
final CompilerLanguageServiceBridge compilerBridge) {
|
||||
return new TcpLspServerHost(request, compilerBridge);
|
||||
}
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
package p.studio.lsp.services;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerEndpoint;
|
||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||
import p.studio.lsp.services.host.ProjectScopedLspServerHost;
|
||||
import p.studio.lsp.services.host.ProjectScopedLspServerHostFactory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class LspServerLifecycleImplTest {
|
||||
|
||||
@Test
|
||||
void lifecycleBootsAndShutsDownProjectScopedHost() {
|
||||
final FakeHost host = new FakeHost();
|
||||
final ProjectScopedLspServerHostFactory factory = (request, compilerBridge) -> host;
|
||||
final var lifecycle = new LspServerLifecycleImpl(factory, new CompilerLanguageServiceBridge());
|
||||
final var project = new LspProjectContext("demo", "pbs", Path.of("."));
|
||||
|
||||
final var handle = lifecycle.bootServer(LspServerBootRequest.defaults(project));
|
||||
|
||||
assertEquals(project, handle.project());
|
||||
assertEquals("127.0.0.1", handle.endpoint().host());
|
||||
assertEquals(7777, handle.endpoint().port());
|
||||
assertTrue(host.started);
|
||||
|
||||
lifecycle.shutdownServer(project);
|
||||
assertTrue(host.closed);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lifecycleRejectsDoubleBootForSameProject() {
|
||||
final ProjectScopedLspServerHostFactory factory = (request, compilerBridge) -> new FakeHost();
|
||||
final var lifecycle = new LspServerLifecycleImpl(factory, new CompilerLanguageServiceBridge());
|
||||
final var request = LspServerBootRequest.defaults(new LspProjectContext("demo", "pbs", Path.of(".")));
|
||||
|
||||
lifecycle.bootServer(request);
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> lifecycle.bootServer(request));
|
||||
}
|
||||
|
||||
private static final class FakeHost implements ProjectScopedLspServerHost {
|
||||
private boolean started;
|
||||
private boolean closed;
|
||||
|
||||
@Override
|
||||
public LspServerEndpoint start() {
|
||||
started = true;
|
||||
return new LspServerEndpoint("127.0.0.1", 7777);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
package p.studio.lsp.services.boundary;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class LspV1BoundaryTest {
|
||||
|
||||
@Test
|
||||
void lsp4jMustRemainContainedInsideLspV1Sources() throws IOException {
|
||||
final List<Path> forbiddenRoots = List.of(
|
||||
Path.of("../prometeu-lsp-api/src/main/java"),
|
||||
Path.of("../../../prometeu-studio/src/main/java"),
|
||||
Path.of("../../../prometeu-app/src/main/java"),
|
||||
Path.of("../../../prometeu-compiler"),
|
||||
Path.of("../../../prometeu-packer"),
|
||||
Path.of("../../../prometeu-infra"));
|
||||
|
||||
for (final Path root : forbiddenRoots) {
|
||||
assertNoLsp4jImports(root.normalize());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNoLsp4jImports(final Path root) throws IOException {
|
||||
if (!Files.exists(root)) {
|
||||
return;
|
||||
}
|
||||
final List<Path> javaFiles;
|
||||
try (var stream = Files.walk(root)) {
|
||||
javaFiles = stream
|
||||
.filter(path -> path.toString().endsWith(".java"))
|
||||
.toList();
|
||||
}
|
||||
|
||||
for (final Path javaFile : javaFiles) {
|
||||
final String source = Files.readString(javaFile);
|
||||
assertFalse(source.contains("org.eclipse.lsp4j"), () -> "LSP4J leaked outside lsp-v1: " + javaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package p.studio.lsp.services.compiler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.BaselineDocumentAnalysis;
|
||||
import p.studio.lsp.messages.BaselineHover;
|
||||
import p.studio.lsp.messages.BaselineServerDescription;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CompilerLanguageServiceBridgeTest {
|
||||
|
||||
@Test
|
||||
void bridgeProvidesStableBaselineResponses() {
|
||||
final CompilerLanguageServiceBridge bridge = new CompilerLanguageServiceBridge();
|
||||
final LspProjectContext project = new LspProjectContext("demo", "pbs", Path.of("."));
|
||||
|
||||
final BaselineServerDescription server = bridge.describeServer(project);
|
||||
final BaselineDocumentAnalysis analysis = bridge.analyzeDocument(project, "file:///demo.pbs", "let x = 1");
|
||||
final BaselineHover hover = bridge.hover(project, "file:///demo.pbs", 0, 0);
|
||||
|
||||
assertEquals("Prometeu Studio LSP", server.name());
|
||||
assertTrue(server.hoverSupported());
|
||||
assertFalse(analysis.issues().isEmpty());
|
||||
assertTrue(analysis.issues().getFirst().message().contains("BuilderPipelineService"));
|
||||
assertTrue(hover.markdown().contains("Compiler-backed baseline"));
|
||||
}
|
||||
}
|
||||
@ -1,175 +0,0 @@
|
||||
package p.studio.lsp.services.protocol;
|
||||
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.BaselineDocumentAnalysis;
|
||||
import p.studio.lsp.messages.BaselineHover;
|
||||
import p.studio.lsp.messages.BaselineServerDescription;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.services.LanguageServiceBridge;
|
||||
import p.studio.lsp.services.protocol.mapping.ProtocolMessageMapper;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PrometeuLanguageServerTest {
|
||||
|
||||
@Test
|
||||
void initializeDelegatesThroughBridgeAndMapper() {
|
||||
final RecordingBridge bridge = new RecordingBridge();
|
||||
final RecordingMapper mapper = new RecordingMapper();
|
||||
final InitializeResult expected = new InitializeResult();
|
||||
mapper.initializeResult = expected;
|
||||
|
||||
final PrometeuLanguageServer server = new PrometeuLanguageServer(
|
||||
new LspProjectContext("demo", "pbs", Path.of(".")),
|
||||
bridge,
|
||||
mapper,
|
||||
new NoopTextDocumentService(),
|
||||
new PrometeuWorkspaceService());
|
||||
|
||||
final InitializeParams params = new InitializeParams();
|
||||
params.setRootUri(Path.of(".").toAbsolutePath().normalize().toUri().toString());
|
||||
|
||||
final InitializeResult result = server.initialize(params).join();
|
||||
|
||||
assertSame(expected, result);
|
||||
assertEquals(1, bridge.describeServerCalls);
|
||||
assertSame(bridge.serverDescription, mapper.lastServerDescription);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializeRejectsClientBoundToDifferentProjectRoot() {
|
||||
final RecordingBridge bridge = new RecordingBridge();
|
||||
final RecordingMapper mapper = new RecordingMapper();
|
||||
final PrometeuLanguageServer server = new PrometeuLanguageServer(
|
||||
new LspProjectContext("demo", "pbs", Path.of("/tmp/studio-project")),
|
||||
bridge,
|
||||
mapper,
|
||||
new NoopTextDocumentService(),
|
||||
new PrometeuWorkspaceService());
|
||||
|
||||
final InitializeParams params = new InitializeParams();
|
||||
params.setRootUri(Path.of("/tmp/another-project").toUri().toString());
|
||||
|
||||
final CompletionException failure = org.junit.jupiter.api.Assertions.assertThrows(
|
||||
CompletionException.class,
|
||||
() -> server.initialize(params).join());
|
||||
|
||||
final ResponseErrorException responseError = assertInstanceOf(ResponseErrorException.class, failure.getCause());
|
||||
assertTrue(responseError.getMessage().contains("/tmp/studio-project"));
|
||||
assertEquals(0, bridge.describeServerCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializeAcceptsMatchingWorkspaceFolder() {
|
||||
final RecordingBridge bridge = new RecordingBridge();
|
||||
final RecordingMapper mapper = new RecordingMapper();
|
||||
mapper.initializeResult = new InitializeResult();
|
||||
final Path projectRoot = Path.of("/tmp/studio-project").toAbsolutePath().normalize();
|
||||
final PrometeuLanguageServer server = new PrometeuLanguageServer(
|
||||
new LspProjectContext("demo", "pbs", projectRoot),
|
||||
bridge,
|
||||
mapper,
|
||||
new NoopTextDocumentService(),
|
||||
new PrometeuWorkspaceService());
|
||||
|
||||
final InitializeParams params = new InitializeParams();
|
||||
params.setWorkspaceFolders(List.of(new WorkspaceFolder(projectRoot.toUri().toString(), "studio-project")));
|
||||
|
||||
server.initialize(params).join();
|
||||
|
||||
assertEquals(1, bridge.describeServerCalls);
|
||||
}
|
||||
|
||||
private static final class RecordingBridge implements LanguageServiceBridge {
|
||||
private int describeServerCalls;
|
||||
private final BaselineServerDescription serverDescription = new BaselineServerDescription("demo", "1", true);
|
||||
|
||||
@Override
|
||||
public BaselineServerDescription describeServer(final LspProjectContext project) {
|
||||
describeServerCalls++;
|
||||
return serverDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaselineDocumentAnalysis analyzeDocument(final LspProjectContext project, final String documentUri, final String text) {
|
||||
return new BaselineDocumentAnalysis(List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaselineHover hover(final LspProjectContext project, final String documentUri, final int line, final int character) {
|
||||
return new BaselineHover("hover");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSave(final LspProjectContext project, final String documentUri) {
|
||||
return "saved";
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingMapper implements ProtocolMessageMapper {
|
||||
private InitializeResult initializeResult;
|
||||
private BaselineServerDescription lastServerDescription;
|
||||
|
||||
@Override
|
||||
public InitializeResult mapInitializeResult(final BaselineServerDescription description) {
|
||||
lastServerDescription = description;
|
||||
return initializeResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishDiagnosticsParams mapDiagnostics(final String uri, final BaselineDocumentAnalysis analysis) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishDiagnosticsParams emptyDiagnostics(final String uri) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.eclipse.lsp4j.Hover mapHover(final BaselineHover hover) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.eclipse.lsp4j.MessageParams mapInfoMessage(final String message) {
|
||||
return new org.eclipse.lsp4j.MessageParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.eclipse.lsp4j.MessageParams mapErrorMessage(final String message) {
|
||||
return new org.eclipse.lsp4j.MessageParams();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoopTextDocumentService implements TextDocumentService {
|
||||
@Override
|
||||
public void didOpen(final org.eclipse.lsp4j.DidOpenTextDocumentParams params) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChange(final org.eclipse.lsp4j.DidChangeTextDocumentParams params) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didClose(final org.eclipse.lsp4j.DidCloseTextDocumentParams params) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSave(final org.eclipse.lsp4j.DidSaveTextDocumentParams params) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,141 +0,0 @@
|
||||
package p.studio.lsp.services.protocol;
|
||||
|
||||
import org.eclipse.lsp4j.*;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.*;
|
||||
import p.studio.lsp.services.LanguageServiceBridge;
|
||||
import p.studio.lsp.services.protocol.mapping.ProtocolMessageMapper;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
class PrometeuTextDocumentServiceTest {
|
||||
|
||||
@Test
|
||||
void documentEventsDelegateToBridgeAndMapper() {
|
||||
final RecordingBridge bridge = new RecordingBridge();
|
||||
final RecordingMapper mapper = new RecordingMapper();
|
||||
final RecordingClient client = new RecordingClient();
|
||||
final PrometeuTextDocumentService service = new PrometeuTextDocumentService(
|
||||
new LspProjectContext("demo", "pbs", Path.of(".")),
|
||||
bridge,
|
||||
mapper);
|
||||
service.connect(client);
|
||||
|
||||
service.didOpen(new DidOpenTextDocumentParams(new TextDocumentItem("file:///demo.pbs", "pbs", 1, "abc")));
|
||||
service.didSave(new DidSaveTextDocumentParams(new TextDocumentIdentifier("file:///demo.pbs"), "abc"));
|
||||
service.hover(new HoverParams(new TextDocumentIdentifier("file:///demo.pbs"), new Position(0, 0))).join();
|
||||
|
||||
assertEquals(1, bridge.analyzeCalls);
|
||||
assertEquals(1, bridge.saveCalls);
|
||||
assertEquals(1, bridge.hoverCalls);
|
||||
assertSame(bridge.lastAnalysis, mapper.lastAnalysis);
|
||||
assertSame(bridge.lastHover, mapper.lastHover);
|
||||
assertSame(mapper.diagnostics, client.lastDiagnostics);
|
||||
assertSame(mapper.infoMessage, client.lastLogMessage);
|
||||
}
|
||||
|
||||
private static final class RecordingBridge implements LanguageServiceBridge {
|
||||
private int analyzeCalls;
|
||||
private int saveCalls;
|
||||
private int hoverCalls;
|
||||
private final BaselineDocumentAnalysis lastAnalysis = new BaselineDocumentAnalysis(List.of(
|
||||
new BaselineDocumentIssue(0, 0, 0, 1, BaselineIssueSeverity.INFORMATION, "src", "msg")));
|
||||
private final BaselineHover lastHover = new BaselineHover("hover");
|
||||
|
||||
@Override
|
||||
public BaselineServerDescription describeServer(final LspProjectContext project) {
|
||||
return new BaselineServerDescription("demo", "1", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaselineDocumentAnalysis analyzeDocument(final LspProjectContext project, final String documentUri, final String text) {
|
||||
analyzeCalls++;
|
||||
return lastAnalysis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaselineHover hover(final LspProjectContext project, final String documentUri, final int line, final int character) {
|
||||
hoverCalls++;
|
||||
return lastHover;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSave(final LspProjectContext project, final String documentUri) {
|
||||
saveCalls++;
|
||||
return "saved";
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingMapper implements ProtocolMessageMapper {
|
||||
private final PublishDiagnosticsParams diagnostics = new PublishDiagnosticsParams("file:///demo.pbs", List.of());
|
||||
private final MessageParams infoMessage = new MessageParams(MessageType.Info, "saved");
|
||||
private BaselineDocumentAnalysis lastAnalysis;
|
||||
private BaselineHover lastHover;
|
||||
|
||||
@Override
|
||||
public InitializeResult mapInitializeResult(final BaselineServerDescription description) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishDiagnosticsParams mapDiagnostics(final String uri, final BaselineDocumentAnalysis analysis) {
|
||||
lastAnalysis = analysis;
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishDiagnosticsParams emptyDiagnostics(final String uri) {
|
||||
return new PublishDiagnosticsParams(uri, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover mapHover(final BaselineHover hover) {
|
||||
lastHover = hover;
|
||||
return new Hover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageParams mapInfoMessage(final String message) {
|
||||
return infoMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageParams mapErrorMessage(final String message) {
|
||||
return infoMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingClient implements LanguageClient {
|
||||
private PublishDiagnosticsParams lastDiagnostics;
|
||||
private MessageParams lastLogMessage;
|
||||
|
||||
@Override
|
||||
public void telemetryEvent(final Object object) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishDiagnostics(final PublishDiagnosticsParams diagnostics) {
|
||||
lastDiagnostics = diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(final MessageParams messageParams) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageActionItem> showMessageRequest(final ShowMessageRequestParams requestParams) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logMessage(final MessageParams message) {
|
||||
lastLogMessage = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package p.studio.projects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@ -35,4 +36,11 @@ public record ProjectReference(
|
||||
public PackerProjectContext toPackerProjectContext() {
|
||||
return new PackerProjectContext(name, absoluteRootPath());
|
||||
}
|
||||
|
||||
public LspProjectContext toLspProjectContext() {
|
||||
return new LspProjectContext(
|
||||
rootPath().toAbsolutePath().normalize().toString(),
|
||||
languageId(),
|
||||
rootPath());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
package p.studio.projectsessions;
|
||||
|
||||
import p.studio.execution.StudioExecutionSessionService;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||
import p.studio.projectstate.ProjectLocalStudioState;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -17,7 +15,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
||||
private final StudioExecutionSessionService executionSessionService;
|
||||
private final LspServerLifecycle lspServerLifecycle;
|
||||
private final LspServerHandle lspServerHandle;
|
||||
private ProjectLocalStudioState projectLocalStudioState;
|
||||
private boolean closed;
|
||||
|
||||
@ -29,7 +26,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
}
|
||||
|
||||
@ -39,14 +35,12 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
||||
final StudioExecutionSessionService executionSessionService,
|
||||
final LspServerLifecycle lspServerLifecycle,
|
||||
final LspServerHandle lspServerHandle,
|
||||
final ProjectLocalStudioState projectLocalStudioState) {
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
||||
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
||||
this.lspServerLifecycle = lspServerLifecycle;
|
||||
this.lspServerHandle = lspServerHandle;
|
||||
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
||||
}
|
||||
|
||||
@ -66,10 +60,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
return executionSessionService;
|
||||
}
|
||||
|
||||
public LspServerHandle lspServerHandle() {
|
||||
return lspServerHandle;
|
||||
}
|
||||
|
||||
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
||||
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
||||
}
|
||||
@ -106,10 +96,9 @@ public final class StudioProjectSession implements AutoCloseable {
|
||||
}
|
||||
|
||||
private void shutdownLspServer() {
|
||||
if (lspServerLifecycle == null || lspServerHandle == null) {
|
||||
if (lspServerLifecycle == null) {
|
||||
return;
|
||||
}
|
||||
final LspProjectContext projectContext = lspServerHandle.project();
|
||||
lspServerLifecycle.shutdownServer(projectContext);
|
||||
lspServerLifecycle.shutdownServer(projectReference.toLspProjectContext());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
package p.studio.projectsessions;
|
||||
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.execution.StudioExecutionSessionService;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -36,22 +34,13 @@ public final class StudioProjectSessionFactory {
|
||||
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
|
||||
final var setup = projectLocalStudioSetupService.load(target);
|
||||
final var state = projectLocalStudioStateService.load(target);
|
||||
final LspProjectContext lspProjectContext = toLspProjectContext(target);
|
||||
final LspServerHandle lspServerHandle = lspServerLifecycle.bootServer(LspServerBootRequest.defaults(lspProjectContext));
|
||||
lspServerLifecycle.bootServer(LspServerConfiguration.defaults(), target.toLspProjectContext());
|
||||
return new StudioProjectSession(
|
||||
target,
|
||||
projectLocalStudioStateService,
|
||||
setup,
|
||||
new StudioExecutionSessionService(),
|
||||
lspServerLifecycle,
|
||||
lspServerHandle,
|
||||
state);
|
||||
}
|
||||
|
||||
private static LspProjectContext toLspProjectContext(final ProjectReference projectReference) {
|
||||
return new LspProjectContext(
|
||||
projectReference.rootPath().toAbsolutePath().normalize().toString(),
|
||||
projectReference.languageId(),
|
||||
projectReference.rootPath());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,18 @@
|
||||
package p.studio.projectsessions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.messages.LspServerEndpoint;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
||||
import p.studio.projectstate.ProjectLocalStudioState;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class StudioProjectSessionFactoryTest {
|
||||
@Test
|
||||
@ -44,10 +39,9 @@ final class StudioProjectSessionFactoryTest {
|
||||
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
||||
assertSame(projectReference, stateService.loadedProjectReference);
|
||||
assertSame(projectReference, setupService.loadedProjectReference);
|
||||
assertNotNull(session.lspServerHandle());
|
||||
assertEquals(1, lspServerLifecycle.bootCalls);
|
||||
assertEquals(projectReference.languageId(), lspServerLifecycle.lastRequest.project().languageId());
|
||||
assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), lspServerLifecycle.lastRequest.project().projectRoot());
|
||||
assertEquals(projectReference.languageId(), lspServerLifecycle.lastProjectContext.languageId());
|
||||
assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), lspServerLifecycle.lastProjectContext.projectRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -98,15 +92,12 @@ final class StudioProjectSessionFactoryTest {
|
||||
|
||||
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||
private int bootCalls;
|
||||
private LspServerBootRequest lastRequest;
|
||||
private LspProjectContext lastProjectContext;
|
||||
|
||||
@Override
|
||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||
bootCalls++;
|
||||
lastRequest = request;
|
||||
return new LspServerHandle(
|
||||
request.project(),
|
||||
new LspServerEndpoint("127.0.0.1", request.configuration().port()));
|
||||
lastProjectContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +107,7 @@ final class StudioProjectSessionFactoryTest {
|
||||
|
||||
private static final class FailingLspServerLifecycle implements LspServerLifecycle {
|
||||
@Override
|
||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||
throw new IllegalStateException("boom");
|
||||
}
|
||||
|
||||
|
||||
@ -2,15 +2,13 @@ package p.studio.projectsessions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.execution.StudioExecutionSessionService;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerHandle;
|
||||
import p.studio.lsp.api.LspServerLifecycle;
|
||||
import p.studio.lsp.messages.LspServerBootRequest;
|
||||
import p.studio.lsp.messages.LspServerEndpoint;
|
||||
import p.studio.lsp.messages.LspProjectContext;
|
||||
import p.studio.lsp.messages.LspServerConfiguration;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||
import p.studio.projectstate.ProjectLocalStudioState;
|
||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@ -27,7 +25,6 @@ final class StudioProjectSessionTest {
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
|
||||
session.close();
|
||||
@ -46,7 +43,6 @@ final class StudioProjectSessionTest {
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
null,
|
||||
null,
|
||||
ProjectLocalStudioState.defaults());
|
||||
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
||||
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
||||
@ -61,23 +57,18 @@ final class StudioProjectSessionTest {
|
||||
void closeShutsDownLspServerOnlyOnce() {
|
||||
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||
final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
|
||||
final LspServerHandle lspServerHandle = new LspServerHandle(
|
||||
new LspProjectContext("demo", "pbs", Path.of("/tmp/example")),
|
||||
new LspServerEndpoint("127.0.0.1", 7777));
|
||||
final StudioProjectSession session = new StudioProjectSession(
|
||||
projectReference(),
|
||||
stateService,
|
||||
ProjectLocalStudioSetup.defaults(),
|
||||
new StudioExecutionSessionService(),
|
||||
lspServerLifecycle,
|
||||
lspServerHandle,
|
||||
ProjectLocalStudioState.defaults());
|
||||
|
||||
session.close();
|
||||
session.close();
|
||||
|
||||
assertEquals(1, lspServerLifecycle.shutdownCalls);
|
||||
assertEquals(lspServerHandle.project(), lspServerLifecycle.lastProject);
|
||||
}
|
||||
|
||||
private ProjectReference projectReference() {
|
||||
@ -99,17 +90,15 @@ final class StudioProjectSessionTest {
|
||||
|
||||
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||
private int shutdownCalls;
|
||||
private LspProjectContext lastProject;
|
||||
|
||||
@Override
|
||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
||||
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownServer(final LspProjectContext project) {
|
||||
shutdownCalls++;
|
||||
lastProject = project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user