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.StudioEventBus;
|
||||||
import p.studio.lsp.events.StudioPackerEventAdapter;
|
import p.studio.lsp.events.StudioPackerEventAdapter;
|
||||||
import p.studio.lsp.services.LspServerLifecycleImpl;
|
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.ThemeService;
|
||||||
import p.studio.utilities.i18n.I18nService;
|
import p.studio.utilities.i18n.I18nService;
|
||||||
|
|
||||||
@ -34,9 +32,7 @@ public final class AppContainer implements Container {
|
|||||||
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
|
this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
|
||||||
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
|
final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
|
||||||
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
|
this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
|
||||||
this.lspServerLifecycle = new LspServerLifecycleImpl(
|
this.lspServerLifecycle = new LspServerLifecycleImpl();
|
||||||
new TcpLspServerHostFactory(),
|
|
||||||
new CompilerLanguageServiceBridge());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
package p.studio.lsp.api;
|
package p.studio.lsp.api;
|
||||||
|
|
||||||
import p.studio.lsp.messages.LspProjectContext;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
import p.studio.lsp.messages.LspServerBootRequest;
|
import p.studio.lsp.messages.LspServerConfiguration;
|
||||||
import p.studio.lsp.messages.LspServerHandle;
|
|
||||||
|
|
||||||
public interface LspServerLifecycle {
|
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;
|
package p.studio.lsp.messages;
|
||||||
|
|
||||||
import java.util.Objects;
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public record LspServerConfiguration(
|
@Builder
|
||||||
String host,
|
@Getter
|
||||||
int port) {
|
public class LspServerConfiguration {
|
||||||
public static final String DEFAULT_HOST = "127.0.0.1";
|
public static final String DEFAULT_HOST = "127.0.0.1";
|
||||||
public static final int DEFAULT_PORT = 7777;
|
public static final int DEFAULT_PORT = 7777;
|
||||||
|
|
||||||
public LspServerConfiguration {
|
@Builder.Default
|
||||||
host = requireHost(host);
|
private final LspServerEndpoint endpoint = new LspServerEndpoint(DEFAULT_HOST, DEFAULT_PORT);
|
||||||
if (port < 0 || port > 65535) {
|
|
||||||
throw new IllegalArgumentException("port must be between 0 and 65535");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LspServerConfiguration defaults() {
|
public static LspServerConfiguration defaults() {
|
||||||
return new LspServerConfiguration(DEFAULT_HOST, DEFAULT_PORT);
|
return LspServerConfiguration.builder().build();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
|
|
||||||
public interface LanguageServiceBridge {
|
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.api.LspServerLifecycle;
|
||||||
import p.studio.lsp.messages.LspProjectContext;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
import p.studio.lsp.messages.LspServerBootRequest;
|
import p.studio.lsp.messages.LspServerConfiguration;
|
||||||
import p.studio.lsp.messages.LspServerHandle;
|
|
||||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
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.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
public final class LspServerLifecycleImpl implements LspServerLifecycle {
|
public final class LspServerLifecycleImpl implements LspServerLifecycle {
|
||||||
private final ProjectScopedLspServerHostFactory serverHostFactory;
|
private final TcpLspServerHostFactory serverHostFactory;
|
||||||
private final CompilerLanguageServiceBridge compilerBridge;
|
private final CompilerLanguageServiceBridge compilerBridge;
|
||||||
private final ConcurrentMap<String, ProjectScopedLspServerHost> activeServers;
|
private final ConcurrentMap<String, TcpLspServerHost> activeServers;
|
||||||
|
|
||||||
public LspServerLifecycleImpl(
|
public LspServerLifecycleImpl() {
|
||||||
final ProjectScopedLspServerHostFactory serverHostFactory,
|
this.compilerBridge = new CompilerLanguageServiceBridge();
|
||||||
final CompilerLanguageServiceBridge compilerBridge) {
|
this.serverHostFactory = new TcpLspServerHostFactory();
|
||||||
this.serverHostFactory = Objects.requireNonNull(serverHostFactory, "serverHostFactory");
|
|
||||||
this.compilerBridge = Objects.requireNonNull(compilerBridge, "compilerBridge");
|
|
||||||
this.activeServers = new ConcurrentHashMap<>();
|
this.activeServers = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
public void bootServer(final LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||||
final LspServerBootRequest safeRequest = Objects.requireNonNull(request, "request");
|
final var host = serverHostFactory.create(serverConfiguration, context, compilerBridge);
|
||||||
final ProjectScopedLspServerHost host = serverHostFactory.create(safeRequest, compilerBridge);
|
final var previous = activeServers.putIfAbsent(context.projectKey(), host);
|
||||||
final ProjectScopedLspServerHost previous = activeServers.putIfAbsent(safeRequest.project().projectKey(), host);
|
|
||||||
if (previous != null) {
|
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 {
|
try {
|
||||||
final var endpoint = host.start();
|
host.start();
|
||||||
return new LspServerHandle(safeRequest.project(), endpoint);
|
|
||||||
} catch (RuntimeException runtimeException) {
|
} catch (RuntimeException runtimeException) {
|
||||||
activeServers.remove(safeRequest.project().projectKey(), host);
|
activeServers.remove(context.projectKey(), host);
|
||||||
closeQuietly(host);
|
closeQuietly(host);
|
||||||
throw runtimeException;
|
throw runtimeException;
|
||||||
}
|
}
|
||||||
@ -46,15 +39,15 @@ public final class LspServerLifecycleImpl implements LspServerLifecycle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdownServer(final LspProjectContext project) {
|
public void shutdownServer(final LspProjectContext project) {
|
||||||
final LspProjectContext safeProject = Objects.requireNonNull(project, "project");
|
final var safeProject = Objects.requireNonNull(project, "project");
|
||||||
final ProjectScopedLspServerHost host = activeServers.remove(safeProject.projectKey());
|
final var host = activeServers.remove(safeProject.projectKey());
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
closeQuietly(host);
|
closeQuietly(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void closeQuietly(final ProjectScopedLspServerHost host) {
|
private static void closeQuietly(final TcpLspServerHost host) {
|
||||||
try {
|
try {
|
||||||
host.close();
|
host.close();
|
||||||
} catch (Exception exception) {
|
} 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.jsonrpc.Launcher;
|
||||||
import org.eclipse.lsp4j.launch.LSPLauncher;
|
import org.eclipse.lsp4j.launch.LSPLauncher;
|
||||||
import org.eclipse.lsp4j.services.LanguageClient;
|
import org.eclipse.lsp4j.services.LanguageClient;
|
||||||
import p.studio.lsp.messages.LspServerBootRequest;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
import p.studio.lsp.messages.LspServerEndpoint;
|
import p.studio.lsp.messages.LspServerConfiguration;
|
||||||
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
import p.studio.lsp.services.compiler.CompilerLanguageServiceBridge;
|
||||||
import p.studio.lsp.services.protocol.PrometeuLanguageServer;
|
import p.studio.lsp.services.protocol.PrometeuLanguageServer;
|
||||||
|
|
||||||
@ -16,19 +16,21 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
public final class TcpLspServerHost {
|
||||||
private final LspServerBootRequest request;
|
private final LspServerConfiguration serverConfiguration;
|
||||||
|
private final LspProjectContext context;
|
||||||
private final CompilerLanguageServiceBridge compilerBridge;
|
private final CompilerLanguageServiceBridge compilerBridge;
|
||||||
private final ExecutorService clientExecutor;
|
private final ExecutorService clientExecutor;
|
||||||
private volatile boolean closed;
|
private volatile boolean closed;
|
||||||
private volatile ServerSocket serverSocket;
|
private volatile ServerSocket serverSocket;
|
||||||
private volatile Thread acceptThread;
|
private volatile Thread acceptThread;
|
||||||
private volatile LspServerEndpoint endpoint;
|
|
||||||
|
|
||||||
public TcpLspServerHost(
|
public TcpLspServerHost(
|
||||||
final LspServerBootRequest request,
|
final LspServerConfiguration serverConfiguration,
|
||||||
|
final LspProjectContext context,
|
||||||
final CompilerLanguageServiceBridge compilerBridge) {
|
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.compilerBridge = Objects.requireNonNull(compilerBridge, "compilerBridge");
|
||||||
this.clientExecutor = Executors.newCachedThreadPool(runnable -> {
|
this.clientExecutor = Executors.newCachedThreadPool(runnable -> {
|
||||||
final Thread thread = new Thread(runnable);
|
final Thread thread = new Thread(runnable);
|
||||||
@ -38,20 +40,16 @@ public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public synchronized void start() {
|
||||||
public synchronized LspServerEndpoint start() {
|
if (serverSocket != null) {
|
||||||
if (endpoint != null) {
|
return;
|
||||||
return endpoint;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final InetAddress bindAddress = InetAddress.getByName(request.configuration().host());
|
final InetAddress bindAddress = InetAddress.getByName(serverConfiguration.getEndpoint().host());
|
||||||
final ServerSocket socket = new ServerSocket(request.configuration().port(), 50, bindAddress);
|
serverSocket = new ServerSocket(serverConfiguration.getEndpoint().port(), 50, bindAddress);
|
||||||
serverSocket = socket;
|
|
||||||
endpoint = new LspServerEndpoint(bindAddress.getHostAddress(), socket.getLocalPort());
|
|
||||||
acceptThread = new Thread(this::acceptLoop, "prometeu-lsp-v1-accept");
|
acceptThread = new Thread(this::acceptLoop, "prometeu-lsp-v1-accept");
|
||||||
acceptThread.setDaemon(true);
|
acceptThread.setDaemon(true);
|
||||||
acceptThread.start();
|
acceptThread.start();
|
||||||
return endpoint;
|
|
||||||
} catch (IOException exception) {
|
} catch (IOException exception) {
|
||||||
throw new IllegalStateException("failed to start TCP LSP host", 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) {
|
private void handleClient(final Socket socket) {
|
||||||
try (socket) {
|
try (socket) {
|
||||||
final var server = new PrometeuLanguageServer(request.project(), compilerBridge);
|
final var server = new PrometeuLanguageServer(context, compilerBridge);
|
||||||
final Launcher<LanguageClient> launcher = LSPLauncher.createServerLauncher(
|
final Launcher<LanguageClient> launcher = LSPLauncher.createServerLauncher(
|
||||||
server,
|
server,
|
||||||
socket.getInputStream(),
|
socket.getInputStream(),
|
||||||
@ -84,7 +82,6 @@ public final class TcpLspServerHost implements ProjectScopedLspServerHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void close() throws Exception {
|
public synchronized void close() throws Exception {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
return;
|
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.messages.*;
|
||||||
import p.studio.lsp.services.LanguageServiceBridge;
|
import p.studio.lsp.services.LanguageServiceBridge;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class CompilerLanguageServiceBridge implements LanguageServiceBridge {
|
public record CompilerLanguageServiceBridge(
|
||||||
private final BuilderPipelineService builderPipelineService;
|
BuilderPipelineService builderPipelineService) implements LanguageServiceBridge {
|
||||||
|
|
||||||
public CompilerLanguageServiceBridge() {
|
public CompilerLanguageServiceBridge() {
|
||||||
this(BuilderPipelineService.INSTANCE);
|
this(BuilderPipelineService.INSTANCE);
|
||||||
@ -17,13 +18,9 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
this.builderPipelineService = Objects.requireNonNull(builderPipelineService, "builderPipelineService");
|
this.builderPipelineService = Objects.requireNonNull(builderPipelineService, "builderPipelineService");
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuilderPipelineService builderPipelineService() {
|
|
||||||
return builderPipelineService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaselineServerDescription describeServer(final LspProjectContext project) {
|
public BaselineServerDescription describeServer(final LspProjectContext context) {
|
||||||
Objects.requireNonNull(project, "project");
|
Objects.requireNonNull(context, "context");
|
||||||
return new BaselineServerDescription(
|
return new BaselineServerDescription(
|
||||||
"Prometeu Studio LSP",
|
"Prometeu Studio LSP",
|
||||||
"0.1.0",
|
"0.1.0",
|
||||||
@ -32,13 +29,13 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaselineDocumentAnalysis analyzeDocument(
|
public BaselineDocumentAnalysis analyzeDocument(
|
||||||
final LspProjectContext project,
|
final LspProjectContext context,
|
||||||
final String documentUri,
|
final String documentUri,
|
||||||
final String text) {
|
final String text) {
|
||||||
Objects.requireNonNull(project, "project");
|
Objects.requireNonNull(context, "context");
|
||||||
// Wave 1 keeps responses intentionally synthetic while hardening the bridge boundary.
|
// Wave 1 keeps responses intentionally synthetic while hardening the bridge boundary.
|
||||||
final String message = "Compiler-backed baseline via " + builderPipelineService.getClass().getSimpleName();
|
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,
|
0,
|
||||||
0,
|
0,
|
||||||
@ -50,25 +47,25 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaselineHover hover(
|
public BaselineHover hover(
|
||||||
final LspProjectContext project,
|
final LspProjectContext context,
|
||||||
final String documentUri,
|
final String documentUri,
|
||||||
final int line,
|
final int line,
|
||||||
final int character) {
|
final int character) {
|
||||||
Objects.requireNonNull(project, "project");
|
Objects.requireNonNull(context, "context");
|
||||||
return new BaselineHover("""
|
return new BaselineHover("""
|
||||||
**Prometeu Studio LSP**
|
**Prometeu Studio LSP**
|
||||||
|
|
||||||
Compiler-backed baseline.
|
Compiler-backed baseline.
|
||||||
|
|
||||||
Semantic enrichment will be layered later.
|
Semantic enrichment will be layered later.
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String onSave(
|
public String onSave(
|
||||||
final LspProjectContext project,
|
final LspProjectContext context,
|
||||||
final String documentUri) {
|
final String documentUri) {
|
||||||
Objects.requireNonNull(project, "project");
|
Objects.requireNonNull(context, "context");
|
||||||
return "Prometeu Studio saw save: " + documentUri;
|
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 org.apache.commons.lang3.StringUtils;
|
||||||
import p.packer.messages.PackerProjectContext;
|
import p.packer.messages.PackerProjectContext;
|
||||||
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
@ -35,4 +36,11 @@ public record ProjectReference(
|
|||||||
public PackerProjectContext toPackerProjectContext() {
|
public PackerProjectContext toPackerProjectContext() {
|
||||||
return new PackerProjectContext(name, absoluteRootPath());
|
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;
|
package p.studio.projectsessions;
|
||||||
|
|
||||||
import p.studio.execution.StudioExecutionSessionService;
|
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.api.LspServerLifecycle;
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
import p.studio.projects.ProjectReference;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -17,7 +15,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
private final ProjectLocalStudioSetup projectLocalStudioSetup;
|
||||||
private final StudioExecutionSessionService executionSessionService;
|
private final StudioExecutionSessionService executionSessionService;
|
||||||
private final LspServerLifecycle lspServerLifecycle;
|
private final LspServerLifecycle lspServerLifecycle;
|
||||||
private final LspServerHandle lspServerHandle;
|
|
||||||
private ProjectLocalStudioState projectLocalStudioState;
|
private ProjectLocalStudioState projectLocalStudioState;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
@ -29,7 +26,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,14 +35,12 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
final ProjectLocalStudioSetup projectLocalStudioSetup,
|
||||||
final StudioExecutionSessionService executionSessionService,
|
final StudioExecutionSessionService executionSessionService,
|
||||||
final LspServerLifecycle lspServerLifecycle,
|
final LspServerLifecycle lspServerLifecycle,
|
||||||
final LspServerHandle lspServerHandle,
|
|
||||||
final ProjectLocalStudioState projectLocalStudioState) {
|
final ProjectLocalStudioState projectLocalStudioState) {
|
||||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||||
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
this.projectLocalStudioStateService = Objects.requireNonNull(projectLocalStudioStateService, "projectLocalStudioStateService");
|
||||||
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
this.projectLocalStudioSetup = Objects.requireNonNull(projectLocalStudioSetup, "projectLocalStudioSetup");
|
||||||
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
this.executionSessionService = Objects.requireNonNull(executionSessionService, "executionSessionService");
|
||||||
this.lspServerLifecycle = lspServerLifecycle;
|
this.lspServerLifecycle = lspServerLifecycle;
|
||||||
this.lspServerHandle = lspServerHandle;
|
|
||||||
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
this.projectLocalStudioState = Objects.requireNonNull(projectLocalStudioState, "projectLocalStudioState");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +60,6 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
return executionSessionService;
|
return executionSessionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LspServerHandle lspServerHandle() {
|
|
||||||
return lspServerHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
public void replaceProjectLocalStudioState(final ProjectLocalStudioState nextProjectLocalStudioState) {
|
||||||
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
this.projectLocalStudioState = Objects.requireNonNull(nextProjectLocalStudioState, "nextProjectLocalStudioState");
|
||||||
}
|
}
|
||||||
@ -106,10 +96,9 @@ public final class StudioProjectSession implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void shutdownLspServer() {
|
private void shutdownLspServer() {
|
||||||
if (lspServerLifecycle == null || lspServerHandle == null) {
|
if (lspServerLifecycle == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final LspProjectContext projectContext = lspServerHandle.project();
|
lspServerLifecycle.shutdownServer(projectReference.toLspProjectContext());
|
||||||
lspServerLifecycle.shutdownServer(projectContext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
package p.studio.projectsessions;
|
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.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.ProjectLocalStudioSetupService;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
import p.studio.projects.ProjectReference;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -36,22 +34,13 @@ public final class StudioProjectSessionFactory {
|
|||||||
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
|
final ProjectReference target = Objects.requireNonNull(projectReference, "projectReference");
|
||||||
final var setup = projectLocalStudioSetupService.load(target);
|
final var setup = projectLocalStudioSetupService.load(target);
|
||||||
final var state = projectLocalStudioStateService.load(target);
|
final var state = projectLocalStudioStateService.load(target);
|
||||||
final LspProjectContext lspProjectContext = toLspProjectContext(target);
|
lspServerLifecycle.bootServer(LspServerConfiguration.defaults(), target.toLspProjectContext());
|
||||||
final LspServerHandle lspServerHandle = lspServerLifecycle.bootServer(LspServerBootRequest.defaults(lspProjectContext));
|
|
||||||
return new StudioProjectSession(
|
return new StudioProjectSession(
|
||||||
target,
|
target,
|
||||||
projectLocalStudioStateService,
|
projectLocalStudioStateService,
|
||||||
setup,
|
setup,
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
lspServerLifecycle,
|
lspServerLifecycle,
|
||||||
lspServerHandle,
|
|
||||||
state);
|
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;
|
package p.studio.projectsessions;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
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.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.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
import p.studio.projectstate.ProjectLocalStudioSetupService;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
import p.studio.projects.ProjectReference;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
final class StudioProjectSessionFactoryTest {
|
final class StudioProjectSessionFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
@ -44,10 +39,9 @@ final class StudioProjectSessionFactoryTest {
|
|||||||
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
assertEquals("IDLE", session.executionSession().snapshot().state().name());
|
||||||
assertSame(projectReference, stateService.loadedProjectReference);
|
assertSame(projectReference, stateService.loadedProjectReference);
|
||||||
assertSame(projectReference, setupService.loadedProjectReference);
|
assertSame(projectReference, setupService.loadedProjectReference);
|
||||||
assertNotNull(session.lspServerHandle());
|
|
||||||
assertEquals(1, lspServerLifecycle.bootCalls);
|
assertEquals(1, lspServerLifecycle.bootCalls);
|
||||||
assertEquals(projectReference.languageId(), lspServerLifecycle.lastRequest.project().languageId());
|
assertEquals(projectReference.languageId(), lspServerLifecycle.lastProjectContext.languageId());
|
||||||
assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), lspServerLifecycle.lastRequest.project().projectRoot());
|
assertEquals(projectReference.rootPath().toAbsolutePath().normalize(), lspServerLifecycle.lastProjectContext.projectRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -98,15 +92,12 @@ final class StudioProjectSessionFactoryTest {
|
|||||||
|
|
||||||
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||||
private int bootCalls;
|
private int bootCalls;
|
||||||
private LspServerBootRequest lastRequest;
|
private LspProjectContext lastProjectContext;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||||
bootCalls++;
|
bootCalls++;
|
||||||
lastRequest = request;
|
lastProjectContext = context;
|
||||||
return new LspServerHandle(
|
|
||||||
request.project(),
|
|
||||||
new LspServerEndpoint("127.0.0.1", request.configuration().port()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -116,7 +107,7 @@ final class StudioProjectSessionFactoryTest {
|
|||||||
|
|
||||||
private static final class FailingLspServerLifecycle implements LspServerLifecycle {
|
private static final class FailingLspServerLifecycle implements LspServerLifecycle {
|
||||||
@Override
|
@Override
|
||||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||||
throw new IllegalStateException("boom");
|
throw new IllegalStateException("boom");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,15 +2,13 @@ package p.studio.projectsessions;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import p.studio.execution.StudioExecutionSessionService;
|
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.api.LspServerLifecycle;
|
||||||
import p.studio.lsp.messages.LspServerBootRequest;
|
import p.studio.lsp.messages.LspProjectContext;
|
||||||
import p.studio.lsp.messages.LspServerEndpoint;
|
import p.studio.lsp.messages.LspServerConfiguration;
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
import p.studio.projectstate.ProjectLocalStudioSetup;
|
import p.studio.projectstate.ProjectLocalStudioSetup;
|
||||||
import p.studio.projectstate.ProjectLocalStudioState;
|
import p.studio.projectstate.ProjectLocalStudioState;
|
||||||
import p.studio.projectstate.ProjectLocalStudioStateService;
|
import p.studio.projectstate.ProjectLocalStudioStateService;
|
||||||
import p.studio.projects.ProjectReference;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
@ -27,7 +25,6 @@ final class StudioProjectSessionTest {
|
|||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
|
|
||||||
session.close();
|
session.close();
|
||||||
@ -46,7 +43,6 @@ final class StudioProjectSessionTest {
|
|||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
final ProjectLocalStudioState nextState = ProjectLocalStudioState.defaults()
|
||||||
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
.withOpenShellState(new ProjectLocalStudioState.OpenShellState("ASSETS"));
|
||||||
@ -61,23 +57,18 @@ final class StudioProjectSessionTest {
|
|||||||
void closeShutsDownLspServerOnlyOnce() {
|
void closeShutsDownLspServerOnlyOnce() {
|
||||||
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
final RecordingProjectLocalStudioStateService stateService = new RecordingProjectLocalStudioStateService();
|
||||||
final RecordingLspServerLifecycle lspServerLifecycle = new RecordingLspServerLifecycle();
|
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(
|
final StudioProjectSession session = new StudioProjectSession(
|
||||||
projectReference(),
|
projectReference(),
|
||||||
stateService,
|
stateService,
|
||||||
ProjectLocalStudioSetup.defaults(),
|
ProjectLocalStudioSetup.defaults(),
|
||||||
new StudioExecutionSessionService(),
|
new StudioExecutionSessionService(),
|
||||||
lspServerLifecycle,
|
lspServerLifecycle,
|
||||||
lspServerHandle,
|
|
||||||
ProjectLocalStudioState.defaults());
|
ProjectLocalStudioState.defaults());
|
||||||
|
|
||||||
session.close();
|
session.close();
|
||||||
session.close();
|
session.close();
|
||||||
|
|
||||||
assertEquals(1, lspServerLifecycle.shutdownCalls);
|
assertEquals(1, lspServerLifecycle.shutdownCalls);
|
||||||
assertEquals(lspServerHandle.project(), lspServerLifecycle.lastProject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProjectReference projectReference() {
|
private ProjectReference projectReference() {
|
||||||
@ -99,17 +90,15 @@ final class StudioProjectSessionTest {
|
|||||||
|
|
||||||
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
private static final class RecordingLspServerLifecycle implements LspServerLifecycle {
|
||||||
private int shutdownCalls;
|
private int shutdownCalls;
|
||||||
private LspProjectContext lastProject;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LspServerHandle bootServer(final LspServerBootRequest request) {
|
public void bootServer(LspServerConfiguration serverConfiguration, LspProjectContext context) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdownServer(final LspProjectContext project) {
|
public void shutdownServer(final LspProjectContext project) {
|
||||||
shutdownCalls++;
|
shutdownCalls++;
|
||||||
lastProject = project;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user