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

Provide progress on post-init #131

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.finos.legend.engine.ide.lsp.classpath;

import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.finos.legend.engine.ide.lsp.server.LegendLanguageServer;

public interface ClasspathFactory
Expand All @@ -24,5 +25,5 @@ default void initialize(LegendLanguageServer server)

}

CompletableFuture<ClassLoader> create(Iterable<String> folders);
CompletableFuture<ClassLoader> create(Iterable<String> folders, Either<String, Integer> progressToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.apache.maven.shared.utils.cli.Commandline;
import org.eclipse.lsp4j.ConfigurationItem;
import org.eclipse.lsp4j.ConfigurationParams;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.finos.legend.engine.ide.lsp.server.LegendLanguageServer;

public class ClasspathUsingMavenFactory implements ClasspathFactory
Expand Down Expand Up @@ -122,8 +123,10 @@ public void initialize(LegendLanguageServer server)
}

@Override
public CompletableFuture<ClassLoader> create(Iterable<String> folders)
public CompletableFuture<ClassLoader> create(Iterable<String> folders, Either<String, Integer> progressToken)
{
this.server.notifyProgress(progressToken, "Preparing for maven...");

this.server.logInfoToClient("Discovering classpath using maven");

ConfigurationItem mavenExecPathConfig = new ConfigurationItem();
Expand Down Expand Up @@ -151,6 +154,7 @@ public CompletableFuture<ClassLoader> create(Iterable<String> folders)
// todo last, use a default pom...

this.server.logInfoToClient("Dependencies loaded from POM: " + pom);
this.server.notifyProgress(progressToken, "Loading dependencies using maven...");

File legendLspClasspath = File.createTempFile("legend_lsp_classpath", ".txt");
legendLspClasspath.deleteOnExit();
Expand All @@ -160,7 +164,7 @@ public CompletableFuture<ClassLoader> create(Iterable<String> folders)

InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(pom);
request.setOutputHandler(new PrintStreamHandler(new PrintStream(this.outputStream, true), true));
request.setOutputHandler(new ClasspathUsingMavenOutputHandler(progressToken, new PrintStream(this.outputStream, true), true));
request.setGoals(Collections.singletonList("dependency:build-classpath"));
request.setProperties(properties);
request.setTimeoutInSeconds((int) TimeUnit.MINUTES.toSeconds(15));
Expand All @@ -181,6 +185,7 @@ public CompletableFuture<ClassLoader> create(Iterable<String> folders)
String classpath = Files.readString(legendLspClasspath.toPath(), StandardCharsets.UTF_8);

this.server.logInfoToClient("Classpath used: " + classpath);
this.server.notifyProgress(progressToken, "Maven finished and classpath is ready...");

String[] classpathEntries = classpath.split(";");
URL[] urls = new URL[classpathEntries.length];
Expand All @@ -204,4 +209,22 @@ public CompletableFuture<ClassLoader> create(Iterable<String> folders)
}
});
}

private class ClasspathUsingMavenOutputHandler extends PrintStreamHandler
{
private final Either<String, Integer> token;

public ClasspathUsingMavenOutputHandler(Either<String, Integer> token, PrintStream out, boolean alwaysFlush)
{
super(out, alwaysFlush);
this.token = token;
}

@Override
public void consumeLine(String line)
{
super.consumeLine(line);
ClasspathUsingMavenFactory.this.server.notifyProgress(token, line);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.finos.legend.engine.ide.lsp.classpath;

import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.finos.legend.engine.ide.lsp.server.LegendLanguageServer;

public class EmbeddedClasspathFactory implements ClasspathFactory
Expand All @@ -28,7 +29,7 @@ public void initialize(LegendLanguageServer server)
}

@Override
public CompletableFuture<ClassLoader> create(Iterable<String> folders)
public CompletableFuture<ClassLoader> create(Iterable<String> folders, Either<String, Integer> progressToken)
{
this.server.logInfoToClient("Using app classpath");
return CompletableFuture.completedFuture(EmbeddedClasspathFactory.class.getClassLoader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.CodeLensOptions;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.ConfigurationItem;
import org.eclipse.lsp4j.ConfigurationParams;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.FileOperationFilter;
import org.eclipse.lsp4j.FileOperationOptions;
Expand All @@ -54,6 +54,7 @@
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.eclipse.lsp4j.WorkDoneProgressBegin;
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
import org.eclipse.lsp4j.WorkDoneProgressEnd;
import org.eclipse.lsp4j.WorkDoneProgressNotification;
import org.eclipse.lsp4j.WorkDoneProgressReport;
Expand Down Expand Up @@ -220,25 +221,43 @@ private void setEngineServerUrl(String url)

private void initializeExtensions()
{
logInfoToClient("Initializing extensions");

this.classpathFactory.create(Collections.unmodifiableSet(this.rootFolders))
.thenAccept(this.extensionGuard::initialize)
.thenRun(this.extensionGuard.wrapOnClasspath(this::reprocessDocuments))
.thenRun(() ->
{
LanguageClient languageClient = this.getLanguageClient();
languageClient.refreshCodeLenses();
languageClient.refreshDiagnostics();
languageClient.refreshInlayHints();
languageClient.refreshInlineValues();
languageClient.refreshSemanticTokens();
}).exceptionally(x ->
{
LOGGER.error("Failed during post-initialization", x);
logErrorToClient("Failed during post-initialization: " + x.getMessage());
return null;
});
this.startProgress("Finalize Initialization (This can take few minutes)", "Figuring out next steps...")
.thenApply(token ->
this.classpathFactory.create(Collections.unmodifiableSet(this.rootFolders), token)
.thenApply(x ->
{
this.notifyProgress(token, "Looking at the classpath for the features Legend supports...");
return Objects.requireNonNull(x, "Not able to determine classpath");
})
.thenAccept(this.extensionGuard::initialize)
.thenRun(() -> this.notifyProgress(token, "Processing Legend code on workspace to figure out the features you want..."))
.thenRun(this.extensionGuard.wrapOnClasspath(this::reprocessDocuments))
.thenCompose(_void ->
{
this.notifyProgress(token, "Almost there. Asking the IDE to refresh for all the goodies we can provide...");
LanguageClient languageClient = this.getLanguageClient();
return CompletableFuture.allOf(
languageClient.refreshCodeLenses(),
languageClient.refreshDiagnostics(),
languageClient.refreshInlayHints(),
languageClient.refreshInlineValues(),
languageClient.refreshSemanticTokens()
);
})
.whenComplete((_void, exp) ->
{
if (exp != null)
{
LOGGER.error("Failed during post-initialization", exp);
logErrorToClient("Failed during post-initialization: " + exp.getMessage());
this.notifyEnd(token, "We are sorry, something went wrong while initializing. Check logs for details.");
}
else
{
this.notifyEnd(token, "Legend extension is ready!");
}
})
);
}

private void reprocessDocuments()
Expand Down Expand Up @@ -628,6 +647,17 @@ Either<String, Integer> newProgressToken()
return Either.forRight(this.progressId.getAndIncrement());
}

CompletableFuture<Either<String, Integer>> startProgress(String title, String message)
{
Either<String, Integer> token = this.newProgressToken();
return this.getLanguageClient().createProgress(new WorkDoneProgressCreateParams(token))
.thenApply(_void ->
{
this.notifyBegin(token, message, title);
return token;
});
}

Either<String, Integer> possiblyNewProgressToken(Either<String, Integer> token)
{
return (token == null) ? newProgressToken() : token;
Expand Down Expand Up @@ -657,7 +687,7 @@ void notifyBegin(Either<String, Integer> token, String message, String title)
notifyProgress(token, begin);
}

void notifyProgress(Either<String, Integer> token, String message)
public void notifyProgress(Either<String, Integer> token, String message)
{
WorkDoneProgressReport report = new WorkDoneProgressReport();
if (message != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.finos.legend.engine.ide.lsp.DummyLanguageClient;
import org.finos.legend.engine.ide.lsp.classpath.ClasspathUsingMavenFactory;
import org.finos.legend.engine.ide.lsp.server.LegendLanguageServer;
Expand Down Expand Up @@ -56,7 +57,7 @@ void loadJarsFromPom(@TempDir Path tempDir) throws Exception

ClasspathUsingMavenFactory factory = new ClasspathUsingMavenFactory(pom.toFile());
factory.initialize(server);
ClassLoader classLoader = factory.create(Collections.emptyList()).get();
ClassLoader classLoader = factory.create(Collections.emptyList(), Either.forLeft("")).get();

try (URLClassLoader urlClassLoader = Assertions.assertInstanceOf(URLClassLoader.class, classLoader))
{
Expand Down
Loading