diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java index fbff7dff7..e840d5a07 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java @@ -69,13 +69,10 @@ public interface Listener { private final Project project; - private final List preprocessors; - public ClasspathResourceChangedManager(Project project) { this.project = project; - this.preprocessors = new ArrayList<>(); // Send source files changed in debounce mode - this.resourceChangedNotifier = new ClasspathResourceChangedNotifier(project, preprocessors); + this.resourceChangedNotifier = new ClasspathResourceChangedNotifier(project); listener = new ClasspathResourceChangedListener(this); projectConnection = project.getMessageBus().connect(); // Track end of Java libraries update @@ -105,13 +102,4 @@ Project getProject() { ClasspathResourceChangedNotifier getResourceChangedNotifier() { return resourceChangedNotifier; } - - /** - * Add a preprocessor to update classpatch when a library changed before sending the {@link Listener#librariesChanged()} event. - * - * @param preprocessor the preprocessor to add. - */ - public void addPreprocessor(RunnableProgress preprocessor) { - preprocessors.add(preprocessor); - } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedNotifier.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedNotifier.java index a2cac68c1..dfb955f6a 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedNotifier.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedNotifier.java @@ -14,11 +14,9 @@ package com.redhat.devtools.intellij.lsp4mp4ij.classpath; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; @@ -27,10 +25,10 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.stream.Collectors; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; /** * Source file change notifier with a debounce mode. @@ -46,12 +44,10 @@ public class ClasspathResourceChangedNotifier implements Disposable { private final Set> sourceFiles; private boolean librariesChanged; - private final List processBeforeLibrariesChanged; private boolean disposed; - public ClasspathResourceChangedNotifier(Project project, List preprocessors) { + public ClasspathResourceChangedNotifier(Project project) { this.project = project; - this.processBeforeLibrariesChanged = preprocessors; sourceFiles = new HashSet<>(); } @@ -107,11 +103,7 @@ private void notifyChanges() { } if (librariesChanged) { // Java Libraries has changed - if (processBeforeLibrariesChanged.isEmpty() || ApplicationManager.getApplication().isUnitTestMode()) { - // No preprocessor or Test context, send directly the librariesChanged event. - for (var runnable : processBeforeLibrariesChanged) { - runnable.run(new EmptyProgressIndicator()); - } + if (ApplicationManager.getApplication().isUnitTestMode()) { // Send the libraries changed event project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).librariesChanged(); librariesChanged = false; @@ -125,9 +117,6 @@ public void run(@NotNull ProgressIndicator progressIndicator) { // Execute preprocessor progressIndicator.setIndeterminate(false); progressIndicator.checkCanceled(); - for (var runnable : processBeforeLibrariesChanged) { - runnable.run(progressIndicator); - } } finally { // Send the libraries changed event project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).librariesChanged(); @@ -152,7 +141,7 @@ public void dispose() { this.disposed = true; if (debounceTask != null) { debounceTask.cancel(); - debounceTask =null; + debounceTask = null; } if (debounceTimer != null) { debounceTimer.cancel(); diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java index 82edd8c72..381ab721e 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java @@ -65,7 +65,7 @@ public static boolean isQuarkusExtensionWithDeploymentArtifact(Library library) if (library != null) { VirtualFile[] files = library.getFiles(OrderRootType.CLASSES); - for(int i=0; !result && i < files.length;++i) { + for (int i = 0; !result && i < files.length; ++i) { if (files[i].isDirectory()) { result = ToolDelegate.getDeploymentJarId(VfsUtilCore.virtualToIoFile(files[i])) != null; } @@ -77,7 +77,7 @@ public static boolean isQuarkusExtensionWithDeploymentArtifact(Library library) /** * Check if the Quarkus library needs to be recomputed and update it if required. * - * @param module the module to check + * @param module the module to check * @param progressIndicator */ public static void ensureQuarkusLibrary(Module module, ProgressIndicator progressIndicator) { @@ -95,20 +95,28 @@ public static void ensureQuarkusLibrary(Module module, ProgressIndicator progres Integer actualHash = computeHash(module); var qlib = OrderEntryUtil.findLibraryOrderEntry(ModuleRootManager.getInstance(module), QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME); if (qlib == null || (actualHash != null && !actualHash.equals(previousHash)) || - !QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_VERSION.equals(component.getVersion())){ + !QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_VERSION.equals(component.getVersion())) { ModuleRootModificationUtil.updateModel(module, model -> { LibraryTable table = model.getModuleLibraryTable(); Library library = table.getLibraryByName(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME); while (library != null) { table.removeLibrary(library); - TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "removeLibrary"); + try { + TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "removeLibrary"); + } catch (Exception e) { + + } library = table.getLibraryByName(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME); } progressIndicator.checkCanceled(); progressIndicator.setText("Collecting Quarkus deployment dependencies..."); List[] files = toolDelegate.getDeploymentFiles(module, progressIndicator); LOGGER.info("Adding library to " + module.getName() + " previousHash=" + previousHash + " newHash=" + actualHash); - TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "addLibrary").send(); + try { + TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "addLibrary").send(); + } catch (Exception e) { + + } addLibrary(model, files, module); }); component.setHash(actualHash); @@ -120,7 +128,7 @@ public static void ensureQuarkusLibrary(Module module, ProgressIndicator progres } private static void addLibrary(ModifiableRootModel model, List[] files, Module module) { - LibraryEx library = (LibraryEx)model.getModuleLibraryTable().createLibrary(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME); + LibraryEx library = (LibraryEx) model.getModuleLibraryTable().createLibrary(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME); LibraryEx.ModifiableModelEx libraryModel = library.getModifiableModel(); for (VirtualFile rootFile : files[ToolDelegate.BINARY]) { @@ -145,14 +153,14 @@ private static Integer computeHash(Module module) { @Override public Set visitLibraryOrderEntry(@NotNull LibraryOrderEntry libraryOrderEntry, Set value) { if (!isQuarkusDeploymentLibrary(libraryOrderEntry) && isQuarkusExtensionWithDeploymentArtifact(libraryOrderEntry.getLibrary())) { - for(VirtualFile file : libraryOrderEntry.getFiles(OrderRootType.CLASSES)) { + for (VirtualFile file : libraryOrderEntry.getFiles(OrderRootType.CLASSES)) { value.add(file.getPath()); } } return value; } }, new HashSet<>()); - return files.isEmpty()?null:files.hashCode(); + return files.isEmpty() ? null : files.hashCode(); } /** @@ -174,7 +182,7 @@ public Boolean visitLibraryOrderEntry(@NotNull LibraryOrderEntry libraryOrderEnt } public static boolean isQuarkusLibrary(@NotNull LibraryOrderEntry libraryOrderEntry) { - return libraryOrderEntry.getLibraryName() != null && + return libraryOrderEntry.getLibraryName() != null && libraryOrderEntry.getLibraryName().contains(QuarkusConstants.QUARKUS_CORE_PREFIX); } @@ -204,9 +212,9 @@ public static boolean checkQuarkusVersion(Module module, Predicate pred .findFirst(); if (quarkusCoreJar.isPresent()) { Matcher quarkusCoreArtifactMatcher = QUARKUS_CORE_PATTERN.matcher(quarkusCoreJar.get().getName()); - if(quarkusCoreArtifactMatcher.matches()) { + if (quarkusCoreArtifactMatcher.matches()) { String quarkusVersion = quarkusCoreArtifactMatcher.group(1); - LOGGER.debug("Detected Quarkus version = " + quarkusVersion); + LOGGER.debug("Detected Quarkus version = " + quarkusVersion); Matcher quarkusVersionMatcher = QUARKUS_STANDARD_VERSIONING.matcher(quarkusVersion); return predicate.test(quarkusVersionMatcher); } else { @@ -219,7 +227,7 @@ public static boolean checkQuarkusVersion(Module module, Predicate pred public static Set getModulesURIs(Project project) { Set uris = new HashSet<>(); - for(Module module : ModuleManager.getInstance(project).getModules()) { + for (Module module : ModuleManager.getInstance(project).getModules()) { uris.add(PsiUtilsLSImpl.getProjectURI(module)); } return uris; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java index 97636622c..32a8649a5 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java @@ -10,17 +10,14 @@ ******************************************************************************/ package com.redhat.devtools.intellij.quarkus; -import com.intellij.ProjectTopics; import com.intellij.json.JsonFileType; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.ModuleListener; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -35,7 +32,6 @@ import org.eclipse.lsp4mp.commons.MicroProfileProjectInfo; import org.eclipse.lsp4mp.commons.MicroProfilePropertiesScope; import org.eclipse.lsp4mp.utils.JSONSchemaUtils; -import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,112 +40,84 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -public class QuarkusProjectService implements ClasspathResourceChangedManager.Listener, Disposable, ModuleListener { - private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusProjectService.class); +public class QuarkusProjectService implements ClasspathResourceChangedManager.Listener, Disposable { + private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusProjectService.class); - private final Map> schemas = new ConcurrentHashMap<>(); + private final Map> schemas = new ConcurrentHashMap<>(); - public static QuarkusProjectService getInstance(Project project) { - return ServiceManager.getService(project, QuarkusProjectService.class); - } + public static QuarkusProjectService getInstance(Project project) { + return ServiceManager.getService(project, QuarkusProjectService.class); + } - private final Project project; + private final MessageBusConnection connection; - private final MessageBusConnection connection; + public QuarkusProjectService(Project project) { + connection = project.getMessageBus().connect(); + connection.subscribe(ClasspathResourceChangedManager.TOPIC, this); + } - public QuarkusProjectService(Project project) { - this.project = project; - connection = project.getMessageBus().connect(); - connection.subscribe(ClasspathResourceChangedManager.TOPIC, this); - connection.subscribe(ProjectTopics.MODULES, this); - // Add the Quarkus deployment preprocessor - ClasspathResourceChangedManager.getInstance(project) - .addPreprocessor( - (progressIndicator) -> { - processModules(progressIndicator); - }); - } - @Override - public void moduleAdded(@NotNull Project project, @NotNull Module module) { - QuarkusModuleUtil.ensureQuarkusLibrary(module, new EmptyProgressIndicator()); - } - - public VirtualFile getSchema(Module module) { - var schemaEntry = schemas.get(module); - if (schemaEntry == null || !schemaEntry.getRight()) { - VirtualFile file = computeSchema(module, schemaEntry != null ? schemaEntry.getLeft() : null); - if (file != null) { - if (schemaEntry != null) { - schemaEntry.setRight(Boolean.TRUE); - } else { - schemaEntry = new MutablePair<>(file, Boolean.TRUE); - schemas.put(module, schemaEntry); - } - } - } - return schemaEntry != null ? schemaEntry.getLeft() : null; - } - - private static VirtualFile createJSONSchemaFile(String name) throws IOException { - return new LightVirtualFile(name + "-schema.json", JsonFileType.INSTANCE, ""); - } - - private VirtualFile computeSchema(Module module, VirtualFile schemaFile) { - try { - if (schemaFile == null) { - schemaFile = createJSONSchemaFile(module.getName()); - } - final VirtualFile schemaFile1 = schemaFile; - DumbService.getInstance(module.getProject()).runWhenSmart(() -> { - ApplicationManager.getApplication().runWriteAction(() -> { - try { - MicroProfileProjectInfo info = PropertiesManager.getInstance().getMicroProfileProjectInfo(module, - MicroProfilePropertiesScope.SOURCES_AND_DEPENDENCIES, ClasspathKind.TEST, PsiUtilsLSImpl.getInstance(module.getProject()), - DocumentFormat.Markdown, new EmptyProgressIndicator()); - String schema = JSONSchemaUtils.toJSONSchema(info, false); - VfsUtil.saveText(schemaFile1, schema); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - }); - }); - return schemaFile; - } catch (IOException | ProcessCanceledException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return null; - } - - @Override - public void librariesChanged() { - // Update the JSON schema cache - schemas.forEach((module, pair) -> { - pair.setRight(Boolean.FALSE); - }); - } - - @Override - public void sourceFilesChanged(Set> sources) { - sources.forEach(pair -> schemas.computeIfPresent(pair.getSecond(), (m, p) -> { - p.setRight(Boolean.FALSE); - return p; - })); - } - - public void processModules(com.intellij.openapi.progress.ProgressIndicator progressIndicator) { - if (!project.isDisposed()) { - for (var module : ModuleManager.getInstance(project).getModules()) { - LOGGER.info("Calling ensure from processModules"); - QuarkusModuleUtil.ensureQuarkusLibrary(module, progressIndicator); + public VirtualFile getSchema(Module module) { + var schemaEntry = schemas.get(module); + if (schemaEntry == null || !schemaEntry.getRight()) { + VirtualFile file = computeSchema(module, schemaEntry != null ? schemaEntry.getLeft() : null); + if (file != null) { + if (schemaEntry != null) { + schemaEntry.setRight(Boolean.TRUE); + } else { + schemaEntry = new MutablePair<>(file, Boolean.TRUE); + schemas.put(module, schemaEntry); + } } } - } + return schemaEntry != null ? schemaEntry.getLeft() : null; + } + private static VirtualFile createJSONSchemaFile(String name) throws IOException { + return new LightVirtualFile(name + "-schema.json", JsonFileType.INSTANCE, ""); + } - @Override - public void dispose() { - connection.disconnect(); - } + private VirtualFile computeSchema(Module module, VirtualFile schemaFile) { + try { + if (schemaFile == null) { + schemaFile = createJSONSchemaFile(module.getName()); + } + final VirtualFile schemaFile1 = schemaFile; + DumbService.getInstance(module.getProject()).runWhenSmart(() -> ApplicationManager.getApplication().runWriteAction(() -> { + try { + MicroProfileProjectInfo info = PropertiesManager.getInstance().getMicroProfileProjectInfo(module, + MicroProfilePropertiesScope.SOURCES_AND_DEPENDENCIES, ClasspathKind.TEST, PsiUtilsLSImpl.getInstance(module.getProject()), + DocumentFormat.Markdown, new EmptyProgressIndicator()); + String schema = JSONSchemaUtils.toJSONSchema(info, false); + VfsUtil.saveText(schemaFile1, schema); + } catch (IOException e) { + LOGGER.warn(e.getLocalizedMessage(), e); + } + })); + return schemaFile; + } catch (IOException | ProcessCanceledException e) { + LOGGER.warn(e.getLocalizedMessage(), e); + } + return null; + } + + @Override + public void librariesChanged() { + // Update the JSON schema cache + schemas.forEach((module, pair) -> pair.setRight(Boolean.FALSE)); + } + + @Override + public void sourceFilesChanged(Set> sources) { + sources.forEach(pair -> schemas.computeIfPresent(pair.getSecond(), (m, p) -> { + p.setRight(Boolean.FALSE); + return p; + })); + } + + @Override + public void dispose() { + connection.disconnect(); + } } diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/ProgressIndicatorWrapper.java b/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/ProgressIndicatorWrapper.java new file mode 100644 index 000000000..adf57d289 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/ProgressIndicatorWrapper.java @@ -0,0 +1,116 @@ +package com.redhat.devtools.intellij.quarkus.deployment; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.impl.CoreProgressManager; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.NlsContexts; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ProgressIndicatorWrapper implements ProgressIndicator { + + private final ProgressIndicator progressIndicator; + + public ProgressIndicatorWrapper(ProgressIndicator progressIndicator) { + this.progressIndicator = progressIndicator; + + } + public void start() { + progressIndicator.start(); + } + + public void stop() { + progressIndicator.stop(); + } + + public boolean isRunning() { + return progressIndicator.isRunning(); + } + + public void cancel() { + progressIndicator.cancel(); + } + + public boolean isCanceled() { + return progressIndicator.isCanceled(); + } + + public void setText(@NlsContexts.ProgressText String text) { + progressIndicator.setText(text); + } + + @NlsContexts.ProgressText + public String getText() { + return progressIndicator.getText(); + } + + public void setText2(@NlsContexts.ProgressDetails String text) { + progressIndicator.setText2(text); + } + + @NlsContexts.ProgressDetails + public String getText2() { + return progressIndicator.getText2(); + } + + public double getFraction() { + return progressIndicator.getFraction(); + } + + public void setFraction(double fraction) { + progressIndicator.setFraction(fraction); + } + + public void pushState() { + progressIndicator.pushState(); + } + + public void popState() { + progressIndicator.popState(); + } + + public boolean isModal() { + return progressIndicator.isModal(); + } + + public @NotNull ModalityState getModalityState() { + return progressIndicator.getModalityState(); + } + + public void setModalityProgress(@Nullable ProgressIndicator modalityProgress) { + progressIndicator.setModalityProgress(modalityProgress); + } + + public boolean isIndeterminate() { + return progressIndicator.isIndeterminate(); + } + + public void setIndeterminate(boolean indeterminate) { + progressIndicator.setIndeterminate(indeterminate); + } + + public void checkCanceled() throws ProcessCanceledException { + progressIndicator.checkCanceled(); + if (isCanceled() /*&& isCancelable()*/) { + Throwable trace = getCancellationTrace(); + throw trace instanceof ProcessCanceledException ? (ProcessCanceledException)trace : new ProcessCanceledException(trace); + } + } + + protected @Nullable Throwable getCancellationTrace() { + return this instanceof Disposable ? Disposer.getDisposalTrace((Disposable)this) : null; + } + + public boolean isPopupWasShown() { + return progressIndicator.isPopupWasShown(); + } + + public boolean isShowing() { + return progressIndicator.isShowing(); + } + +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/QuarkusDeploymentSupport.java b/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/QuarkusDeploymentSupport.java new file mode 100644 index 000000000..0f08da627 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/deployment/QuarkusDeploymentSupport.java @@ -0,0 +1,135 @@ +package com.redhat.devtools.intellij.quarkus.deployment; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.impl.libraries.LibraryEx; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.messages.MessageBusConnection; +import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; +import com.redhat.devtools.intellij.quarkus.QuarkusConstants; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; + +public class QuarkusDeploymentSupport implements ClasspathResourceChangedManager.Listener, Disposable { + + private static final Key> QUARKUS_DEPLOYMENT_LOADER_KEY = new Key<>(QuarkusDeploymentSupport.class.getName()); + + private final List trackedModules; + + private final MessageBusConnection connection; + + public static QuarkusDeploymentSupport getInstance(Project project) { + return ServiceManager.getService(project, QuarkusDeploymentSupport.class); + } + + public QuarkusDeploymentSupport(Project project) { + this.trackedModules = new ArrayList<>(); + connection = project.getMessageBus().connect(project); + connection.subscribe(ClasspathResourceChangedManager.TOPIC, this); + } + + public CompletableFuture updateClasspathWithQuarkusDeployment(Module module) { + if (!trackedModules.contains(module)) { + trackedModules.add(module); + } + CompletableFuture loader = module.getUserData(QUARKUS_DEPLOYMENT_LOADER_KEY); + if (isOutOfDated(loader)) { + return updateClasspathWithQuarkusDeploymentSync(module); + } + return loader; + } + + private static boolean isOutOfDated(CompletableFuture loader) { + return loader == null /*|| loader.isCancelled() || loader.isCompletedExceptionally()*/; + } + + @NotNull + private synchronized static CompletableFuture updateClasspathWithQuarkusDeploymentSync(Module module) { + CompletableFuture loader = module.getUserData(QUARKUS_DEPLOYMENT_LOADER_KEY); + if (!isOutOfDated(loader)) { + return loader; + } + var project = module.getProject(); + final CompletableFuture future = new CompletableFuture<>(); + CompletableFuture.runAsync(() -> { + Runnable task = () -> ProgressManager.getInstance().run(new Task.Backgroundable(project, "Computing deployment jars...") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + System.out.println("Computing deployment jars..."); + try { + long start = System.currentTimeMillis(); + ProgressIndicator wrappedIndicator = new ProgressIndicatorWrapper(indicator) { + + @Override + public boolean isCanceled() { + return super.isCanceled() || future.isCancelled(); + } + }; + QuarkusModuleUtil.ensureQuarkusLibrary(module, wrappedIndicator); + long elapsed = System.currentTimeMillis() - start; + System.out.println("Ensured QuarkusLibrary in " + elapsed + " ms"); + future.complete(null); + } catch (CancellationException | ProcessCanceledException e) { + future.cancel(true); + } catch (Exception e) { + future.completeExceptionally(e); + } + } + }); + if (DumbService.getInstance(project).isDumb()) { + DumbService.getInstance(project).runWhenSmart(task); + } else { + task.run(); + } + }); + module.putUserData(QUARKUS_DEPLOYMENT_LOADER_KEY, future); + return future; + } + + @Override + public void dispose() { + connection.dispose(); + cancelFutures(); + } + + @Override + public void librariesChanged() { + cancelFutures(); + } + + private void cancelFutures() { + for (Module module : trackedModules) { + CompletableFuture loader = module.getUserData(QUARKUS_DEPLOYMENT_LOADER_KEY); + if (loader != null) { + loader.cancel(true); + module.putUserData(QUARKUS_DEPLOYMENT_LOADER_KEY, null); + } + } + } + + @Override + public void sourceFilesChanged(Set> sources) { + // Do nothing + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java index 282cc2e35..e96476ea5 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java @@ -33,6 +33,7 @@ import com.redhat.devtools.intellij.lsp4mp4ij.settings.MicroProfileInspectionsInfo; import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.quarkus.deployment.QuarkusDeploymentSupport; import org.eclipse.lsp4j.*; import org.eclipse.lsp4mp.commons.*; import org.eclipse.lsp4mp.commons.codeaction.CodeActionResolveData; @@ -41,6 +42,7 @@ import org.eclipse.lsp4mp.ls.api.MicroProfileLanguageServerAPI; import org.jetbrains.annotations.NotNull; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -57,6 +59,9 @@ public class QuarkusLanguageClient extends IndexAwareLanguageClient implements M public QuarkusLanguageClient(Project project) { super(project); + // Call Quarkus deployment support here to react on library changed (to evict quarkus deploiement cache) before + // sending an LSP microprofile/propertiesChanged notifications + QuarkusDeploymentSupport.getInstance(project); connection = project.getMessageBus().connect(project); connection.subscribe(ClasspathResourceChangedManager.TOPIC, this); inspectionsInfo = MicroProfileInspectionsInfo.getMicroProfileInspectionInfo(project); @@ -144,11 +149,31 @@ private boolean isConfigSource(VirtualFile file) { @Override public CompletableFuture getProjectInfo(MicroProfileProjectInfoParams params) { + IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject()); + VirtualFile file = null; + try { + file = utils.findFile(params.getUri()); + } catch (IOException e) { + throw new RuntimeException(e); + } + Module module = utils.getModule(file); + if (module == null) { + throw new RuntimeException(); + } + CompletableFuture loader = QuarkusDeploymentSupport.getInstance(getProject()).updateClasspathWithQuarkusDeployment(module); + if (loader.isDone()) { + return internalGetProjectInfo(params); + } + return loader + .thenCompose(unused -> internalGetProjectInfo(params)); + } + + private CompletableFuture internalGetProjectInfo(MicroProfileProjectInfoParams params) { var coalesceBy = new CoalesceByKey("microprofile/projectInfo", params.getUri(), params.getScopes()); String filePath = getFilePath(params.getUri()); return runAsBackground("Computing MicroProfile properties for '" + filePath + "'.", monitor -> - PropertiesManager.getInstance().getMicroProfileProjectInfo(params, PsiUtilsLSImpl.getInstance(getProject()), monitor) - , coalesceBy); + PropertiesManager.getInstance().getMicroProfileProjectInfo(params, PsiUtilsLSImpl.getInstance(getProject()), monitor) + , coalesceBy); } @Override diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java index d3b72a53e..37d0b3402 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java @@ -121,7 +121,7 @@ private void getDeploymentFiles(Module module, MavenProject mavenProject, List ensureDownloaded(Module module, MavenProject mavenPr } } else { List infos = deploymentIds.stream().map(id -> new MavenArtifactInfo(id, "jar", classifier)).collect(Collectors.toList()); - result = serverWrapper.resolveTransitively(infos, mavenProject.getRemoteRepositories()); + result = serverWrapper.resolveArtifactTransitively(infos, mavenProject.getRemoteRepositories()).mavenResolvedArtifacts; } } catch (MavenProcessCanceledException | RuntimeException e) { LOGGER.warn(e.getLocalizedMessage(), e); diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ea4f68ef0..5ef647863 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -332,6 +332,7 @@ +