Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Language Server integration with PM #11880

Merged
merged 22 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2283,12 +2283,13 @@ lazy val `language-server` = (project in file("engine/language-server"))
javaModuleName := "org.enso.language.server",
Compile / moduleDependencies ++=
Seq(
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"commons-cli" % "commons-cli" % commonsCliVersion,
"commons-io" % "commons-io" % commonsIoVersion,
"com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion,
"org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"commons-cli" % "commons-cli" % commonsCliVersion,
"commons-io" % "commons-io" % commonsIoVersion,
"com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion,
"org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion,
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
),
Compile / internalModuleDependencies := Seq(
(`akka-wrapper` / Compile / exportedModule).value,
Expand Down Expand Up @@ -3614,13 +3615,17 @@ lazy val `engine-runner` = project
val epbLang =
(`runtime-language-epb` / Compile / fullClasspath).value
.map(_.data.getAbsolutePath)
val langServer =
(`language-server` / Compile / fullClasspath).value
.map(_.data.getAbsolutePath)
val core = (
runnerDeps ++
runtimeDeps ++
loggingDeps ++
replDebugInstr ++
runtimeServerInstr ++
idExecInstr ++
langServer ++
epbLang
).distinct
val stdLibsJars =
Expand Down Expand Up @@ -3705,6 +3710,7 @@ lazy val `engine-runner` = project
"-H:IncludeResources=.*Main.enso$",
"-H:+AddAllCharsets",
"-H:+IncludeAllLocales",
"-H:+UnlockExperimentalVMOptions",
hubertp marked this conversation as resolved.
Show resolved Hide resolved
"-ea",
// useful perf & debug switches:
// "-g",
Expand All @@ -3721,6 +3727,7 @@ lazy val `engine-runner` = project
"org.jline",
"io.methvin.watchservice",
"zio.internal",
"zio",
"org.enso.runner",
"sun.awt",
"sun.java2d",
Expand All @@ -3732,7 +3739,8 @@ lazy val `engine-runner` = project
"akka.http",
"org.enso.base",
"org.enso.image",
"org.enso.table"
"org.enso.table",
"org.eclipse.jgit"
)
)
}
Expand Down
23 changes: 20 additions & 3 deletions engine/common/src/main/java/org/enso/common/ContextFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.enso.logger.JulHandler;
import org.enso.logging.config.LoggerSetup;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.io.MessageTransport;
import org.slf4j.event.Level;
Expand Down Expand Up @@ -52,6 +53,7 @@ public final class ContextFactory {
private String checkForWarnings;
private int warningsLimit = 100;
private java.util.Map<String, String> options = new HashMap<>();
private java.util.Map<String, String> engineOptions = null;
private boolean enableDebugServer;

private ContextFactory() {}
Expand Down Expand Up @@ -145,6 +147,11 @@ public ContextFactory options(Map<String, String> options) {
return this;
}

public ContextFactory engineOptions(Map<String, String> options) {
this.engineOptions = options;
return this;
}

public ContextFactory checkForWarnings(String fqnOfMethod) {
this.checkForWarnings = fqnOfMethod;
return this;
Expand Down Expand Up @@ -189,9 +196,6 @@ public Context build() {
if (enableDebugServer) {
builder.option(DebugServerInfo.ENABLE_OPTION, "true");
}
if (messageTransport != null) {
builder.serverTransport(messageTransport);
}
builder.option(RuntimeOptions.LOG_LEVEL, logLevelName);
var logHandler = JulHandler.get();
var logLevels = LoggerSetup.get().getConfig().getLoggers();
Expand Down Expand Up @@ -228,6 +232,19 @@ public Context build() {
.allowCreateThread(true);
}

if (engineOptions != null) {
// In AOT mode one must not use a shared engine; the latter causes issues when initializing
// message transport - it is set to `null`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I don't see a reason wny transport shouldn't work. Maybe it is a bug. Do we have a (small) reproducer that we could report to GraalVM guys and find out what they think?

var eng = Engine.newBuilder().allowExperimentalOptions(true).options(engineOptions);

if (messageTransport != null) {
eng.serverTransport(messageTransport);
}
builder.engine(eng.build());
} else if (messageTransport != null) {
builder.serverTransport(messageTransport);
}

var ctx = builder.build();
ContextInsightSetup.configureContext(ctx);
return ctx;
Expand Down
2 changes: 1 addition & 1 deletion engine/language-server/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
requires org.enso.text.buffer;
requires org.enso.task.progress.notifications;
requires org.enso.ydoc.polyfill;
requires org.openide.util.lookup.RELEASE180;
hubertp marked this conversation as resolved.
Show resolved Hide resolved

exports org.enso.languageserver.boot;
exports org.enso.languageserver.filemanager to scala.library;
exports org.enso.languageserver.runtime to scala.library;
exports org.enso.languageserver.search to scala.library;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.slf4j.event.Level;
import scala.concurrent.ExecutionContext;

@org.openide.util.lookup.ServiceProvider(service = LanguageServerApi.class)
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
public final class LanguageServerRunner extends LanguageServerApi {
public LanguageServerRunner() {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package org.enso.languageserver.boot.resource;

import akka.event.EventStream;
import java.io.IOException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.concurrent.*;
import org.apache.commons.io.FileUtils;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import org.enso.languageserver.data.ProjectDirectoriesConfig;
import org.enso.languageserver.event.InitializedEvent;
import org.enso.logger.masking.MaskedPath;
import org.enso.searcher.memory.InMemorySuggestionsRepo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -72,14 +69,18 @@ public CompletableFuture<Void> init() {
}

private CompletableFuture<Void> initSuggestionsRepo() {
return CompletableFuture.runAsync(
() -> logger.debug("Initializing suggestions repo [{}]...", suggestionsRepo), executor)
.thenComposeAsync(
v -> {
if (!isInitialized)
return doInitSuggestionsRepo()
.exceptionallyComposeAsync(this::recoverInitializationError, executor);
else return CompletableFuture.completedFuture(v);
return CompletableFuture.supplyAsync(
() -> {
logger.debug("Initializing Suggestions repo [{}]...", suggestionsRepo);
try {
lock.acquire();
if (!isInitialized)
return doInitSuggestionsRepo()
.exceptionallyComposeAsync(this::recoverInitializationError, executor);
else return CompletableFuture.completedFuture(null);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},
executor)
.thenRunAsync(
Expand All @@ -105,60 +106,6 @@ private CompletableFuture<Void> recoverInitializationError(Throwable error) {
.thenComposeAsync(v -> doInitSuggestionsRepo(), executor);
}

private CompletableFuture<Void> clearDatabaseFile(int retries) {
return CompletableFuture.runAsync(
() -> {
if (!isInitialized) {
logger.debug("Clear database file. Attempt #{}", retries + 1);
try {
Files.delete(projectDirectoriesConfig.suggestionsDatabaseFile().toPath());
} catch (IOException e) {
throw new CompletionException(e);
}
}
},
executor)
.exceptionallyComposeAsync(error -> recoverClearDatabaseFile(error, retries), executor);
}

private CompletableFuture<Void> recoverClearDatabaseFile(Throwable error, int retries) {
if (error instanceof CompletionException) {
return recoverClearDatabaseFile(error.getCause(), retries);
} else if (error instanceof NoSuchFileException) {
logger.warn(
"Failed to delete the database file. Attempt #{}. File does not exist [{}]",
retries + 1,
new MaskedPath(projectDirectoriesConfig.suggestionsDatabaseFile().toPath()));
return CompletableFuture.completedFuture(null);
} else if (error instanceof FileSystemException) {
logger.error(
"Failed to delete the database file. Attempt #{}. The file will be removed during the"
+ " shutdown",
retries + 1,
error);
Runtime.getRuntime()
.addShutdownHook(
new Thread(
() ->
FileUtils.deleteQuietly(projectDirectoriesConfig.suggestionsDatabaseFile())));
return CompletableFuture.failedFuture(error);
} else if (error instanceof IOException) {
logger.error("Failed to delete the database file. Attempt #{}", retries + 1, error);
if (retries < MAX_RETRIES) {
try {
Thread.sleep(RETRY_DELAY_MILLIS);
} catch (InterruptedException e) {
throw new CompletionException(e);
}
return clearDatabaseFile(retries + 1);
} else {
return CompletableFuture.failedFuture(error);
}
}

return CompletableFuture.completedFuture(null);
}

private CompletionStage<Void> doInitSuggestionsRepo() {
return FutureConverters.asJava(suggestionsRepo.init()).thenAcceptAsync(res -> {}, executor);
}
Expand Down
Loading
Loading