diff --git a/.github/workflows/IJ.yml b/.github/workflows/IJ.yml
index 759fba1d0..0da0da0d4 100644
--- a/.github/workflows/IJ.yml
+++ b/.github/workflows/IJ.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- IJ: [IC-2020.3.1, IC-2021.1, IC-2021.2, IC-2021.3, IC-2022.1, IC-2022.2, IC-2022.3, IC-2023.1]
+ IJ: [IC-2021.3, IC-2022.1, IC-2022.2, IC-2022.3, IC-2023.1]
steps:
- uses: actions/checkout@v2
diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java
index 9410adc21..26fa6faa0 100644
--- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java
@@ -144,7 +144,7 @@ public static Document getDocument(VirtualFile docFile) {
public static @Nullable Module getProject(VirtualFile file) {
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
- Module module = ReadAction.compute(() -> ProjectFileIndex.getInstance(project).getModuleForFile(file));
+ Module module = ReadAction.compute(() -> ProjectFileIndex.getInstance(project).getModuleForFile(file, false));
if (module != null) {
return module;
}
diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java
new file mode 100644
index 000000000..f1aba5976
--- /dev/null
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package com.redhat.devtools.intellij.lsp4mp4ij.classpath;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.ModuleListener;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.impl.libraries.LibraryEx;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.BulkFileListener;
+import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
+import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
+import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
+import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiTreeChangeAdapter;
+import com.intellij.psi.PsiTreeChangeEvent;
+import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils;
+import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Classpath resource changed listener used to track update of:
+ *
+ *
+ * - library has changed.
+ * - Java source file has changed.
+ * - microprofile-config.properties file has changed.
+ *
+ */
+class ClasspathResourceChangedListener extends PsiTreeChangeAdapter implements BulkFileListener, LibraryTable.Listener, ModuleListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ClasspathResourceChangedListener.class);
+
+ private final ClasspathResourceChangedManager manager;
+
+ private final Set modulesBeingEnsured = new HashSet<>();
+
+ ClasspathResourceChangedListener(ClasspathResourceChangedManager manager) {
+ this.manager = manager;
+ }
+
+ // Track modules changed
+
+ public CompletableFuture processModules() {
+ var overriders = manager.getOverriders();
+ if (overriders.isEmpty()) {
+ return CompletableFuture.completedFuture(null);
+ }
+ // Loop for each modules and process the load of libraries by using Classpath overrider.
+ return CompletableFuture.runAsync(() -> {
+ for (var module : ModuleManager.getInstance(manager.getProject()).getModules()) {
+ LOGGER.info("Calling ensure from processModules");
+ checkOverridedLibrary(module, true);
+ }
+ }, manager.getExecutor());
+ }
+
+ private CompletableFuture processModule(Module module) {
+ return checkOverridedLibrary(module, false);
+ }
+
+ private CompletableFuture checkOverridedLibrary(Module module, boolean sync) {
+ var overriders = manager.getOverriders();
+ if (modulesBeingEnsured.add(module)) {
+ if (sync) {
+ for (var overrider : overriders) {
+ overrider.overrideClasspath(module);
+ }
+ modulesBeingEnsured.remove(module);
+ return CompletableFuture.completedFuture(null);
+ } else {
+ return CompletableFuture.runAsync(() -> {
+ for (var overrider : overriders) {
+ overrider.overrideClasspath(module);
+ }
+ modulesBeingEnsured.remove(module);
+ }, manager.getExecutor());
+ }
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public void moduleAdded(@NotNull Project project, @NotNull Module module) {
+ moduleChanged(module);
+ }
+
+ @Override
+ public void moduleRemoved(@NotNull Project project, @NotNull Module module) {
+ moduleChanged(module);
+ }
+
+ private void moduleChanged(Module module) {
+ LOGGER.info("Calling ensure from moduleChanged for module " + module.getName());
+ var overriders = manager.getOverriders();
+ if (!overriders.isEmpty()) {
+ // A module has changed, process the load of libraries by using Classpath overrider.
+ checkOverridedLibrary(module, false);
+ }
+ }
+
+ // Track library changes
+
+ @Override
+ public void afterLibraryAdded(@NotNull Library newLibrary) {
+ handleLibraryUpdate(newLibrary);
+ }
+
+ @Override
+ public void afterLibraryRemoved(@NotNull Library library) {
+ handleLibraryUpdate(library);
+ }
+
+ private void handleLibraryUpdate(Library library) {
+ LOGGER.info("handleLibraryUpdate called " + library.getName());
+ var project = manager.getProject();
+
+ // Notify that a library has changed.
+ final var notifier = manager.getResourceChangedNotifier();
+ notifier.addLibrary(library);
+
+ // Process classpath overriders.
+ var overriders = manager.getOverriders();
+ if (overriders.isEmpty()) {
+ if (library instanceof LibraryEx && ((LibraryEx) library).getModule() != null) {
+ var module = ((LibraryEx) library).getModule();
+ processModule(module).thenRun(() -> {
+ project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).moduleUpdated(module);
+ });
+ } else {
+ processModules().thenRun(() -> {
+ notifier.addLibrary(library);
+ project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).modulesUpdated();
+ });
+ }
+ }
+ }
+
+ // Track Psi file changes
+
+ @Override
+ public void childAdded(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ @Override
+ public void childRemoved(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ @Override
+ public void childReplaced(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ @Override
+ public void childMoved(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ @Override
+ public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ @Override
+ public void propertyChanged(@NotNull PsiTreeChangeEvent event) {
+ handleChangedPsiTree(event);
+ }
+
+ private void handleChangedPsiTree(PsiTreeChangeEvent event) {
+ // A Psi file has been changed in the editor
+ PsiFile psiFile = event.getFile();
+ if (psiFile == null) {
+ return;
+ }
+ tryToAddSourceFile(psiFile.getVirtualFile(), true);
+ }
+
+ // Track file system changes
+
+ @Override
+ public void before(@NotNull List extends VFileEvent> events) {
+ for (VFileEvent event : events) {
+ boolean expectedEvent = (event instanceof VFileDeleteEvent);
+ if (expectedEvent) {
+ // A file has been deleted
+ // We need to track delete event in 'before' method because we need the project of the file (in after we loose this information).
+ tryToAddSourceFile(event.getFile(), false);
+ }
+ }
+ }
+
+ @Override
+ public void after(@NotNull List extends VFileEvent> events) {
+ for (VFileEvent event : events) {
+ boolean expectedEvent = (event instanceof VFileCreateEvent || event instanceof VFileContentChangeEvent);
+ if (expectedEvent) {
+ // A file has been created, updated
+ tryToAddSourceFile(event.getFile(), false);
+ }
+ }
+ }
+
+ private static boolean isJavaFile(VirtualFile file) {
+ return PsiMicroProfileProjectManager.isJavaFile(file);
+ }
+
+ private static boolean isConfigSource(VirtualFile file) {
+ return PsiMicroProfileProjectManager.isConfigSource(file);
+ }
+
+ private void tryToAddSourceFile(VirtualFile file, boolean checkExistingFile) {
+ if (checkExistingFile && (file == null || !file.exists())) {
+ // The file doesn't exist
+ return;
+ }
+ var project = manager.getProject();
+ if (!isJavaFile(file) && !isConfigSource(file)) {
+ return;
+ }
+ // The file is a Java file or microprofile-config.properties
+ Module module = LSPIJUtils.getProject(file);
+ if (module == null || module.isDisposed()) {
+ return;
+ }
+ // Notify that the file has changed
+ var notifier = manager.getResourceChangedNotifier();
+ notifier.addSourceFile(Pair.pair(file, module));
+ }
+
+}
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
new file mode 100644
index 000000000..df73b720e
--- /dev/null
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package com.redhat.devtools.intellij.lsp4mp4ij.classpath;
+
+import com.intellij.ProjectTopics;
+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.project.Project;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.psi.PsiManager;
+import com.intellij.util.ConcurrencyUtil;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.messages.Topic;
+import com.intellij.workspaceModel.ide.WorkspaceModelTopics;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Classpath resource change manager provides the capability to track update of libraries changed and Java, microprofile-config properties files
+ * by any component by registering a listener {@link Listener}.
+ *
+ *
+ * ClasspathResourceChangeManager.Listener myListener = ...
+ * project.getMessageBus().connect(project).subscribe(ClasspathResourceChangeManager.TOPIC, myListener);
+ *
+ *
+ *
+ *
+ * - Track update of libraries is done with {@link WorkspaceModelTopics}.
+ * In other words {@link Listener#librariesChanged()} are fired when all libraries are inserted, deleted, updated.
+ * - Track update of Java, microprofile-config properties files are done when Java Psi file is updated, when Java file is created, deleted, saved.
+ *
+ */
+public class ClasspathResourceChangedManager implements Disposable {
+
+ public static final Topic TOPIC = Topic.create(ClasspathResourceChangedManager.class.getName(), ClasspathResourceChangedManager.Listener.class);
+
+ private final ExecutorService executor;
+ private final ClasspathResourceChangedNotifier resourceChangedNotifier;
+ private final MessageBusConnection projectConnection;
+ private final MessageBusConnection appConnection;
+ private final ClasspathResourceChangedListener listener;
+ private final List overriders;
+
+ public static ClasspathResourceChangedManager getInstance(Project project) {
+ return ServiceManager.getService(project, ClasspathResourceChangedManager.class);
+ }
+
+ public interface Listener {
+
+ void librariesChanged();
+
+ void sourceFilesChanged(Set> sources);
+
+ void moduleUpdated(Module module);
+
+ void modulesUpdated();
+ }
+
+ public interface ClasspathOverrider {
+
+ void overrideClasspath(Module module);
+
+ }
+
+ private final Project project;
+
+ public ClasspathResourceChangedManager(Project project) {
+ this.project = project;
+ this.overriders = new ArrayList<>();
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ this.executor = ConcurrencyUtil.newSameThreadExecutorService();
+ } else {
+ this.executor = new ThreadPoolExecutor(0, 1,
+ 1L, TimeUnit.MINUTES, new LinkedBlockingQueue(),
+ r -> new Thread(r, "Quarkus lib pool " + project.getName()));
+ }
+ // Send source files changed in debounce mode
+ this.resourceChangedNotifier = new ClasspathResourceChangedNotifier(project);
+ listener = new ClasspathResourceChangedListener(this);
+ projectConnection = project.getMessageBus().connect();
+ // Track end of Java libraries update
+ LibraryTablesRegistrar.getInstance().getLibraryTable(project).addListener(listener);
+ // Track update of Psi Java, properties files
+ PsiManager.getInstance(project).addPsiTreeChangeListener(listener, project);
+ // Track modules changes
+ projectConnection.subscribe(ProjectTopics.MODULES, listener);
+ // Track delete, create, update of file
+ appConnection = ApplicationManager.getApplication().getMessageBus().connect(project);
+ appConnection.subscribe(VirtualFileManager.VFS_CHANGES, listener);
+ processModules();
+ }
+
+ public void processModules() {
+ listener.processModules();
+ }
+
+ public void addClasspathOverrider(ClasspathOverrider overrider) {
+ overriders.add(overrider);
+ }
+
+ @Override
+ public void dispose() {
+ this.resourceChangedNotifier.dispose();
+ this.projectConnection.disconnect();
+ this.appConnection.disconnect();
+ LibraryTablesRegistrar.getInstance().getLibraryTable(project).removeListener(listener);
+ PsiManager.getInstance(project).removePsiTreeChangeListener(listener);
+ executor.shutdown();
+ }
+
+ Project getProject() {
+ return project;
+ }
+
+ ClasspathResourceChangedNotifier getResourceChangedNotifier() {
+ return resourceChangedNotifier;
+ }
+
+ ExecutorService getExecutor() {
+ return executor;
+ }
+
+ List getOverriders() {
+ return overriders;
+ }
+}
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
new file mode 100644
index 000000000..79852e884
--- /dev/null
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedNotifier.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package com.redhat.devtools.intellij.lsp4mp4ij.classpath;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.*;
+
+/**
+ * Source file change notifier with a debounce mode.
+ */
+public class ClasspathResourceChangedNotifier implements Disposable {
+
+ private static final long DEBOUNCE_DELAY = 1000;
+
+ private final Project project;
+
+ private Timer debounceTimer;
+ private TimerTask debounceTask;
+
+ private final Set> sourceFiles;
+ private boolean librariesChanged;
+
+ public ClasspathResourceChangedNotifier(Project project) {
+ this.project = project;
+ sourceFiles = new HashSet<>();
+ }
+
+ public synchronized void addLibrary(Library library) {
+ if (debounceTask != null) {
+ debounceTask.cancel();
+ }
+ librariesChanged = true;
+ asyncNotifyChanges();
+ }
+
+ public synchronized void addSourceFile(Pair pair) {
+ if (debounceTask != null) {
+ debounceTask.cancel();
+ }
+ synchronized (sourceFiles) {
+ sourceFiles.add(pair);
+ }
+
+ asyncNotifyChanges();
+ }
+
+ private void asyncNotifyChanges() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ notifyChanges();
+ } else {
+ debounceTask = new TimerTask() {
+ @Override
+ public void run() {
+ notifyChanges();
+ }
+ };
+
+ if (debounceTimer == null) {
+ debounceTimer = new Timer();
+ }
+
+ debounceTimer.schedule(debounceTask, DEBOUNCE_DELAY);
+ }
+ }
+
+ private void notifyChanges() {
+ synchronized (sourceFiles) {
+ project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).sourceFilesChanged(sourceFiles);
+ sourceFiles.clear();
+ }
+ if (librariesChanged) {
+ project.getMessageBus().syncPublisher(ClasspathResourceChangedManager.TOPIC).librariesChanged();
+ librariesChanged = false;
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (debounceTask != null) {
+ debounceTask.cancel();
+ }
+ if(debounceTimer != null) {
+ debounceTimer.cancel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java
index 5d6107144..4c7d03163 100644
--- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java
@@ -1,126 +1,153 @@
/*******************************************************************************
-* Copyright (c) 2020 Red Hat Inc. and others.
-* All rights reserved. This program and the accompanying materials
-* which accompanies this distribution, and is available at
-* https://www.eclipse.org/legal/epl-v20.html
-*
-* Contributors:
-* Red Hat Inc. - initial API and implementation
-*******************************************************************************/
+ * Copyright (c) 2020 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-v20.html
+ *
+ * Contributors:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project;
import com.intellij.ProjectTopics;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.components.Service;
+import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ModuleListener;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.newvfs.BulkFileListener;
-import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.messages.MessageBusConnection;
-import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl;
+import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* {@link PsiMicroProfileProject} manager.
- *
+ *
* @author Angelo ZERR
* @see https://github.com/redhat-developer/quarkus-ls/blob/master/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/core/project/JDTMicroProfileProjectManager.java
- *
*/
-@Service
-public final class PsiMicroProfileProjectManager {
-
- public static PsiMicroProfileProjectManager getInstance(Project project) {
- return ServiceManager.getService(project, PsiMicroProfileProjectManager.class);
- }
-
- private Project project;
-
- private final Map projects;
- private MicroProfileProjectListener microprofileProjectListener;
-
- private class MicroProfileProjectListener implements ModuleListener, BulkFileListener {
- @Override
- public void after(@NotNull List extends VFileEvent> events) {
- for(VFileEvent event : events) {
- if ((event instanceof VFileDeleteEvent || event instanceof VFileContentChangeEvent ||
- event instanceof VFileCreateEvent) && isConfigSource(event.getFile())) {
- Module javaProject = PsiUtilsLSImpl.getInstance(project).getModule(event.getFile());
- if (javaProject != null) {
- PsiMicroProfileProject mpProject = getJDTMicroProfileProject(javaProject);
- if (mpProject != null) {
- mpProject.evictConfigSourcesCache();
- }
- }
-
- }
- }
- }
-
- @Override
- public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) {
- evict(module);
- }
-
- private void evict(Module javaProject) {
- if (javaProject != null) {
- // Remove the JDTMicroProfile project instance from the cache.
- projects.remove(javaProject);
- }
- }
- }
-
- private PsiMicroProfileProjectManager(Project project) {
- this.project = project;
- this.projects = new HashMap<>();
- initialize();
- }
-
- public PsiMicroProfileProject getJDTMicroProfileProject(Module project) {
- return getJDTMicroProfileProject(project, true);
- }
-
- private PsiMicroProfileProject getJDTMicroProfileProject(Module project, boolean create) {
- Module javaProject = project;
- PsiMicroProfileProject info = projects.get(javaProject);
- if (info == null) {
- if (!create) {
- return null;
- }
- info = new PsiMicroProfileProject(javaProject);
- projects.put(javaProject, info);
- }
- return info;
- }
-
- public boolean isConfigSource(VirtualFile file) {
- String fileName = file.getName();
- for (IConfigSourceProvider provider : IConfigSourceProvider.EP_NAME.getExtensions()) {
- if (provider.isConfigSource(fileName)) {
- return true;
- }
- }
- return false;
- }
-
- public void initialize() {
- if (microprofileProjectListener != null) {
- return;
- }
- microprofileProjectListener = new MicroProfileProjectListener();
- MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect(project);
- connection.subscribe(VirtualFileManager.VFS_CHANGES, microprofileProjectListener);
- project.getMessageBus().connect(project).subscribe(ProjectTopics.MODULES, microprofileProjectListener);
- }
+public final class PsiMicroProfileProjectManager implements Disposable {
+
+ private static final String JAVA_FILE_EXTENSION = "java";
+
+ public static PsiMicroProfileProjectManager getInstance(Project project) {
+ return ServiceManager.getService(project, PsiMicroProfileProjectManager.class);
+ }
+
+ private final MessageBusConnection connection;
+
+ private final Project project;
+
+ private final Map projects;
+ private final MicroProfileProjectListener microprofileProjectListener;
+
+ private class MicroProfileProjectListener implements ModuleListener, ClasspathResourceChangedManager.Listener {
+
+ @Override
+ public void librariesChanged() {
+ // Do nothing
+ }
+
+ @Override
+ public void sourceFilesChanged(Set> sources) {
+ for (var pair : sources) {
+ VirtualFile file = pair.getFirst();
+ if (isConfigSource(file)) {
+ // A microprofile config file properties file source has been updated, evict the cache of the properties
+ Module javaProject = pair.getSecond();
+ PsiMicroProfileProject mpProject = getJDTMicroProfileProject(javaProject);
+ if (mpProject != null) {
+ mpProject.evictConfigSourcesCache();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void modulesUpdated() {
+ // Do nothing
+ }
+
+ @Override
+ public void moduleUpdated(Module module) {
+ // Do nothing
+ }
+
+ @Override
+ public void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) {
+ evict(module);
+ }
+
+ private void evict(Module javaProject) {
+ if (javaProject != null) {
+ // Remove the JDTMicroProfile project instance from the cache.
+ projects.remove(javaProject);
+ }
+ }
+ }
+
+ private PsiMicroProfileProjectManager(Project project) {
+ this.project = project;
+ this.projects = new HashMap<>();
+ microprofileProjectListener = new MicroProfileProjectListener();
+ connection = project.getMessageBus().connect(project);
+ connection.subscribe(ClasspathResourceChangedManager.TOPIC, microprofileProjectListener);
+ connection.subscribe(ProjectTopics.MODULES, microprofileProjectListener);
+ }
+
+ public PsiMicroProfileProject getJDTMicroProfileProject(Module project) {
+ return getJDTMicroProfileProject(project, true);
+ }
+
+ private PsiMicroProfileProject getJDTMicroProfileProject(Module project, boolean create) {
+ Module javaProject = project;
+ PsiMicroProfileProject info = projects.get(javaProject);
+ if (info == null) {
+ if (!create) {
+ return null;
+ }
+ info = new PsiMicroProfileProject(javaProject);
+ projects.put(javaProject, info);
+ }
+ return info;
+ }
+
+ /**
+ * Returns true if the given file is a MicroProfile config properties file (microprofile-config.properties, application.properties, application.yaml, etc) and false otherwise.
+ *
+ * @param file the file to check.
+ * @return true if the given file is a MicroProfile config properties file (microprofile-config.properties, application.properties, application.yaml, etc) and false otherwise.
+ */
+ public static boolean isConfigSource(VirtualFile file) {
+ if (file == null) {
+ return false;
+ }
+ String fileName = file.getName();
+ for (IConfigSourceProvider provider : IConfigSourceProvider.EP_NAME.getExtensions()) {
+ if (provider.isConfigSource(fileName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given file is a Java file and false otherwise.
+ *
+ * @param file the file to check.
+ * @return true if the given file is a Java file and false otherwise.
+ */
+ public static boolean isJavaFile(VirtualFile file) {
+ return file != null && JAVA_FILE_EXTENSION.equals(file.getExtension());
+ }
+
+ @Override
+ public void dispose() {
+ connection.disconnect();
+ }
}
diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java
index e47ba98cc..8b047716b 100644
--- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java
+++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java
@@ -79,10 +79,7 @@ public IPsiUtils refine(Module module) {
@Override
public Module getModule(VirtualFile file) {
- if (file != null && !project.isDisposed()) {
- return ProjectFileIndex.getInstance(project).getModuleForFile(file, false);
- }
- return null;
+ return LSPIJUtils.getProject(file);
}
@Override
diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java
index 38e3c0b82..475d28b68 100644
--- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java
+++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java
@@ -1,25 +1,23 @@
-/*******************************************************************************
- * Copyright (c) 2019-2020 Red Hat, Inc.
- * Distributed under license by Red Hat, Inc. All rights reserved.
- * This program is made available under the terms of the
- * Eclipse Public License v2.0 which accompanies this distribution,
- * and is available at https://www.eclipse.org/legal/epl-v20.html
- *
- * Contributors:
- * Red Hat, Inc. - initial API and implementation
- ******************************************************************************/
-package com.redhat.devtools.intellij.quarkus;
-
-import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleManager;
-import com.intellij.openapi.project.DumbAware;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.startup.StartupActivity;
-import org.jetbrains.annotations.NotNull;
-
-public class QuarkusPostStartupActivity implements StartupActivity, DumbAware {
- @Override
- public void runActivity(@NotNull Project project) {
- QuarkusProjectService.getInstance(project);
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2019-2020 Red Hat, Inc.
+ * Distributed under license by Red Hat, Inc. All rights reserved.
+ * This program is made available under the terms of the
+ * Eclipse Public License v2.0 which accompanies this distribution,
+ * and is available at https://www.eclipse.org/legal/epl-v20.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ ******************************************************************************/
+package com.redhat.devtools.intellij.quarkus;
+
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.startup.StartupActivity;
+import org.jetbrains.annotations.NotNull;
+
+public class QuarkusPostStartupActivity implements StartupActivity, DumbAware {
+ @Override
+ public void runActivity(@NotNull Project project) {
+ QuarkusProjectService.getInstance(project);
+ }
+}
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 a32ec7cfc..5c1f30bfd 100644
--- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java
+++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java
@@ -10,215 +10,64 @@
******************************************************************************/
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.ProcessCanceledException;
import com.intellij.openapi.project.DumbService;
-import com.intellij.openapi.project.ModuleListener;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectFileIndex;
-import com.intellij.openapi.roots.impl.libraries.LibraryEx;
-import com.intellij.openapi.roots.libraries.Library;
-import com.intellij.openapi.roots.libraries.LibraryTable;
-import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileManager;
-import com.intellij.openapi.vfs.newvfs.BulkFileListener;
-import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.testFramework.LightVirtualFile;
-import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.messages.MessageBusConnection;
-import com.intellij.util.messages.Topic;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.PropertiesManager;
-import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl;
+import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager;
import org.apache.commons.lang3.tuple.MutablePair;
-import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.lsp4mp.commons.ClasspathKind;
import org.eclipse.lsp4mp.commons.DocumentFormat;
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;
import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
+import java.util.*;
+import java.util.concurrent.*;
-public class QuarkusProjectService implements LibraryTable.Listener, BulkFileListener, ModuleListener, Disposable {
+public class QuarkusProjectService implements ClasspathResourceChangedManager.Listener, Disposable {
private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusProjectService.class);
- private final Project project;
-
private final Map> schemas = new ConcurrentHashMap<>();
- private final ExecutorService executor;
-
- private Set modulesBeingEnsured = new HashSet<>();
-
- @Override
- public void dispose() {
- connection.disconnect();
- executor.shutdown();
- }
-
- public interface Listener {
- void libraryUpdated(Library library);
- void sourceUpdated(List> sources);
- }
-
public static QuarkusProjectService getInstance(Project project) {
return ServiceManager.getService(project, QuarkusProjectService.class);
}
- public static final Topic TOPIC = Topic.create(QuarkusProjectService.class.getName(), Listener.class);
+ private final Project project;
private final MessageBusConnection connection;
public QuarkusProjectService(Project project) {
this.project = project;
- if (ApplicationManager.getApplication().isUnitTestMode()) {
- this.executor = ConcurrencyUtil.newSameThreadExecutorService();
- } else {
- this.executor = new ThreadPoolExecutor(0, 1,
- 1L, TimeUnit.MINUTES, new LinkedBlockingQueue(),
- r -> new Thread(r, "Quarkus lib pool " + project.getName()));
- }
- LibraryTablesRegistrar.getInstance().getLibraryTable(project).addListener(this, project);
- connection = ApplicationManager.getApplication().getMessageBus().connect(project);
- connection.subscribe(VirtualFileManager.VFS_CHANGES, this);
- project.getMessageBus().connect().subscribe(ProjectTopics.MODULES, this);
- processModules();
- }
-
- private CompletableFuture checkQuarkusLibrary(Module module, boolean sync) {
- if (modulesBeingEnsured.add(module)) {
- if (sync) {
- QuarkusModuleUtil.ensureQuarkusLibrary(module);
- modulesBeingEnsured.remove(module);
- return CompletableFuture.completedFuture(null);
- } else {
- return CompletableFuture.runAsync(() -> {
- QuarkusModuleUtil.ensureQuarkusLibrary(module);
- modulesBeingEnsured.remove(module);
- }, executor);
- }
- }
- return CompletableFuture.completedFuture(null);
- }
-
- public CompletableFuture processModules() {
- return CompletableFuture.runAsync(() -> {
- for (var module : ModuleManager.getInstance(project).getModules()) {
- LOGGER.info("Calling ensure from processModules");
- checkQuarkusLibrary(module, true);
- }
- }, executor);
- }
-
- private CompletableFuture processModule(Module module) {
- return checkQuarkusLibrary(module, false);
- }
-
- private void handleLibraryUpdate(Library library) {
- LOGGER.info("handleLibraryUpdate called " + library.getName());
- if (library instanceof LibraryEx && ((LibraryEx) library).getModule() != null) {
- var module = ((LibraryEx) library).getModule();
- processModule(module).thenRun(() -> {
- var pair = schemas.get(module);
- if (pair != null) {
- pair.setRight(Boolean.FALSE);
- }
- });
- } else {
- processModules().thenRun(() -> {
- project.getMessageBus().syncPublisher(TOPIC).libraryUpdated(library);
- schemas.forEach((module, pair) -> {
- pair.setRight(Boolean.FALSE);
+ ClasspathResourceChangedManager.getInstance(project)
+ .addClasspathOverrider(new ClasspathResourceChangedManager.ClasspathOverrider() {
+ @Override
+ public void overrideClasspath(Module module) {
+ QuarkusModuleUtil.ensureQuarkusLibrary(module);
+ }
});
- });
- }
- }
-
- @Override
- public void afterLibraryAdded(@NotNull Library newLibrary) {
- handleLibraryUpdate(newLibrary);
- }
-
- @Override
- public void afterLibraryRemoved(@NotNull Library library) {
- handleLibraryUpdate(library);
- }
-
- @Override
- public void after(@NotNull List extends VFileEvent> events) {
- List> pairs = events.stream()
- .map(event -> toPair(event))
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- if (!pairs.isEmpty()) {
- pairs.forEach(pair -> schemas.computeIfPresent(pair.getLeft(), (m, p) -> {
- p.setRight(Boolean.FALSE);
- return p;
- }));
- project.getMessageBus().syncPublisher(TOPIC).sourceUpdated(pairs);
- }
- }
-
- private Pair toPair(VFileEvent event) {
- VirtualFile file = event.getFile();
- if (file == null || !file.exists()) {
- return null;
- }
- boolean expectedEvent = (event instanceof VFileCreateEvent || event instanceof VFileContentChangeEvent || event instanceof VFileDeleteEvent);
- if (!expectedEvent) {
- return null;
- }
- // Here a file has been created, saved ot deleted
- Module module = ProjectFileIndex.getInstance(project).getModuleForFile(file);
- if (module == null || module.isDisposed()) {
- return null;
- }
- if (!isJavaFile(file) && !isConfigSource(file, project)) {
- return null;
- }
- // Here a java file or a microprofile-config.properties, application.properties has been changed
- return Pair.of(module, file);
- }
-
- private static boolean isJavaFile(VirtualFile file) {
- return "java".equalsIgnoreCase(file.getExtension());
- }
-
- private static boolean isConfigSource(VirtualFile file, Project project) {
- return PsiMicroProfileProjectManager.getInstance(project).isConfigSource(file);
+ connection = project.getMessageBus().connect();
+ connection.subscribe(ClasspathResourceChangedManager.TOPIC, this);
}
public VirtualFile getSchema(Module module) {
- MutablePair schemaEntry = schemas.get(module);
+ var schemaEntry = schemas.get(module);
if (schemaEntry == null || !schemaEntry.getRight()) {
- VirtualFile file = computeSchema(module, schemaEntry!=null?schemaEntry.getLeft():null);
+ VirtualFile file = computeSchema(module, schemaEntry != null ? schemaEntry.getLeft() : null);
if (file != null) {
if (schemaEntry != null) {
schemaEntry.setRight(Boolean.TRUE);
@@ -228,14 +77,13 @@ public VirtualFile getSchema(Module module) {
}
}
}
- return schemaEntry!=null?schemaEntry.getLeft():null;
+ 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) {
@@ -256,24 +104,46 @@ private VirtualFile computeSchema(Module module, VirtualFile schemaFile) {
});
});
return schemaFile;
- } catch (IOException| ProcessCanceledException e) {
+ } catch (IOException | ProcessCanceledException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
}
return null;
}
- private void moduleChanged(Module module) {
- LOGGER.info("Calling ensure from moduleChanged for module " + module.getName());
- checkQuarkusLibrary(module, false);
+ @Override
+ public void dispose() {
+ connection.disconnect();
}
@Override
- public void moduleAdded(@NotNull Project project, @NotNull Module module) {
- moduleChanged(module);
+ public void librariesChanged() {
+ // Do nothing
}
@Override
- public void moduleRemoved(@NotNull Project project, @NotNull Module module) {
- moduleChanged(module);
+ public void sourceFilesChanged(Set> sources) {
+ sources.forEach(pair -> schemas.computeIfPresent(pair.getSecond(), (m, p) -> {
+ p.setRight(Boolean.FALSE);
+ return p;
+ }));
+ }
+
+ @Override
+ public void moduleUpdated(Module module) {
+ var pair = schemas.get(module);
+ if (pair != null) {
+ pair.setRight(Boolean.FALSE);
+ }
+ }
+
+ @Override
+ public void modulesUpdated() {
+ schemas.forEach((module, pair) -> {
+ pair.setRight(Boolean.FALSE);
+ });
+ }
+
+ public void processModules() {
+ ClasspathResourceChangedManager.getInstance(project).processModules();
}
}
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 f288e462a..1eabd43c6 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
@@ -12,7 +12,7 @@
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBusConnection;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.ProjectLabelManager;
@@ -22,9 +22,8 @@
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl;
import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil;
-import com.redhat.devtools.intellij.quarkus.QuarkusProjectService;
import com.redhat.devtools.intellij.lsp4ij.IndexAwareLanguageClient;
-import org.apache.commons.lang3.tuple.Pair;
+import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4mp.commons.*;
import org.eclipse.lsp4mp.commons.codeaction.CodeActionResolveData;
@@ -41,21 +40,20 @@
import java.util.stream.Collectors;
-public class QuarkusLanguageClient extends IndexAwareLanguageClient implements MicroProfileLanguageClientAPI, QuarkusProjectService.Listener {
+public class QuarkusLanguageClient extends IndexAwareLanguageClient implements MicroProfileLanguageClientAPI, ClasspathResourceChangedManager.Listener {
private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusLanguageClient.class);
- private static final String JAVA_FILE_EXTENSION = "java";
private final MessageBusConnection connection;
public QuarkusLanguageClient(Project project) {
super(project);
connection = project.getMessageBus().connect(project);
- connection.subscribe(QuarkusProjectService.TOPIC, this);
- QuarkusProjectService.getInstance(project);
+ connection.subscribe(ClasspathResourceChangedManager.TOPIC, this);
}
@Override
public void dispose() {
+ super.dispose();
connection.disconnect();
}
@@ -70,31 +68,50 @@ private void sendPropertiesChangeEvent(List scope,
}
@Override
- public void libraryUpdated(Library library) {
+ public void librariesChanged() {
+ if (isDisposed()) {
+ // The language client has been disposed, ignore changes in libraries
+ return;
+ }
sendPropertiesChangeEvent(Collections.singletonList(MicroProfilePropertiesScope.dependencies), QuarkusModuleUtil.getModulesURIs(getProject()));
}
@Override
- public void sourceUpdated(List> sources) {
- List> info = sources.stream().
- filter(pair -> isJavaFile(pair.getRight()) || isConfigSource(pair.getRight(), pair.getLeft())).
- map(pair -> Pair.of(PsiUtilsLSImpl.getProjectURI(pair.getLeft()), getScope(pair.getRight()))).
+ public void sourceFilesChanged(Set> sources) {
+ if (isDisposed()) {
+ // The language client has been disposed, ignore changes in Java source / microprofile-config.properties files
+ return;
+ }
+ List> info = sources.stream()
+ .filter(pair -> isJavaFile(pair.getFirst()) || isConfigSource(pair.getFirst()))
+ .map(pair -> Pair.pair(PsiUtilsLSImpl.getProjectURI(pair.getSecond()), getScope(pair.getFirst()))).
collect(Collectors.toList());
if (!info.isEmpty()) {
- sendPropertiesChangeEvent(info.stream().map(Pair::getRight).collect(Collectors.toList()), info.stream().map(Pair::getLeft).collect(Collectors.toSet()));
+ sendPropertiesChangeEvent(info.stream().map(p -> p.getSecond()).collect(Collectors.toList()),
+ info.stream().map(p -> p.getFirst()).collect(Collectors.toSet()));
}
}
+ @Override
+ public void modulesUpdated() {
+ // Do nothing
+ }
+
+ @Override
+ public void moduleUpdated(Module module) {
+ // Do nothing
+ }
+
private MicroProfilePropertiesScope getScope(VirtualFile file) {
return isJavaFile(file)?MicroProfilePropertiesScope.sources:MicroProfilePropertiesScope.configfiles;
}
private boolean isJavaFile(VirtualFile file) {
- return JAVA_FILE_EXTENSION.equals(file.getExtension());
+ return PsiMicroProfileProjectManager.isJavaFile(file);
}
- private boolean isConfigSource(VirtualFile file, Module project) {
- return PsiMicroProfileProjectManager.getInstance(project.getProject()).isConfigSource(file);
+ private boolean isConfigSource(VirtualFile file) {
+ return PsiMicroProfileProjectManager.isConfigSource(file);
}
@Override
diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java
index 2c6c21452..2a9759a34 100644
--- a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java
+++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java
@@ -13,12 +13,14 @@
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBusConnection;
+import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl;
import com.redhat.devtools.intellij.quarkus.QuarkusProjectService;
import com.redhat.devtools.intellij.lsp4ij.IndexAwareLanguageClient;
+import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager;
import com.redhat.devtools.intellij.qute.psi.QuteSupportForJava;
import com.redhat.devtools.intellij.qute.psi.QuteSupportForTemplate;
import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils;
@@ -43,7 +45,6 @@
import com.redhat.qute.commons.usertags.UserTagInfo;
import com.redhat.qute.ls.api.QuteLanguageClientAPI;
import com.redhat.qute.ls.api.QuteLanguageServerAPI;
-import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.DocumentLink;
import org.eclipse.lsp4j.Location;
@@ -58,7 +59,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
-public class QuteLanguageClient extends IndexAwareLanguageClient implements QuteLanguageClientAPI, QuarkusProjectService.Listener {
+public class QuteLanguageClient extends IndexAwareLanguageClient implements QuteLanguageClientAPI, ClasspathResourceChangedManager.Listener {
private static final Logger LOGGER = LoggerFactory.getLogger(QuteLanguageClient.class);
private final MessageBusConnection connection;
@@ -66,7 +67,7 @@ public class QuteLanguageClient extends IndexAwareLanguageClient implements Qute
public QuteLanguageClient(Project project) {
super(project);
connection = project.getMessageBus().connect(project);
- connection.subscribe(QuarkusProjectService.TOPIC, this);
+ connection.subscribe(ClasspathResourceChangedManager.TOPIC, this);
QuarkusProjectService.getInstance(project);
}
@@ -82,7 +83,7 @@ public void dispose() {
*
* @param uris the project uris where the data model must be refreshed.
*/
- private void notifyDataModelChanged(Set uris) {
+ private void notifyQuteDataModelChanged(Set uris) {
QuteLanguageServerAPI server = (QuteLanguageServerAPI) getLanguageServer();
if (server != null) {
JavaDataModelChangeEvent event = new JavaDataModelChangeEvent();
@@ -92,31 +93,43 @@ private void notifyDataModelChanged(Set uris) {
}
@Override
- public void libraryUpdated(Library library) {
+ public void librariesChanged() {
if (isDisposed()) {
- // The language client has been disposed, ignore the changed of library
+ // The language client has been disposed, ignore changes in libraries
return;
}
Set uris = new HashSet<>();
uris.add(PsiQuteProjectUtils.getProjectURI(getProject()));
- notifyDataModelChanged(uris);
+ notifyQuteDataModelChanged(uris);
}
@Override
- public void sourceUpdated(List> sources) {
+ public void sourceFilesChanged(Set> sources) {
if (isDisposed()) {
- // The language client has been disposed, ignore the changed of Java source files
+ // The language client has been disposed, ignore changes in Java source files
return;
}
Set uris = sources.stream()
- .map(pair -> pair.getLeft())
- .map(module -> PsiQuteProjectUtils.getProjectURI(module))
+ // qute/dataModelChanged must be sent only if there are some Java files which are changed
+ .filter(pair -> PsiMicroProfileProjectManager.isJavaFile(pair.getFirst()))
+ .map(pair -> pair.getSecond())
+ .map(module -> PsiUtilsLSImpl.getProjectURI(module))
.collect(Collectors.toSet());
if (!uris.isEmpty()) {
- notifyDataModelChanged(uris);
+ notifyQuteDataModelChanged(uris);
}
}
+ @Override
+ public void modulesUpdated() {
+ // Do nothing
+ }
+
+ @Override
+ public void moduleUpdated(Module module) {
+ // Do nothing
+ }
+
@Override
public CompletableFuture getProjectInfo(QuteProjectParams params) {
return runAsBackground("getProjectInfo", monitor -> QuteSupportForTemplate.getInstance().getProjectInfo(params, PsiUtilsLSImpl.getInstance(getProject()), monitor));
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index a01b9591a..af0e3b947 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -235,7 +235,7 @@
-
+
@@ -264,6 +264,8 @@
+
+