From 1d99aaff0f58b7539a8249edbe915a37f13d9e77 Mon Sep 17 00:00:00 2001 From: azerr Date: Tue, 12 Sep 2023 18:09:49 +0200 Subject: [PATCH] fix: Set project as workspace folder Fixes #1155 Signed-off-by: azerr --- .../devtools/intellij/lsp4ij/LSPIJUtils.java | 18 ++++- .../lsp4ij/LanguageServerWrapper.java | 81 ++++++++++++++----- .../lsp4ij/client/LanguageClientImpl.java | 15 ++-- .../lsp4ij/internal/SupportedFeatures.java | 3 +- .../format/QuteFileIndentOptionsProvider.java | 7 +- .../intellij/qute/lsp/QuteLanguageClient.java | 32 ++------ .../qute/psi/utils/PsiQuteProjectUtils.java | 2 +- 7 files changed, 96 insertions(+), 62 deletions(-) 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 bb36680e2..5adb53889 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java @@ -14,7 +14,9 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiDocumentManager; @@ -196,19 +198,27 @@ public static Position toPosition(int offset, Document document) { } @Nonnull - public static WorkspaceFolder toWorkspaceFolder(@Nonnull Module project) { + public static WorkspaceFolder toWorkspaceFolder(@Nonnull Project project) { WorkspaceFolder folder = new WorkspaceFolder(); - folder.setUri(toUri(project).toString()); + folder.setUri(toUri(project).toASCIIString()); folder.setName(project.getName()); return folder; } - public static URI toUri(Module project) { - File file = new File(project.getModuleFilePath()).getParentFile(); + public static URI toUri(Module module) { + VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots(); + if (roots.length > 0) { + return toUri(roots[0]); + } + File file = new File(module.getModuleFilePath()).getParentFile(); return file.toURI(); } public static URI toUri(Project project) { + VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots(); + if (roots.length > 0) { + return toUri(roots[0]); + } File file = new File(project.getProjectFilePath()).getParentFile(); return file.toURI(); } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java index aaef7abc1..e82c696f6 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java @@ -20,7 +20,6 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.*; @@ -75,7 +74,7 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f try { // Remove the cached file wrapper if needed LSPVirtualFileWrapper.dispose(file); - // Disconnect the given file from all language servers + // Disconnect the given file from the current language servers disconnect(uri, !isDisposed()); } catch (Exception e) { LOGGER.warn("Error while disconnecting the file '" + uri + "' from all language servers", e); @@ -83,41 +82,85 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f } } + + @Override + public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { + if (event.getPropertyName().equals(VirtualFile.PROP_NAME) && event.getOldValue() instanceof String) { + // A file (Test1.java) has been renamed (to Test2.java) by using Refactor / Rename from IJ + + // 1. Send a textDocument/didClose for the renamed file (Test1.java) + URI oldFileUri = didClose(event.getFile().getParent(), (String) event.getOldValue()); + URI newFileUri = LSPIJUtils.toUri(event.getFile()); + // 2. Send a workspace/didChangeWatchedFiles + didChangeWatchedFiles(fe(oldFileUri, FileChangeType.Deleted), + fe(newFileUri, FileChangeType.Created)); + } + } + @Override public void contentsChanged(@NotNull VirtualFileEvent event) { - // Manage textDocument/didSave URI uri = LSPIJUtils.toUri(event.getFile()); if (uri != null) { DocumentContentSynchronizer documentListener = connectedDocuments.get(uri); if (documentListener != null) { + // 1. Send a textDocument/didSave for the saved file documentListener.documentSaved(); } + // 2. Send a workspace/didChangeWatchedFiles + didChangeWatchedFiles(fe(uri, FileChangeType.Changed)); } } @Override - public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { - if (event.getPropertyName().equals(VirtualFile.PROP_NAME) && event.getOldValue() instanceof String) { - // A file (Test1.java) has been renamed (to Test2.java) by using Refactor / Rename from IJ - // Send a didClose for the renamed file (Test1.java) - didCloseOldFile(event.getFile().getParent(), (String) event.getOldValue()); + public void fileCreated(@NotNull VirtualFileEvent event) { + URI uri = LSPIJUtils.toUri(event.getFile()); + if (uri != null) { + // 2. Send a workspace/didChangeWatchedFiles + didChangeWatchedFiles(fe(uri, FileChangeType.Created)); + } + } + + @Override + public void fileDeleted(@NotNull VirtualFileEvent event) { + URI uri = LSPIJUtils.toUri(event.getFile()); + if (uri != null) { + // 2. Send a workspace/didChangeWatchedFiles + didChangeWatchedFiles(fe(uri, FileChangeType.Deleted)); } } @Override public void fileMoved(@NotNull VirtualFileMoveEvent event) { // A file (foo.Test1.java) has been moved (to bar1.Test1.java) - // Send a didClose for the moved file (foo.Test1.java) - didCloseOldFile(event.getOldParent(), event.getFileName()); + + // 1. Send a textDocument/didClose for the moved file (foo.Test1.java) + URI oldFileUri = didClose(event.getOldParent(), event.getFileName()); + URI newFileUri = LSPIJUtils.toUri(event.getFile()); + // 2. Send a workspace/didChangeWatchedFiles + didChangeWatchedFiles(fe(oldFileUri, FileChangeType.Deleted), + fe(newFileUri, FileChangeType.Created)); + } + + private FileEvent fe(URI uri, FileChangeType type) { + return new FileEvent(uri.toASCIIString(), type); } - private void didCloseOldFile(VirtualFile virtualParentFile, String fileName) { + private @NotNull URI didClose(VirtualFile virtualParentFile, String fileName) { File parent = VfsUtilCore.virtualToIoFile(virtualParentFile); URI uri = LSPIJUtils.toUri(new File(parent, fileName)); DocumentContentSynchronizer documentListener = connectedDocuments.get(uri); if (documentListener != null) { disconnect(uri, false); } + return uri; + } + + private void didChangeWatchedFiles(FileEvent... changes) { + LanguageServerWrapper.this.sendNotification(ls -> { + DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(changes)); + ls.getWorkspaceService() + .didChangeWatchedFiles(params); + }); } } @@ -129,8 +172,6 @@ private void didCloseOldFile(VirtualFile virtualParentFile, String fileName) { @Nullable protected final Project initialProject; @Nonnull - protected final Set allWatchedProjects; - @Nonnull protected Map connectedDocuments; @Nullable protected final URI initialPath; @@ -184,7 +225,6 @@ private LanguageServerWrapper(@Nullable Project project, @Nonnull LanguageServer @Nullable URI initialPath) { this.initialProject = project; this.initialPath = initialPath; - this.allWatchedProjects = new HashSet<>(); this.serverDefinition = serverDefinition; this.connectedDocuments = new HashMap<>(); String projectName = (project != null && project.getName() != null && !serverDefinition.isSingleton) ? ("@" + project.getName()) : ""; //$NON-NLS-1$//$NON-NLS-2$ @@ -207,11 +247,6 @@ private LanguageServerWrapper(@Nullable Project project, @Nonnull LanguageServer } } - @Nonnull - public Set getAllWatchedProjects() { - return allWatchedProjects; - } - public Project getProject() { return initialProject; } @@ -334,7 +369,6 @@ public synchronized void start() throws LanguageServerException { currentConnectionProvider.handleMessage(message, this.languageServer, rootURI); } }); - // initParams.setWorkspaceFolders(getRelevantWorkspaceFolders()); Launcher launcher = serverDefinition.createLauncherBuilder() // .setLocalService(languageClient)// .setRemoteInterface(serverDefinition.getServerInterface())// @@ -422,6 +456,11 @@ private CompletableFuture initServer(final URI rootURI) { initParams.setClientInfo(getClientInfo()); initParams.setTrace(this.lspStreamProvider.getTrace(rootURI)); + if (initialProject != null) { + var folders = Arrays.asList(LSPIJUtils.toWorkspaceFolder(initialProject)); + initParams.setWorkspaceFolders(folders); + } + // no then...Async future here as we want this chain of operation to be sequential and "atomic"-ish return languageServer.initialize(initParams); } @@ -675,7 +714,7 @@ CompletableFuture connect(Document document) throws IOException * @since 0.5 */ public boolean canOperate(Project project) { - if (project != null && (project.equals(this.initialProject) || this.allWatchedProjects.contains(project))) { + if (project != null && project.equals(this.initialProject)) { return true; } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java index 64b5c9274..327faff3c 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java @@ -2,7 +2,6 @@ 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.util.Disposer; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; @@ -13,7 +12,8 @@ import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -95,11 +95,12 @@ public CompletableFuture unregisterCapability(UnregistrationParams params) @Override public CompletableFuture> workspaceFolders() { - List res = new ArrayList<>(wrapper.getAllWatchedProjects().size()); - for (final Module project : wrapper.getAllWatchedProjects()) { - res.add(LSPIJUtils.toWorkspaceFolder(project)); + Project project = wrapper.getProject(); + if (project != null) { + List folders = Arrays.asList(LSPIJUtils.toWorkspaceFolder(project)); + return CompletableFuture.completedFuture(folders); } - return CompletableFuture.completedFuture(res); + return CompletableFuture.completedFuture(Collections.emptyList()); } @Override @@ -129,7 +130,7 @@ protected void triggerChangeConfiguration() { return; } Object settings = createSettings(); - if(settings == null) { + if (settings == null) { return; } DidChangeConfigurationParams params = new DidChangeConfigurationParams(settings); diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java index 9b62c2dc8..b6815a315 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java @@ -127,7 +127,7 @@ public class SupportedFeatures { workspaceClientCapabilities.setExecuteCommand(new ExecuteCommandCapabilities(Boolean.TRUE)); // TODO // workspaceClientCapabilities.setSymbol(new SymbolCapabilities(Boolean.TRUE)); - workspaceClientCapabilities.setWorkspaceFolders(Boolean.FALSE); + workspaceClientCapabilities.setWorkspaceFolders(Boolean.TRUE); WorkspaceEditCapabilities editCapabilities = new WorkspaceEditCapabilities(); editCapabilities.setDocumentChanges(Boolean.TRUE); // TODO @@ -136,6 +136,7 @@ public class SupportedFeatures { // TODO // editCapabilities.setFailureHandling(FailureHandlingKind.Undo); workspaceClientCapabilities.setWorkspaceEdit(editCapabilities); + workspaceClientCapabilities.setDidChangeWatchedFiles(new DidChangeWatchedFilesCapabilities(Boolean.TRUE)); return workspaceClientCapabilities; } diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java index 4e9f26aae..051363d6f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java @@ -16,6 +16,7 @@ package com.redhat.devtools.intellij.qute.lang.format; import com.intellij.lang.Language; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.FileViewProvider; @@ -40,7 +41,11 @@ public class QuteFileIndentOptionsProvider extends FileIndentOptionsProvider { public CommonCodeStyleSettings.@Nullable IndentOptions getIndentOptions(@NotNull CodeStyleSettings settings, @NotNull PsiFile file) { if (file.getFileType().equals(QuteFileType.QUTE)) { VirtualFile virtualFile = file.getVirtualFile(); - Project project = LSPIJUtils.getModule(virtualFile).getProject(); + Module module = LSPIJUtils.getModule(virtualFile); + if (module == null) { + return null; + } + Project project = module.getProject(); FileViewProvider provider = PsiManagerEx.getInstanceEx(project).findViewProvider(virtualFile); if (provider instanceof TemplateLanguageFileViewProvider) { Language language = ((TemplateLanguageFileViewProvider)provider).getTemplateDataLanguage(); 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 605972c03..0d339a4fa 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 @@ -22,45 +22,23 @@ import com.intellij.profile.ProfileChangeAdapter; import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey; -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.lsp4ij.client.IndexAwareLanguageClient; import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; -import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings; +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.qute.psi.QuteSupportForJava; import com.redhat.devtools.intellij.qute.psi.QuteSupportForTemplate; import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; import com.redhat.devtools.intellij.qute.settings.QuteInspectionsInfo; import com.redhat.devtools.intellij.qute.settings.UserDefinedQuteSettings; -import com.redhat.qute.commons.GenerateMissingJavaMemberParams; -import com.redhat.qute.commons.JavaTypeInfo; -import com.redhat.qute.commons.ProjectInfo; -import com.redhat.qute.commons.QuteJavaCodeLensParams; -import com.redhat.qute.commons.QuteJavaDefinitionParams; -import com.redhat.qute.commons.QuteJavaDiagnosticsParams; -import com.redhat.qute.commons.QuteJavaDocumentLinkParams; -import com.redhat.qute.commons.QuteJavaTypesParams; -import com.redhat.qute.commons.QuteJavadocParams; -import com.redhat.qute.commons.QuteProjectParams; -import com.redhat.qute.commons.QuteResolvedJavaTypeParams; -import com.redhat.qute.commons.ResolvedJavaTypeInfo; -import com.redhat.qute.commons.datamodel.DataModelParameter; -import com.redhat.qute.commons.datamodel.DataModelProject; -import com.redhat.qute.commons.datamodel.DataModelTemplate; -import com.redhat.qute.commons.datamodel.JavaDataModelChangeEvent; -import com.redhat.qute.commons.datamodel.QuteDataModelProjectParams; +import com.redhat.qute.commons.*; +import com.redhat.qute.commons.datamodel.*; import com.redhat.qute.commons.usertags.QuteUserTagParams; import com.redhat.qute.commons.usertags.UserTagInfo; import com.redhat.qute.ls.api.QuteLanguageClientAPI; import com.redhat.qute.ls.api.QuteLanguageServerAPI; -import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.DocumentLink; -import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.*; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java index 6befe65d2..246dcb61f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java @@ -33,7 +33,7 @@ private PsiQuteProjectUtils() { public static ProjectInfo getProjectInfo(Module javaProject) { String projectUri = getProjectURI(javaProject); - String templateBaseDir = LSPIJUtils.toUri(QuarkusModuleUtil.getModuleDirPath(javaProject).findFileByRelativePath(TEMPLATES_BASE_DIR)).toString(); + String templateBaseDir = LSPIJUtils.toUriAsString(QuarkusModuleUtil.getModuleDirPath(javaProject)) + TEMPLATES_BASE_DIR; return new ProjectInfo(projectUri, templateBaseDir); }