From 2c57e1db09f05520191b6f431546b2374a8cb7b8 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 15 Sep 2023 15:42:44 -0400 Subject: [PATCH 01/15] Remove lsp4ij code and add a reference to the lsp4ij plugin. Signed-off-by: Paul Gooderham --- build.gradle | 28 +- .../lsp/LibertyConfigLanguageClient.java | 2 +- .../lsp/LibertyConfigLanguageServer.java | 3 +- .../liberty/lsp/LibertyXmlLanguageClient.java | 2 +- .../liberty/lsp/LibertyXmlServer.java | 2 +- .../lsp/JakartaLanguageClient.java | 4 +- .../lsp/JakartaLanguageServer.java | 2 +- .../lsp/MicroProfileLanguageClient.java | 6 +- .../lsp4mp/lsp/MicroProfileServer.java | 3 +- .../lsp4ij/AbstractLSPInlayProvider.java | 101 -- .../lsp4mp/lsp4ij/CompletableFutures.java | 58 -- ...umentToLanguageServerSetupParticipant.java | 55 -- ...ContentTypeToLanguageServerDefinition.java | 17 - .../lsp4ij/DocumentContentSynchronizer.java | 269 ------ .../intellij/lsp4mp/lsp4ij/LSPIJUtils.java | 332 ------- .../lsp4mp/lsp4ij/LSPVirtualFileWrapper.java | 117 --- .../lsp4mp/lsp4ij/LanguageClientImpl.java | 128 --- .../LanguageMappingExtensionPointBean.java | 24 - .../lsp4mp/lsp4ij/LanguageServerWrapper.java | 904 ------------------ .../lsp4ij/LanguageServersRegistry.java | 259 ----- .../lsp4ij/LanguageServiceAccessor.java | 628 ------------ .../LoggingStreamConnectionProviderProxy.java | 202 ---- .../lsp4ij/ServerExtensionPointBean.java | 51 - .../lsp4mp/lsp4ij/ServerMessageHandler.java | 83 -- .../command/internal/CommandExecutor.java | 268 ------ .../LSPCodeActionIntentionAction.java | 145 --- .../operations/codelens/LSPInlayProvider.java | 216 ----- .../completion/CompletionProposalTools.java | 198 ---- .../completion/LSCompletionProposal.java | 53 - .../completion/LSContentAssistProcessor.java | 128 --- .../LSIncompleteCompletionProposal.java | 450 --------- .../diagnostics/LSPDiagnosticAnnotator.java | 123 --- .../diagnostics/LSPDiagnosticHandler.java | 74 -- .../diagnostics/LSPDiagnosticsForServer.java | 266 ------ .../LSPInspectionToolProvider.java | 22 - .../diagnostics/LSPLocalInspectionTool.java | 112 --- .../operations/diagnostics/LSPPSiElement.java | 332 ------- .../diagnostics/LSPPsiReference.java | 71 -- .../highlight/LSPHighlightUsagesHandler.java | 47 - .../LSPHighlightUsagesHandlerFactory.java | 92 -- .../lsp4ij/operations/hover/LSPTextHover.java | 309 ------ .../inlayhint/LSPInlayHintInlayProvider.java | 162 ---- .../navigation/LSPGotoDeclarationHandler.java | 116 --- .../ProcessStreamConnectionProvider.java | 164 ---- .../server/StreamConnectionProvider.java | 122 --- .../intellij/lsp4mp/lsp4ij/ui/Messages.java | 74 -- .../command/MicroprofileOpenURIAction.java | 2 +- .../core/project/PsiMicroProfileProject.java | 2 +- .../psi/internal/core/ls/PsiUtilsLSImpl.java | 2 +- src/main/resources/META-INF/lsp.xml | 46 +- src/main/resources/META-INF/plugin.xml | 6 +- 51 files changed, 35 insertions(+), 6847 deletions(-) delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/AbstractLSPInlayProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/CompletableFutures.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ContentTypeToLanguageServerDefinition.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/DocumentContentSynchronizer.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPIJUtils.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPVirtualFileWrapper.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageClientImpl.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageMappingExtensionPointBean.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServerWrapper.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServersRegistry.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServiceAccessor.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LoggingStreamConnectionProviderProxy.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerExtensionPointBean.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerMessageHandler.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/command/internal/CommandExecutor.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codeactions/LSPCodeActionIntentionAction.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codelens/LSPInlayProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/CompletionProposalTools.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSCompletionProposal.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSContentAssistProcessor.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSIncompleteCompletionProposal.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPInspectionToolProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPLocalInspectionTool.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPSiElement.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPsiReference.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/hover/LSPTextHover.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/ProcessStreamConnectionProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/StreamConnectionProvider.java delete mode 100644 src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ui/Messages.java diff --git a/build.gradle b/build.gradle index bf31925b5..531dbff93 100644 --- a/build.gradle +++ b/build.gradle @@ -64,16 +64,25 @@ configurations { dependencies { // LSP4IJ dependencies - implementation 'org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:0.6.0' - implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.15.0' - implementation 'org.eclipse.lemminx:org.eclipse.lemminx:0.25.0' + implementation ('org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:0.6.0') { + exclude group: 'org.eclipse.lsp4j' + } +// implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.15.0' + implementation ('org.eclipse.lemminx:org.eclipse.lemminx:0.25.0') { + exclude group: 'org.eclipse.lsp4j' + } implementation 'io.openliberty.tools:liberty-langserver-lemminx:2.0' - implementation 'io.openliberty.tools:liberty-langserver:2.0' - implementation 'org.eclipse.lsp4jakarta:org.eclipse.lsp4jakarta.ls:0.1.1' + implementation ('io.openliberty.tools:liberty-langserver:2.0') { + exclude group: 'org.eclipse.lsp4j' + } + implementation ('org.eclipse.lsp4jakarta:org.eclipse.lsp4jakarta.ls:0.1.1') { + exclude group: 'org.eclipse.lsp4mp' + exclude group: 'org.eclipse.lsp4j' + } //required by lsp4j as the version from IJ is incompatible - implementation 'com.google.code.gson:gson:2.8.9' - implementation 'com.vladsch.flexmark:flexmark:0.50.50' - implementation 'org.jsoup:jsoup:1.15.3' +// implementation 'com.google.code.gson:gson:2.8.9' +// implementation 'com.vladsch.flexmark:flexmark:0.50.50' +// implementation 'org.jsoup:jsoup:1.15.3' //Add junit dependency back when tests are added //testImplementation group: 'junit', name: 'junit', version: '4.13.1' implementation 'org.apache.maven:maven-artifact:3.6.3' @@ -129,7 +138,8 @@ intellij { // For a full list of IntelliJ IDEA releases please see https://www.jetbrains.com/intellij-repository/releases version = '2023.1.2' - plugins = ['java', 'maven', 'gradle-java', 'properties', 'terminal', 'org.jetbrains.idea.maven', 'com.intellij.gradle'] + plugins = ['java', 'maven', 'gradle-java', 'properties', 'terminal', 'org.jetbrains.idea.maven', 'com.intellij.gradle', + file("../intellij-lsp-common-provider/lsp4ij/build/libs/lsp4ij-0.0.4.uber.jar")] updateSinceUntilBuild = false } diff --git a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageClient.java b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageClient.java index fa4c1813c..269fbaec4 100644 --- a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageClient.java +++ b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageClient.java @@ -15,7 +15,7 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; import io.openliberty.tools.intellij.lsp4mp.MicroProfileProjectService; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageClientImpl; +import org.microshed.lsp4ij.LanguageClientImpl; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageServer.java b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageServer.java index 2d24bff00..371d0bc3e 100644 --- a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageServer.java +++ b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyConfigLanguageServer.java @@ -13,7 +13,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.ProcessStreamConnectionProvider; +import org.microshed.lsp4ij.server.ProcessStreamConnectionProvider; import io.openliberty.tools.intellij.util.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +21,6 @@ import java.io.File; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlLanguageClient.java b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlLanguageClient.java index dd6ff38f2..0440a14ab 100644 --- a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlLanguageClient.java +++ b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlLanguageClient.java @@ -15,7 +15,7 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; import io.openliberty.tools.intellij.lsp4mp.MicroProfileProjectService; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageClientImpl; +import org.microshed.lsp4ij.LanguageClientImpl; import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lemminx.customservice.XMLLanguageClientAPI; import org.slf4j.Logger; diff --git a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlServer.java b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlServer.java index 9015d752d..309bb0c2b 100644 --- a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlServer.java +++ b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyXmlServer.java @@ -13,7 +13,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.ProcessStreamConnectionProvider; +import org.microshed.lsp4ij.server.ProcessStreamConnectionProvider; import io.openliberty.tools.intellij.util.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageClient.java b/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageClient.java index 0c91da297..9c64c25c2 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageClient.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageClient.java @@ -16,16 +16,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.JDTUtils; import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.PropertiesManagerForJakarta; import io.openliberty.tools.intellij.lsp4mp.MicroProfileProjectService; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageClientImpl; +import org.microshed.lsp4ij.LanguageClientImpl; import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures; import org.eclipse.lsp4jakarta.api.JakartaLanguageClientAPI; import org.eclipse.lsp4jakarta.commons.*; import org.slf4j.Logger; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageServer.java b/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageServer.java index 6c5b6ec70..098dff481 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageServer.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4jakarta/lsp/JakartaLanguageServer.java @@ -14,7 +14,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.ProcessStreamConnectionProvider; +import org.microshed.lsp4ij.server.ProcessStreamConnectionProvider; import io.openliberty.tools.intellij.util.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileLanguageClient.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileLanguageClient.java index bf20a2db3..601a873e0 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileLanguageClient.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileLanguageClient.java @@ -11,9 +11,6 @@ package io.openliberty.tools.intellij.lsp4mp.lsp; import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.VirtualFile; @@ -24,7 +21,7 @@ import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager; import io.openliberty.tools.intellij.lsp4mp.MicroProfileModuleUtil; import io.openliberty.tools.intellij.lsp4mp.MicroProfileProjectService; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageClientImpl; +import org.microshed.lsp4ij.LanguageClientImpl; import io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import org.apache.commons.lang3.tuple.Pair; import org.eclipse.lsp4j.CodeAction; @@ -44,7 +41,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; import java.util.stream.Collectors; /** diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileServer.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileServer.java index 1989276b3..5bff66223 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileServer.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp/MicroProfileServer.java @@ -13,8 +13,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; -import io.openliberty.tools.intellij.liberty.lsp.LibertyXmlServer; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.ProcessStreamConnectionProvider; +import org.microshed.lsp4ij.server.ProcessStreamConnectionProvider; import io.openliberty.tools.intellij.util.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/AbstractLSPInlayProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/AbstractLSPInlayProvider.java deleted file mode 100644 index 45bac1d6f..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/AbstractLSPInlayProvider.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.codeInsight.hints.ChangeListener; -import com.intellij.codeInsight.hints.ImmediateConfigurable; -import com.intellij.codeInsight.hints.InlayHintsProvider; -import com.intellij.codeInsight.hints.NoSettings; -import com.intellij.codeInsight.hints.SettingsKey; -import com.intellij.ide.DataManager; -import com.intellij.lang.Language; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.actionSystem.impl.SimpleDataContext; -import com.intellij.ui.layout.LCFlags; -import com.intellij.ui.layout.LayoutKt; -import org.eclipse.lsp4j.Command; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.JComponent; -import java.awt.Component; - -public abstract class AbstractLSPInlayProvider implements InlayHintsProvider { - public static final DataKey LSP_COMMAND = DataKey.create("io.openliberty.tools.intellij.lsp4mp.lsp4ij.command"); - - private SettingsKey key = new SettingsKey<>("LSP.hints"); - - @Override - public boolean isVisibleInSettings() { - return true; - } - - @NotNull - @Override - public SettingsKey getKey() { - return key; - } - - @NotNull - @Override - public String getName() { - return "LSP"; - } - - @Nullable - @Override - public String getPreviewText() { - return "Preview"; - } - - @NotNull - @Override - public ImmediateConfigurable createConfigurable(@NotNull NoSettings o) { - return new ImmediateConfigurable() { - @NotNull - @Override - public JComponent createComponent(@NotNull ChangeListener changeListener) { - return LayoutKt.panel(new LCFlags[0], "LSP", builder -> { - return null; - }); - } - }; - } - - @NotNull - @Override - public NoSettings createSettings() { - return new NoSettings(); - } - - @Override - public boolean isLanguageSupported(@NotNull Language language) { - return true; - } - - protected void executeClientCommand(Component source, Command command) { - if (command != null) { - AnAction action = ActionManager.getInstance().getAction(command.getCommand()); - if (action != null) { - DataContext context = SimpleDataContext.getSimpleContext(LSP_COMMAND, command, DataManager.getInstance().getDataContext(source)); - action.actionPerformed(new AnActionEvent(null, context, - ActionPlaces.UNKNOWN, new Presentation(), - ActionManager.getInstance(), 0)); - } - } - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/CompletableFutures.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/CompletableFutures.java deleted file mode 100644 index c298a4ff4..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/CompletableFutures.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import org.eclipse.lsp4j.jsonrpc.CancelChecker; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures.FutureCancelChecker; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -public class CompletableFutures { - - private CompletableFutures() { - - } - - /** - * It's a copy of - * {@link org.eclipse.lsp4j.jsonrpc.CompletableFutures#computeAsync} that - * accepts a function that returns a CompletableFuture. - * - * @see CompletableFutures#computeAsyncCompose(Function) - * - * @param the return type of the asynchronous computation - * @param code the code to run asynchronously - * @return a future that sends the correct $/cancelRequest notification when - * canceled - */ - public static CompletableFuture computeAsyncCompose( - Function> code) { - CompletableFuture start = new CompletableFuture<>(); - CompletableFuture result = start.thenComposeAsync(code); - start.complete(new FutureCancelChecker(result)); - return result; - } - - /** - * Returns true if the given {@link CompletableFuture} is done normally and false otherwise. - * - * @param future the completable future. - * - * @return true if the given {@link CompletableFuture} is done normally and false otherwise. - */ - public static boolean isDoneNormally(CompletableFuture future) { - return future != null && future.isDone() && !future.isCancelled() && !future.isCompletedExceptionally(); - } -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java deleted file mode 100644 index 53620b384..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManagerListener; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URI; - -/** - * Track file opened / closed to start language servers / disconnect file from language servers. - */ -public class ConnectDocumentToLanguageServerSetupParticipant implements ProjectComponent, FileEditorManagerListener { - - private static final Logger LOGGER = LoggerFactory.getLogger(ConnectDocumentToLanguageServerSetupParticipant.class); - - private Project project; - - public ConnectDocumentToLanguageServerSetupParticipant(Project project) { - this.project = project; - } - - @Override - public void projectOpened() { - project.getMessageBus().connect(project).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this); - } - - @Override - public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - // Force the start of all languages servers mapped with the given file - LanguageServiceAccessor.getInstance(source.getProject()) - .getLanguageServers(document, capabilities -> true); - } - } - -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ContentTypeToLanguageServerDefinition.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ContentTypeToLanguageServerDefinition.java deleted file mode 100644 index d0c187699..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ContentTypeToLanguageServerDefinition.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.lang.Language; - -import javax.annotation.Nonnull; -import java.util.AbstractMap; - -public class ContentTypeToLanguageServerDefinition extends AbstractMap.SimpleEntry { - public ContentTypeToLanguageServerDefinition(@Nonnull Language language, - @Nonnull LanguageServersRegistry.LanguageServerDefinition provider) { - super(language, provider); - } - - public boolean isEnabled() { - return true; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/DocumentContentSynchronizer.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/DocumentContentSynchronizer.java deleted file mode 100644 index bff4c7c30..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/DocumentContentSynchronizer.java +++ /dev/null @@ -1,269 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import org.eclipse.lsp4j.DidChangeTextDocumentParams; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; -import org.eclipse.lsp4j.DidSaveTextDocumentParams; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.TextDocumentContentChangeEvent; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentItem; -import org.eclipse.lsp4j.TextDocumentSyncKind; -import org.eclipse.lsp4j.TextDocumentSyncOptions; -import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -public class DocumentContentSynchronizer implements DocumentListener { - - /** - * Key for accessing / storing a set of DocumentContentSynchronizers as custom user data on a Document. - * This set is identical to the set of DocumentContentSynchronizers that have been registered as DocumentListeners - * and should be kept in synch with the addition and removal of listeners. This otherwise redundant set provides - * clients of the Document with a method for retrieving the DocumentContentSynchronizers which cannot be accessed - * through the native Document API (that provides no getDocumentListeners() method). - */ - public final static Key> KEY = Key.create(DocumentContentSynchronizer.class.getName()); - - private final static Logger LOGGER = LoggerFactory.getLogger(DocumentContentSynchronizer.class); - - private final @Nonnull - LanguageServerWrapper languageServerWrapper; - private final @Nonnull - Document document; - private final @Nonnull - URI fileUri; - private final TextDocumentSyncKind syncKind; - - private int version = 0; - private final List changeEvents; - private long modificationStamp; - final @Nonnull - CompletableFuture didOpenFuture; - - public DocumentContentSynchronizer(@Nonnull LanguageServerWrapper languageServerWrapper, - @Nonnull Document document, - TextDocumentSyncKind syncKind) { - this.languageServerWrapper = languageServerWrapper; - this.fileUri = LSPIJUtils.toUri(document); - this.modificationStamp = -1; - this.syncKind = syncKind != null ? syncKind : TextDocumentSyncKind.Full; - - this.document = document; - // add a document buffer - TextDocumentItem textDocument = new TextDocumentItem(); - textDocument.setUri(fileUri.toString()); - textDocument.setText(document.getText()); - - Language contentTypes = LSPIJUtils.getDocumentLanguage(this.document, languageServerWrapper.getProject()); - - String languageId = languageServerWrapper.getLanguageId(contentTypes); - - //TODO: determine languageId more precisely - /*IPath fromPortableString = Path.fromPortableString(this.fileUri.getPath()); - if (languageId == null) { - languageId = fromPortableString.getFileExtension(); - if (languageId == null) { - languageId = fromPortableString.lastSegment(); - } - }*/ - - textDocument.setLanguageId(languageId); - textDocument.setVersion(++version); - didOpenFuture = languageServerWrapper.getInitializedServer() - .thenAcceptAsync(ls -> ls.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(textDocument))); - - // Initialize LSP change events - changeEvents = new ArrayList<>(); - } - - @Override - public void documentChanged(DocumentEvent event) { - if (syncKind == TextDocumentSyncKind.None) { - return; - } - checkEvent(event); - if (syncKind == TextDocumentSyncKind.Full) { - synchronized (changeEvents) { - changeEvents.clear(); - changeEvents.add(createChangeEvent(event)); - } - } - - if (ApplicationManager.getApplication().isUnitTestMode()) { - sendDidChangeEvents(); - } else { - if(languageServerWrapper.getProject().isDisposed()) { - return; - } - PsiDocumentManager.getInstance(languageServerWrapper.getProject()).performForCommittedDocument(event.getDocument(), this::sendDidChangeEvents); - } - } - - // REVISIT: Is there a better way to force diagnostics to be computed than sending a change event when there are no changes? - public void documentFullRefresh(Document document) { - if (syncKind == TextDocumentSyncKind.None) { - return; - } - checkDocument(document); - - final TextDocumentContentChangeEvent changeEvent = new TextDocumentContentChangeEvent(); - changeEvent.setText(document.getText()); - final List events = Collections.singletonList(changeEvent); - - if (ApplicationManager.getApplication().isUnitTestMode()) { - sendDidChangeEvents(events); - } else { - if(languageServerWrapper.getProject().isDisposed()) { - return; - } - PsiDocumentManager.getInstance(languageServerWrapper.getProject()).performForCommittedDocument(document, () -> sendDidChangeEvents(events)); - } - } - - private void sendDidChangeEvents() { - List events = null; - synchronized (changeEvents) { - events = new ArrayList<>(changeEvents); - changeEvents.clear(); - } - sendDidChangeEvents(events); - } - - private void sendDidChangeEvents(List events) { - DidChangeTextDocumentParams changeParamsToSend = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(), events); - changeParamsToSend.getTextDocument().setUri(fileUri.toString()); - changeParamsToSend.getTextDocument().setVersion(++version); - languageServerWrapper.getInitializedServer() - .thenAcceptAsync(ls -> ls.getTextDocumentService().didChange(changeParamsToSend)); - } - - @Override - public void beforeDocumentChange(DocumentEvent event) { - checkEvent(event); - if (syncKind == TextDocumentSyncKind.Incremental) { - // this really needs to happen before event gets actually - // applied, to properly compute positions - synchronized (changeEvents) { - changeEvents.add(createChangeEvent(event)); - } - } - } - - private TextDocumentContentChangeEvent createChangeEvent(DocumentEvent event) { - TextDocumentSyncKind syncKind = getTextDocumentSyncKind(); - switch (syncKind) { - case None: - return null; - case Full: { - TextDocumentContentChangeEvent changeEvent = new TextDocumentContentChangeEvent(); - changeEvent.setText(event.getDocument().getText()); - return changeEvent; - } - case Incremental: { - TextDocumentContentChangeEvent changeEvent = new TextDocumentContentChangeEvent(); - CharSequence newText = event.getNewFragment(); - int offset = event.getOffset(); - int length = event.getOldLength(); - try { - // try to convert the Eclipse start/end offset to LS range. - Range range = new Range(LSPIJUtils.toPosition(offset, document), - LSPIJUtils.toPosition(offset + length, document)); - changeEvent.setRange(range); - changeEvent.setText(newText.toString()); - changeEvent.setRangeLength(length); - } catch (Exception e) { - // error while conversion (should never occur) - // set the full document text as changes. - changeEvent.setText(document.getText()); - } - return changeEvent; - } - } - return null; - } - - public void documentSaved(long timestamp) { - this.modificationStamp = timestamp; - ServerCapabilities serverCapabilities = languageServerWrapper.getServerCapabilities(); - if (serverCapabilities != null) { - Either textDocumentSync = serverCapabilities.getTextDocumentSync(); - if (textDocumentSync.isRight() && textDocumentSync.getRight().getSave() == null) { - return; - } - } - TextDocumentIdentifier identifier = new TextDocumentIdentifier(fileUri.toString()); - DidSaveTextDocumentParams params = new DidSaveTextDocumentParams(identifier, document.getText()); - languageServerWrapper.getInitializedServer().thenAcceptAsync(ls -> ls.getTextDocumentService().didSave(params)); - } - - public void documentClosed() { - // When LS is shut down all documents are being disconnected. No need to send "didClose" message to the LS that is being shut down or not yet started - if (languageServerWrapper.isActive()) { - TextDocumentIdentifier identifier = new TextDocumentIdentifier(fileUri.toString()); - DidCloseTextDocumentParams params = new DidCloseTextDocumentParams(identifier); - languageServerWrapper.getInitializedServer().thenAcceptAsync(ls -> ls.getTextDocumentService().didClose(params)); - } - } - - /** - * Returns the text document sync kind capabilities of the server and {@link TextDocumentSyncKind#Full} otherwise. - * - * @return the text document sync kind capabilities of the server and {@link TextDocumentSyncKind#Full} otherwise. - */ - private TextDocumentSyncKind getTextDocumentSyncKind() { - return syncKind; - } - - protected long getModificationStamp() { - return modificationStamp; - } - - public Document getDocument() { - return this.document; - } - - int getVersion() { - return version; - } - - private void logDocument(String header, Document document) { - LOGGER.warn(header + " text='" + document.getText()); - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - if (file != null) { - LOGGER.warn(header + " file=" + file); - } - } - - private void checkEvent(DocumentEvent event) { - checkDocument(event.getDocument()); - } - - private void checkDocument(Document eventDocument) { - if (this.document != eventDocument) { - logDocument("Listener document", this.document); - logDocument("Event document", eventDocument); - throw new IllegalStateException("Synchronizer should apply to only a single document, which is the one it was instantiated for"); //$NON-NLS-1$ - } - } - -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPIJUtils.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPIJUtils.java deleted file mode 100644 index 53a0747a8..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPIJUtils.java +++ /dev/null @@ -1,332 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.lang.LanguageUtil; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.application.WriteAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.fileEditor.FileDocumentManager; -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.util.TextRange; -import com.intellij.openapi.vfs.*; -import com.intellij.psi.PsiElement; -import org.apache.commons.lang.StringUtils; -import org.eclipse.lsp4j.CompletionParams; -import org.eclipse.lsp4j.CreateFile; -import org.eclipse.lsp4j.DeleteFile; -import org.eclipse.lsp4j.HoverParams; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.RenameFile; -import org.eclipse.lsp4j.ResourceOperation; -import org.eclipse.lsp4j.TextDocumentEdit; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.TextDocumentPositionParams; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.WorkspaceEdit; -import org.eclipse.lsp4j.WorkspaceFolder; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class LSPIJUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPIJUtils.class); - - @Nonnull - public static Language getFileLanguage(@Nonnull VirtualFile file, Project project) { - return ReadAction.compute(() -> LanguageUtil.getLanguageForPsi(project, file)); - } - - private static T toTextDocumentPositionParamsCommon(T param, int offset, Document document) { - URI uri = toUri(document); - Position start = toPosition(offset, document); - param.setPosition(start); - TextDocumentIdentifier id = new TextDocumentIdentifier(); - if (uri != null) { - id.setUri(uri.toString()); - } - param.setTextDocument(id); - return param; - } - - public static TextDocumentPositionParams toTextDocumentPosistionParams(int offset, Document document) { - return toTextDocumentPositionParamsCommon(new TextDocumentPositionParams(), offset, document); - } - - public static HoverParams toHoverParams(int offset, Document document) { - return toTextDocumentPositionParamsCommon(new HoverParams(), offset, document); - } - - public static URI toUri(File file) { - // URI scheme specified by language server protocol and LSP - try { - return new URI("file", "", file.getAbsoluteFile().toURI().getPath(), null); //$NON-NLS-1$ //$NON-NLS-2$ - } catch (URISyntaxException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return file.getAbsoluteFile().toURI(); - } - } - - public static URI toUri(VirtualFile file) { - return toUri(VfsUtilCore.virtualToIoFile(file)); - } - - public static URI toUri(Document document) { - VirtualFile file = getFile(document); - return file != null ? toUri(file) : null; - } - - public static VirtualFile getFile(Document document) { - return FileDocumentManager.getInstance().getFile(document); - } - - public static Document getDocument(VirtualFile docFile) { - return FileDocumentManager.getInstance().getDocument(docFile); - } - - public static Module getProject(VirtualFile file) { - for (Project project : ProjectManager.getInstance().getOpenProjects()) { - Module module = ReadAction.compute(() -> ProjectFileIndex.getInstance(project).getModuleForFile(file)); - if (module != null) { - return module; - } - } - return null; - } - - public static int toOffset(Position start, Document document) { - int lineStartOffset = document.getLineStartOffset(start.getLine()); - return lineStartOffset + start.getCharacter(); - } - - public static Position toPosition(int offset, Document document) { - int line = document.getLineNumber(offset); - int lineStart = document.getLineStartOffset(line); - String lineTextBeforeOffset = document.getText(new TextRange(lineStart, offset)); - int column = lineTextBeforeOffset.length(); - return new Position(line, column); - } - - @Nonnull - public static WorkspaceFolder toWorkspaceFolder(@Nonnull Module project) { - WorkspaceFolder folder = new WorkspaceFolder(); - folder.setUri(toUri(project).toString()); - folder.setName(project.getName()); - return folder; - } - - public static URI toUri(Module project) { - // Module.getModuleFilePath() is an internal only API - // File file = new File(project.getModuleFilePath()).getParentFile(); - VirtualFile[] roots = ModuleRootManager.getInstance(project).getContentRoots(); - if (roots.length > 0) { - return roots[0].toNioPath().toUri(); // choose one of the context roots - } - return URI.create("file:///"); // error return value //$NON-NLS-1$ - } - - public static void applyWorkspaceEdit(WorkspaceEdit edit) { - applyWorkspaceEdit(edit, null); - } - - public static void applyWorkspaceEdit(WorkspaceEdit edit, String label) { - if (edit.getDocumentChanges() != null) { - for(Either change : edit.getDocumentChanges()) { - if (change.isLeft()) { - VirtualFile file = findResourceFor(change.getLeft().getTextDocument().getUri()); - if (file != null) { - Document document = getDocument(file); - if (document != null) { - applyWorkspaceEdit(document, change.getLeft().getEdits()); - } - } - } else if (change.isRight()) { - ResourceOperation resourceOperation = change.getRight(); - if (resourceOperation instanceof CreateFile) { - try { - CreateFile createOperation = (CreateFile) resourceOperation; - URI targetURI = URI.create(createOperation.getUri()); - VirtualFile targetFile = VfsUtil.findFileByURL(targetURI.toURL()); - if (targetFile != null && createOperation.getOptions() != null) { - if (!createOperation.getOptions().getIgnoreIfExists()) { - Document document = getDocument(targetFile); - if (document != null) { - TextEdit textEdit = new TextEdit(new Range(toPosition(0, document), toPosition(document.getTextLength(), document)), ""); - applyWorkspaceEdit(document, Collections.singletonList(textEdit)); - } - } - } else { - try { - File f = new File(targetURI); - f.createNewFile(); - VfsUtil.findFileByIoFile(f, true); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - } catch (MalformedURLException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } else if (resourceOperation instanceof DeleteFile) { - try { - VirtualFile resource = findResourceFor(((DeleteFile) resourceOperation).getUri()); - if (resource != null) { - resource.delete(null); - } - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - } - } - } else if (edit.getChanges() != null) { - for(Map.Entry> change : edit.getChanges().entrySet()) { - VirtualFile file = findResourceFor(change.getKey()); - if (file != null) { - Document document = getDocument(file); - if (document != null) { - applyWorkspaceEdit(document, change.getValue()); - } - } - - } - - } - } - - private static void applyWorkspaceEdit(Document document, List edits) { - for(TextEdit edit : edits) { - if (edit.getRange() != null) { - String text = edit.getNewText(); - // compute start and end char offsets of the new Edit text - int start = toOffset(edit.getRange().getStart(), document); - int end = toOffset(edit.getRange().getEnd(), document); - if (StringUtils.isEmpty(text)) { - document.deleteString(start, end); - } else { - text = text.replaceAll("\r", ""); // removes carriage return - if (end >= 0) { - if (end - start <= 0) { - document.insertString(start, text); - } else { - document.replaceString(start, end, text); - } - } else if (start == 0) { - document.setText(text); - } else if (start > 0) { - document.insertString(start, text); - } - } - } - } - } - - - public static Language getDocumentLanguage(Document document, Project project) { - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - return getFileLanguage(file, project); - } - - public static VirtualFile findResourceFor(URI uri) { - return LocalFileSystem.getInstance().findFileByIoFile(Paths.get(uri).toFile()); - } - - public static VirtualFile findResourceFor(String uri) { - try { - return VfsUtil.findFileByURL(new URL(uri)); - } catch (MalformedURLException e) { - return null; - } - } - - public static Editor[] editorsForFile(VirtualFile file) { - Editor[] editors = new Editor[0]; - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - editors = editorsForFile(file, document); - } - return editors; - } - - public static Editor[] editorsForFile(VirtualFile file, Document document) { - Module module = LSPIJUtils.getProject(file); - return module!=null?EditorFactory.getInstance().getEditors(document, module.getProject()):new Editor[0]; - } - - public static Editor editorForFile(VirtualFile file) { - Editor[] editors = editorsForFile(file); - return editors.length > 0 ? editors[0] : null; - } - - public static Editor editorForElement(PsiElement element) { - if (element.getContainingFile() != null && element.getContainingFile().getVirtualFile() != null) { - return editorForFile(element.getContainingFile().getVirtualFile()); - } - return null; - } - - public static CompletionParams toCompletionParams(URI fileUri, int offset, Document document) { - Position start = toPosition(offset, document); - CompletionParams param = new CompletionParams(); - param.setPosition(start); - param.setTextDocument(toTextDocumentIdentifier(fileUri)); - return param; - } - - public static TextDocumentIdentifier toTextDocumentIdentifier(final URI uri) { - return new TextDocumentIdentifier(uri.toASCIIString()); - } - - public static void applyEdit(Editor editor, TextEdit textEdit, Document document) { - RangeMarker marker = document.createRangeMarker(LSPIJUtils.toOffset(textEdit.getRange().getStart(), document), LSPIJUtils.toOffset(textEdit.getRange().getEnd(), document)); - int startOffset = marker.getStartOffset(); - int endOffset = marker.getEndOffset(); - String text = textEdit.getNewText(); - if (text != null) { - text = text.replaceAll("\r", ""); - } - if (text == null || "".equals(text)) { - document.deleteString(startOffset, endOffset); - } else if (endOffset - startOffset <= 0) { - document.insertString(startOffset, text); - } else { - document.replaceString(startOffset, endOffset, text); - } - if (text != null && !"".equals(text)) { - editor.getCaretModel().moveCaretRelatively(text.length(), 0, false, false, true); - } - marker.dispose(); - } - - - public static void applyEdits(Editor editor, Document document, List edits) { - ApplicationManager.getApplication().runWriteAction(() -> edits.forEach(edit -> applyEdit(editor, edit, document))); - } - - public static boolean hasCapability(final Either eitherCapability) { - if(eitherCapability == null) { - return false; - } - return eitherCapability.isRight() || (eitherCapability.isLeft() && eitherCapability.getLeft()); - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPVirtualFileWrapper.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPVirtualFileWrapper.java deleted file mode 100644 index efd44cc99..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LSPVirtualFileWrapper.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * 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 - * Some piece of code has been inspired by https://github.com/ballerina-platform/lsp4intellij/blob/master/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java - *******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics.LSPDiagnosticsForServer; -import org.eclipse.lsp4j.Diagnostic; - -import java.util.List; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * LSP wrapper for VirtualFile which maintains the current diagnostics - * for all language servers mapped with this file. - * - * @author Angelo ZERR - */ -public class LSPVirtualFileWrapper { - - private static final Key KEY = new Key<>(LSPVirtualFileWrapper.class.getName()); - - private final VirtualFile file; - - private final Map diagnosticsPerServer; - - LSPVirtualFileWrapper(VirtualFile file) { - this.file = file; - this.diagnosticsPerServer = new HashMap<>(); - } - - public VirtualFile getFile() { - return file; - } - - // ------------------------ LSP Diagnostics - - /** - * Update the new published diagnostics for the given language server id. - * - * @param diagnostics the new diagnostics list - * @param languageServerWrapper the language server id which has published those diagnostics. - */ - public void updateDiagnostics(List diagnostics, LanguageServerWrapper languageServerWrapper) { - LSPDiagnosticsForServer diagnosticsForServer = diagnosticsPerServer.get(languageServerWrapper); - if (diagnosticsForServer == null) { - diagnosticsForServer = new LSPDiagnosticsForServer(languageServerWrapper, getFile()); - diagnosticsPerServer.put(languageServerWrapper, diagnosticsForServer); - } - diagnosticsForServer.update(diagnostics); - } - - /** - * Returns all current diagnostics reported by all language servers mapped with the file. - * - * @return all current diagnostics reported by all language servers mapped with the file. - */ - public Collection getAllDiagnostics() { - if (diagnosticsPerServer.isEmpty()) { - return Collections.emptyList(); - } - return diagnosticsPerServer.values(); - } - - public void dispose() { - this.diagnosticsPerServer.clear(); - } - - // ------------------------ Static accessor - - /** - * Returns the LSPVirtualFileWrapper for the given file. - * - * @param file the virtual file. - * @return the LSPVirtualFileWrapper for the given file. - */ - public static LSPVirtualFileWrapper getLSPVirtualFileWrapper(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - return wrapper; - } - return getOrCreateLSPVirtualFileWrapper(file); - } - - private static synchronized LSPVirtualFileWrapper getOrCreateLSPVirtualFileWrapper(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - return wrapper; - } - wrapper = new LSPVirtualFileWrapper(file); - file.putUserData(KEY, wrapper); - return wrapper; - } - - public static void dispose(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - wrapper.dispose(); - file.putUserData(KEY, null); - } - } -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageClientImpl.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageClientImpl.java deleted file mode 100644 index 1c4c4fff5..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageClientImpl.java +++ /dev/null @@ -1,128 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics.LSPDiagnosticHandler; -import org.eclipse.lsp4j.ApplyWorkspaceEditParams; -import org.eclipse.lsp4j.ApplyWorkspaceEditResponse; -import org.eclipse.lsp4j.MessageActionItem; -import org.eclipse.lsp4j.MessageParams; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.RegistrationParams; -import org.eclipse.lsp4j.ShowMessageRequestParams; -import org.eclipse.lsp4j.UnregistrationParams; -import org.eclipse.lsp4j.WorkspaceFolder; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class LanguageClientImpl implements LanguageClient { - private final Project project; - private Consumer diagnosticHandler; - - private LanguageServer server; - private LanguageServerWrapper wrapper; - - public LanguageClientImpl(Project project) { - this.project = project; - } - - public Project getProject() { - return project; - } - - public final void connect(LanguageServer server, LanguageServerWrapper wrapper) { - this.server = server; - this.wrapper = wrapper; - this.diagnosticHandler = new LSPDiagnosticHandler(wrapper); - } - - protected final LanguageServer getLanguageServer() { - return server; - } - - @Override - public void telemetryEvent(Object object) { - // TODO - } - - @Override - public final CompletableFuture showMessageRequest(ShowMessageRequestParams requestParams) { - return ServerMessageHandler.showMessageRequest(wrapper, requestParams); - } - - @Override - public final void showMessage(MessageParams messageParams) { - ServerMessageHandler.showMessage(wrapper.serverDefinition.label, messageParams); - } - - @Override - public final void publishDiagnostics(PublishDiagnosticsParams diagnostics) { - if (this.diagnosticHandler != null) { - this.diagnosticHandler.accept(diagnostics); - } - } - - @Override - public final void logMessage(MessageParams message) { - CompletableFuture.runAsync(() -> ServerMessageHandler.logMessage(wrapper, message)); - } - - @Override - public final CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { - CompletableFuture future = new CompletableFuture<>(); - ApplicationManager.getApplication().executeOnPooledThread(() -> { - LSPIJUtils.applyWorkspaceEdit(params.getEdit()); - future.complete(new ApplyWorkspaceEditResponse(true)); - }); - return future; - } - - @Override - public CompletableFuture registerCapability(RegistrationParams params) { - return CompletableFuture.runAsync(() -> wrapper.registerCapability(params)); - } - - @Override - public CompletableFuture unregisterCapability(UnregistrationParams params) { - return CompletableFuture.runAsync(() -> wrapper.unregisterCapability(params)); - } - - @Override - public CompletableFuture> workspaceFolders() { - List res = new ArrayList<>(wrapper.allWatchedProjects.size()); - for (final Module project : wrapper.allWatchedProjects) { - res.add(LSPIJUtils.toWorkspaceFolder(project)); - } - return CompletableFuture.completedFuture(res); - } - - protected CompletableFuture runAsBackground(String title, Supplier supplier) { - CompletableFuture future = new CompletableFuture<>(); - CompletableFuture.runAsync(() -> { - Runnable task = () -> ProgressManager.getInstance().runProcess(() -> { - try { - future.complete(supplier.get()); - } catch (Throwable t) { - future.completeExceptionally(t); - } - }, new EmptyProgressIndicator()); - if (DumbService.getInstance(getProject()).isDumb()) { - DumbService.getInstance(getProject()).runWhenSmart(task); - } else { - task.run(); - } - }); - return future; - } - -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageMappingExtensionPointBean.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageMappingExtensionPointBean.java deleted file mode 100644 index 982fb701b..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageMappingExtensionPointBean.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.openapi.extensions.AbstractExtensionPointBean; -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.util.xmlb.annotations.Attribute; - -public class LanguageMappingExtensionPointBean extends AbstractExtensionPointBean { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("open-liberty.intellij.languageMapping"); - - @Attribute("id") - public String id; - - @Attribute("language") - public String language; - - @Attribute("serverId") - public String serverId; - - /** - * Optional list of file patterns to narrow down the scope of the language server. - */ - @Attribute("filePattern") - public String filePattern; -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServerWrapper.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServerWrapper.java deleted file mode 100644 index dbad11006..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServerWrapper.java +++ /dev/null @@ -1,904 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 Red Hat, Inc. and others - * 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 - * IBM Corp - update start() and stop() - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.intellij.AppTopics; -import com.intellij.ProjectTopics; -import com.intellij.lang.Language; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileDocumentManagerListener; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.ModuleListener; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.messages.MessageBusConnection; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; -import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.URI; -import java.util.*; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.UnaryOperator; - -public class LanguageServerWrapper { - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServerWrapper.class);//$NON-NLS-1$ - private static final String CLIENT_NAME = "IntelliJ"; - - class Listener implements DocumentListener, FileDocumentManagerListener, FileEditorManagerListener { - @Override - public void documentChanged(@NotNull DocumentEvent event) { - URI uri = LSPIJUtils.toUri(event.getDocument()); - if (uri != null) { - DocumentContentSynchronizer documentListener = connectedDocuments.get(uri); - if (documentListener != null && documentListener.getModificationStamp() < event.getOldTimeStamp()) { - documentListener.documentSaved(event.getOldTimeStamp()); - } - } - } - - @Override - public void beforeDocumentSaving(@NotNull Document document) { - /*VirtualFile file = LSPIJUtils.getFile(document); - URI uri = LSPIJUtils.toUri(file); - if (uri != null) { - disconnect(uri); - }*/ - } - - @Override - public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - URI uri = LSPIJUtils.toUri(file); - if (uri != null) { - try { - // Remove the cached file wrapper if needed - LSPVirtualFileWrapper.dispose(file); - // Disconnect the given file from all language servers - disconnect(uri); - } catch (Exception e) { - LOGGER.warn("Error while disconnecting the file '" + uri + "' from all language servers", e); - } - } - } - } - - private Listener fileBufferListener = new Listener(); - private MessageBusConnection messageBusConnection = null; - - @Nonnull - public final LanguageServersRegistry.LanguageServerDefinition serverDefinition; - @Nullable - protected final Module initialProject; - @Nonnull - protected final Set allWatchedProjects; - @Nonnull - protected Map connectedDocuments; - @Nullable - protected final URI initialPath; - - protected StreamConnectionProvider lspStreamProvider; - private Future launcherFuture; - private CompletableFuture initializeFuture; - private LanguageServer languageServer; - private ServerCapabilities serverCapabilities; - private AtomicBoolean stopping = new AtomicBoolean(false); - - /** - * Map containing unregistration handlers for dynamic capability registrations. - */ - private @Nonnull Map dynamicRegistrations = new HashMap<>(); - private boolean initiallySupportsWorkspaceFolders = false; - - /* Backwards compatible constructor */ - public LanguageServerWrapper(@Nonnull Module project, @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition) { - this(project, serverDefinition, null); - } - - public LanguageServerWrapper(@Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @Nullable URI initialPath) { - this(null, serverDefinition, initialPath); - } - - /** Unified private constructor to set sensible defaults in all cases */ - private LanguageServerWrapper(@Nullable Module project, @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition, - @Nullable URI initialPath) { - this.initialProject = project; - this.initialPath = initialPath; - this.allWatchedProjects = new HashSet<>(); - this.serverDefinition = serverDefinition; - this.connectedDocuments = new HashMap<>(); - this.launcherFuture = null; - this.initializeFuture = null; - } - - public Project getProject() { - return initialProject.getProject(); - } - - /** - * Starts a language server and triggers an initialization future. - * Sets up initializeFuture to access the server. - * If language server is started and active, does nothing. - * If language server is inactive, restart it. - * - * @throws IOException - */ - public synchronized void start() throws IOException { - Map filesToReconnect = new HashMap<>(); - if (this.languageServer != null) { // already been started - if (isActive()) { - return; - } else { - for (Map.Entry entry : this.connectedDocuments.entrySet()) { - filesToReconnect.put(entry.getKey(), entry.getValue().getDocument()); - } - stop(); - } - } - if (this.initializeFuture == null) { - - ExecutorService executorService = Executors.newCachedThreadPool(); - final InitializeParams initParams = new InitializeParams(); - - final URI rootURI = getRootURI(); - this.initializeFuture = CompletableFuture.supplyAsync(() -> { - if (LoggingStreamConnectionProviderProxy.shouldLog(serverDefinition.id)) { - this.lspStreamProvider = new LoggingStreamConnectionProviderProxy( - serverDefinition.createConnectionProvider(), serverDefinition.id); - } else { - this.lspStreamProvider = serverDefinition.createConnectionProvider(); - } - initParams.setInitializationOptions(this.lspStreamProvider.getInitializationOptions(rootURI)); - try { - lspStreamProvider.start(); - } catch (IOException e) { - throw new RuntimeException(e); - } - return null; - }).thenApply(unused -> { - LanguageClientImpl client = serverDefinition.createLanguageClient(initialProject.getProject()); - initParams.setProcessId(getCurrentProcessId()); - if (rootURI != null) { - initParams.setRootUri(rootURI.toString()); - initParams.setRootPath(rootURI.getPath()); - } - UnaryOperator wrapper = - consumer -> (message -> { - try { - logMessage(message); - consumer.consume(message); - final StreamConnectionProvider currentConnectionProvider = this.lspStreamProvider; - if (currentConnectionProvider != null && isActive()) { - currentConnectionProvider.handleMessage(message, this.languageServer, rootURI); - } - } catch (Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - }); - Launcher launcher = Launcher.createLauncher(client, serverDefinition.getServerInterface(), - this.lspStreamProvider.getInputStream(), this.lspStreamProvider.getOutputStream(), - executorService, wrapper); - - this.languageServer = launcher.getRemoteProxy(); - client.connect(languageServer, this); - this.launcherFuture = launcher.startListening(); - return null; - }).thenCompose(unused -> { - - WorkspaceClientCapabilities workspaceClientCapabilities = new WorkspaceClientCapabilities(); - workspaceClientCapabilities.setApplyEdit(Boolean.TRUE); - workspaceClientCapabilities.setExecuteCommand(new ExecuteCommandCapabilities(Boolean.TRUE)); - workspaceClientCapabilities.setSymbol(new SymbolCapabilities(Boolean.TRUE)); - workspaceClientCapabilities.setWorkspaceFolders(Boolean.TRUE); - WorkspaceEditCapabilities editCapabilities = new WorkspaceEditCapabilities(); - editCapabilities.setDocumentChanges(Boolean.TRUE); - editCapabilities.setResourceOperations(Arrays.asList(ResourceOperationKind.Create, - ResourceOperationKind.Delete, ResourceOperationKind.Rename)); - editCapabilities.setFailureHandling(FailureHandlingKind.Undo); - workspaceClientCapabilities.setWorkspaceEdit(editCapabilities); - - TextDocumentClientCapabilities textDocumentClientCapabilities = new TextDocumentClientCapabilities(); - CodeActionCapabilities codeAction = new CodeActionCapabilities(new CodeActionLiteralSupportCapabilities( - new CodeActionKindCapabilities(Arrays.asList(CodeActionKind.QuickFix, CodeActionKind.Refactor, - CodeActionKind.RefactorExtract, CodeActionKind.RefactorInline, - CodeActionKind.RefactorRewrite, CodeActionKind.Source, - CodeActionKind.SourceOrganizeImports))), - true); - codeAction.setDataSupport(true); - codeAction.setResolveSupport(new CodeActionResolveSupportCapabilities(List.of("edit"))); //$NON-NLS-1$ - textDocumentClientCapabilities.setCodeAction(codeAction); - - textDocumentClientCapabilities.setCodeLens(new CodeLensCapabilities()); - textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities()); - CompletionItemCapabilities completionItemCapabilities = new CompletionItemCapabilities(Boolean.TRUE); - completionItemCapabilities.setDocumentationFormat(Arrays.asList(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT)); - textDocumentClientCapabilities - .setCompletion(new CompletionCapabilities(completionItemCapabilities)); - DefinitionCapabilities definitionCapabilities = new DefinitionCapabilities(); - definitionCapabilities.setLinkSupport(Boolean.TRUE); - textDocumentClientCapabilities.setDefinition(definitionCapabilities); - TypeDefinitionCapabilities typeDefinitionCapabilities = new TypeDefinitionCapabilities(); - typeDefinitionCapabilities.setLinkSupport(Boolean.TRUE); - textDocumentClientCapabilities.setTypeDefinition(typeDefinitionCapabilities); - textDocumentClientCapabilities.setDocumentHighlight(new DocumentHighlightCapabilities()); - textDocumentClientCapabilities.setDocumentLink(new DocumentLinkCapabilities()); - DocumentSymbolCapabilities documentSymbol = new DocumentSymbolCapabilities(); - documentSymbol.setHierarchicalDocumentSymbolSupport(true); - documentSymbol.setSymbolKind(new SymbolKindCapabilities(Arrays.asList(SymbolKind.Array, SymbolKind.Boolean, - SymbolKind.Class, SymbolKind.Constant, SymbolKind.Constructor, SymbolKind.Enum, - SymbolKind.EnumMember, SymbolKind.Event, SymbolKind.Field, SymbolKind.File, SymbolKind.Function, - SymbolKind.Interface, SymbolKind.Key, SymbolKind.Method, SymbolKind.Module, SymbolKind.Namespace, - SymbolKind.Null, SymbolKind.Number, SymbolKind.Object, SymbolKind.Operator, SymbolKind.Package, - SymbolKind.Property, SymbolKind.String, SymbolKind.Struct, SymbolKind.TypeParameter, - SymbolKind.Variable))); - textDocumentClientCapabilities.setDocumentSymbol(documentSymbol); - textDocumentClientCapabilities.setFormatting(new FormattingCapabilities(Boolean.TRUE)); - HoverCapabilities hoverCapabilities = new HoverCapabilities(); - hoverCapabilities.setContentFormat(Arrays.asList(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT)); - textDocumentClientCapabilities.setHover(hoverCapabilities); - textDocumentClientCapabilities.setOnTypeFormatting(null); // TODO - textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities()); - textDocumentClientCapabilities.setReferences(new ReferencesCapabilities()); - textDocumentClientCapabilities.setRename(new RenameCapabilities()); - textDocumentClientCapabilities.setSignatureHelp(new SignatureHelpCapabilities()); - textDocumentClientCapabilities - .setSynchronization(new SynchronizationCapabilities(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)); - initParams.setCapabilities( - new ClientCapabilities(workspaceClientCapabilities, textDocumentClientCapabilities, lspStreamProvider.getExperimentalFeaturesPOJO())); - initParams.setClientInfo(new ClientInfo(CLIENT_NAME)); - initParams.setTrace(this.lspStreamProvider.getTrace(rootURI)); - // no then...Async future here as we want this chain of operation to be sequential and "atomic"-ish - return languageServer.initialize(initParams); - }).thenAccept(res -> { - serverCapabilities = res.getCapabilities(); - this.initiallySupportsWorkspaceFolders = supportsWorkspaceFolders(serverCapabilities); - }).thenRun(() -> { - this.languageServer.initialized(new InitializedParams()); - }).thenRun(() -> { - final Map toReconnect = filesToReconnect; - initializeFuture.thenRunAsync(() -> { - if (this.initialProject != null) { - watchProject(this.initialProject, true); - } - for (Map.Entry fileToReconnect : toReconnect.entrySet()) { - try { - connect(fileToReconnect.getKey(), fileToReconnect.getValue()); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - }); - }).exceptionally(e -> { - LOGGER.warn(e.getLocalizedMessage(), e); - initializeFuture.completeExceptionally(e); - stop(); - return null; - }); - EditorFactory.getInstance().getEventMulticaster().addDocumentListener(fileBufferListener); - messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect(); - messageBusConnection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, fileBufferListener); - } - } - - @Nullable - private URI getRootURI() { - Module project = this.initialProject; - if (project != null) { - return LSPIJUtils.toUri(this.initialProject); - } - // This is required due to overzealous static analysis. Dereferencing - // this.initialPath directly will trigger a "potential null" - // warning/error. Checking for this.initialPath == null is not - // enough. - final URI initialPath = this.initialPath; - if (initialPath != null) { - File projectDirectory = new File(initialPath); - if (projectDirectory.isFile()) { - projectDirectory = projectDirectory.getParentFile(); - } - return LSPIJUtils.toUri(projectDirectory); - } // else if path is null do we want '/' root uri? LSPIJUtils.toUri(new File("/")).toString()); //$NON-NLS-1$ - return null; - } - - private static boolean supportsWorkspaceFolders(ServerCapabilities serverCapabilities) { - return serverCapabilities != null && serverCapabilities.getWorkspace() != null - && serverCapabilities.getWorkspace().getWorkspaceFolders() != null - && Boolean.TRUE.equals(serverCapabilities.getWorkspace().getWorkspaceFolders().getSupported()); - } - - private Integer getCurrentProcessId() { - String segment = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; //$NON-NLS-1$ - try { - return Integer.valueOf(segment); - } catch (NumberFormatException ex) { - return null; - } - } - - private void logMessage(Message message) { - if (message instanceof ResponseMessage && ((ResponseMessage) message).getError() != null - && ((ResponseMessage) message).getId() - .equals(Integer.toString(ResponseErrorCode.RequestCancelled.getValue()))) { - ResponseMessage responseMessage = (ResponseMessage) message; - LOGGER.warn("", new ResponseErrorException(responseMessage.getError())); - } else if (LOGGER.isDebugEnabled()) { - LOGGER.info(message.getClass().getSimpleName() + '\n' + message.toString()); - } -// else { -// LOGGER.warn(message.getClass().getSimpleName() + '\n' + message.toString()); -// } - } - - /** - * @return whether the underlying connection to language server is still active - */ - public boolean isActive() { - // isDone() includes Completion and Cancellation https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html - return this.launcherFuture != null && !this.launcherFuture.isDone(); - } - - /** - * @return whether the language server initialization connection is still active - */ - private boolean isInitializing() { - return (this.initializeFuture != null) && !this.initializeFuture.isDone(); - } - - synchronized void stop() { - final boolean alreadyStopping = this.stopping.getAndSet(true); - if (alreadyStopping) { - return; - } - if (this.initializeFuture != null) { - this.initializeFuture.cancel(true); - this.initializeFuture = null; - } - - this.serverCapabilities = null; - this.dynamicRegistrations.clear(); - - final Future serverFuture = this.launcherFuture; - final StreamConnectionProvider provider = this.lspStreamProvider; - final LanguageServer languageServerInstance = this.languageServer; - - Runnable shutdownKillAndStopFutureAndProvider = () -> { - if (languageServerInstance != null) { - CompletableFuture shutdown = languageServerInstance.shutdown(); - try { - shutdown.get(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - - if (serverFuture != null) { - serverFuture.cancel(true); - } - - if (languageServerInstance != null) { - languageServerInstance.exit(); - } - - if (provider != null) { - provider.stop(); - } - this.stopping.set(false); - }; - - CompletableFuture.runAsync(shutdownKillAndStopFutureAndProvider); - - this.launcherFuture = null; - this.lspStreamProvider = null; - - while (!this.connectedDocuments.isEmpty()) { - disconnect(this.connectedDocuments.keySet().iterator().next()); - } - this.languageServer = null; - - EditorFactory.getInstance().getEventMulticaster().removeDocumentListener(fileBufferListener); - if (messageBusConnection != null) { - messageBusConnection.disconnect(); - } - } - - /** - * - * @param file - * @param document - * @return null if not connection has happened, a future tracking the connection state otherwise - * @throws IOException - */ - public @Nullable CompletableFuture connect(@Nonnull VirtualFile file, Document document) throws IOException { - return connect(LSPIJUtils.toUri(file), document); - } - - /** - * - * @param document - * @return null if not connection has happened, a future tracking the connection state otherwise - * @throws IOException - */ - public @Nullable CompletableFuture connect(Document document) throws IOException { - VirtualFile file = LSPIJUtils.getFile(document); - - if (file != null && file.exists()) { - return connect(file, document); - } else { - URI uri = LSPIJUtils.toUri(document); - if (uri != null) { - return connect(uri, document); - } - } - return null; - } - - protected synchronized void watchProject(Module project, boolean isInitializationRootProject) { - if (this.allWatchedProjects.contains(project)) { - return; - } - if (isInitializationRootProject && !this.allWatchedProjects.isEmpty()) { - return; // there can be only one root project - } - if (!isInitializationRootProject && !supportsWorkspaceFolderCapability()) { - // multi project and WorkspaceFolder notifications not supported by this server - // instance - return; - } - this.allWatchedProjects.add(project); - project.getProject().getMessageBus().connect(project.getProject()).subscribe(ProjectTopics.MODULES, new ModuleListener() { - @Override - public void moduleRemoved(@NotNull Project project, @NotNull Module module) { - unwatchProject(module); - } - //TODO: should we handle module rename - }); - /*project.getWorkspace().addResourceChangeListener(event -> { - if (project.equals(event.getResource()) && (event.getDelta().getKind() == IResourceDelta.MOVED_FROM - || event.getDelta().getKind() == IResourceDelta.REMOVED)) { - unwatchProject(project); - } - }, IResourceChangeEvent.POST_CHANGE);*/ - if (supportsWorkspaceFolderCapability()) { - WorkspaceFoldersChangeEvent event = new WorkspaceFoldersChangeEvent(); - event.getAdded().add(LSPIJUtils.toWorkspaceFolder(project)); - DidChangeWorkspaceFoldersParams params = new DidChangeWorkspaceFoldersParams(); - params.setEvent(event); - this.languageServer.getWorkspaceService().didChangeWorkspaceFolders(params); - } - } - - private synchronized void unwatchProject(@Nonnull Module project) { - this.allWatchedProjects.remove(project); - // TODO? disconnect resources? - if (supportsWorkspaceFolderCapability()) { - WorkspaceFoldersChangeEvent event = new WorkspaceFoldersChangeEvent(); - event.getRemoved().add(LSPIJUtils.toWorkspaceFolder(project)); - DidChangeWorkspaceFoldersParams params = new DidChangeWorkspaceFoldersParams(); - params.setEvent(event); - this.languageServer.getWorkspaceService().didChangeWorkspaceFolders(params); - } - } - - /** - * Check whether this LS is suitable for provided project. Starts the LS if not - * already started. - * - * @return whether this language server can operate on the given project - * @since 0.5 - */ - public boolean canOperate(Module project) { - if (project != null && (project.equals(this.initialProject) || this.allWatchedProjects.contains(project))) { - return true; - } - - return serverDefinition.isSingleton || supportsWorkspaceFolderCapability(); - } - - /** - * @return true, if the server supports multi-root workspaces via workspace - * folders - * @since 0.6 - */ - private boolean supportsWorkspaceFolderCapability() { - if (this.initializeFuture != null) { - try { - this.initializeFuture.get(1, TimeUnit.SECONDS); - } catch (TimeoutException e) { - LOGGER.warn("Could not get if the workspace folder capability is supported due to timeout after 1 second", e); //$NON-NLS-1$ - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - return initiallySupportsWorkspaceFolders || supportsWorkspaceFolders(serverCapabilities); - } - - /** - * To make public when we support non IFiles - * - * @return null if no connection has happened, a future that completes when file is initialized otherwise - * @noreference internal so far - */ - private CompletableFuture connect(@Nonnull URI absolutePath, Document document) throws IOException { - final URI thePath = absolutePath; // should be useless - - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - if (file != null && file.exists()) { - watchProject(LSPIJUtils.getProject(file), false); - } - - if (this.connectedDocuments.containsKey(thePath)) { - return CompletableFuture.completedFuture(languageServer); - } - start(); - if (this.initializeFuture == null) { - return null; - } - if (document == null) { - VirtualFile docFile = LSPIJUtils.findResourceFor(thePath); - document = LSPIJUtils.getDocument(docFile); - } - if (document == null) { - return null; - } - final Document theDocument = document; - return initializeFuture.thenComposeAsync(theVoid -> { - synchronized (connectedDocuments) { - if (this.connectedDocuments.containsKey(thePath)) { - return CompletableFuture.completedFuture(null); - } - Either syncOptions = initializeFuture == null ? null - : this.serverCapabilities.getTextDocumentSync(); - TextDocumentSyncKind syncKind = null; - if (syncOptions != null) { - if (syncOptions.isRight()) { - syncKind = syncOptions.getRight().getChange(); - } else if (syncOptions.isLeft()) { - syncKind = syncOptions.getLeft(); - } - } - DocumentContentSynchronizer listener = new DocumentContentSynchronizer(this, theDocument, syncKind); - theDocument.addDocumentListener(listener); - Set synchronizers = theDocument.getUserData(DocumentContentSynchronizer.KEY); - if (synchronizers == null) { - synchronizers = Collections.synchronizedSet(new LinkedHashSet<>()); - theDocument.putUserData(DocumentContentSynchronizer.KEY, synchronizers); - } - synchronizers.add(listener); - LanguageServerWrapper.this.connectedDocuments.put(thePath, listener); - return listener.didOpenFuture; - } - }).thenApply(theVoid -> languageServer); - } - - public void disconnect(URI path) { - DocumentContentSynchronizer documentListener = this.connectedDocuments.remove(path); - if (documentListener != null) { - final Document document = documentListener.getDocument(); - document.removeDocumentListener(documentListener); - Set synchronizers = document.getUserData(DocumentContentSynchronizer.KEY); - if (synchronizers != null) { - synchronizers.remove(documentListener); - if (synchronizers.isEmpty()) { - document.putUserData(DocumentContentSynchronizer.KEY, null); - } - } - documentListener.documentClosed(); - } - if (this.connectedDocuments.isEmpty()) { - stop(); - } - } - - public void disconnectContentType(@Nonnull Language language) { - List pathsToDisconnect = new ArrayList<>(); - for (URI path : connectedDocuments.keySet()) { - VirtualFile foundFiles = LSPIJUtils.findResourceFor(path); - if (foundFiles != null) { - Language fileLanguage = LSPIJUtils.getFileLanguage(foundFiles, initialProject.getProject()); - if (fileLanguage.isKindOf(language)) { - pathsToDisconnect.add(path); - } - } - } - for (URI path : pathsToDisconnect) { - disconnect(path); - } - } - - /** - * checks if the wrapper is already connected to the document at the given path - * - * @noreference test only - */ - public boolean isConnectedTo(URI location) { - return connectedDocuments.containsKey(location); - } - - /** - * Starts and returns the language server, regardless of if it is initialized. - * If not in the UI Thread, will wait to return the initialized server. - * - * @deprecated use {@link #getInitializedServer()} instead. - */ - @Deprecated - @Nullable - public LanguageServer getServer() { - CompletableFuture languageServerFuture = getInitializedServer(); - if (ApplicationManager.getApplication().isDispatchThread()) { // UI Thread - return this.languageServer; - } else { - return languageServerFuture.join(); - } - } - - /** - * Starts the language server and returns a CompletableFuture waiting for the - * server to be initialized. TODO: If done in the UI stream, a job will be created - * displaying that the server is being initialized - */ - @Nonnull - public CompletableFuture getInitializedServer() { - try { - start(); - } catch (IOException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - if (isInitializing()) { - /*if (ApplicationManager.getApplication().isDispatchThread()) { // UI Thread - try { - ProgressManager.getInstance().run(new Task.WithResult(null, Messages.initializeLanguageServer_job, false) { - @Override - protected Void compute(@NotNull ProgressIndicator indicator) throws Exception { - indicator.setText("Waiting for server " + LanguageServerWrapper.this.serverDefinition.id + " to be started"); - initializeFuture.join(); - return null; - } - }); - } catch (Exception e) { - LOGGER.error(e.getLocalizedMessage(), e); - } - }*/ - return initializeFuture.thenApply(r -> this.languageServer); - } - return CompletableFuture.completedFuture(this.languageServer); - } - - /** - * Warning: this is a long running operation - * - * @return the server capabilities, or null if initialization job didn't - * complete - */ - @Nullable - public ServerCapabilities getServerCapabilities() { - try { - getInitializedServer().get(10, TimeUnit.SECONDS); - } catch (TimeoutException e) { - LOGGER.warn("LanguageServer not initialized after 10s", e); //$NON-NLS-1$ - } catch (ExecutionException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (CancellationException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - - return this.serverCapabilities; - } - - /** - * @return The language ID that this wrapper is dealing with if defined in the - * content type mapping for the language server - */ - @Nullable - public String getLanguageId(Language language) { - while (language != null) { - String languageId = serverDefinition.languageIdMappings.get(language); - if (languageId != null) { - return languageId; - } - language = language.getBaseLanguage(); - } - return null; - } - - void registerCapability(RegistrationParams params) { - if (initializeFuture == null) { // language server is shutting down but registration messages are still arriving - if (LOGGER.isDebugEnabled()) { - StringBuffer buffer = new StringBuffer(); - params.getRegistrations().forEach(reg -> { buffer.append(reg.getMethod()).append(","); }); - LOGGER.info("registerCapability, initializeFuture==null, ignore capabilities " + buffer); - } - return; - } - initializeFuture.thenRun(() -> { - params.getRegistrations().forEach(reg -> { - if ("workspace/didChangeWorkspaceFolders".equals(reg.getMethod())) { //$NON-NLS-1$ - assert serverCapabilities != null : - "Dynamic capability registration failed! Server not yet initialized?"; //$NON-NLS-1$ - if (initiallySupportsWorkspaceFolders) { - // Can treat this as a NOP since nothing can disable it dynamically if it was - // enabled on initialization. - } else if (supportsWorkspaceFolders(serverCapabilities)) { - LOGGER.warn( - "Dynamic registration of 'workspace/didChangeWorkspaceFolders' ignored. It was already enabled before"); //$NON-NLS-1$); - } else { - addRegistration(reg, () -> setWorkspaceFoldersEnablement(false)); - setWorkspaceFoldersEnablement(true); - } - } else if ("workspace/executeCommand".equals(reg.getMethod())) { //$NON-NLS-1$ - Gson gson = new Gson(); // TODO? retrieve the GSon used by LS - ExecuteCommandOptions executeCommandOptions = gson.fromJson((JsonObject) reg.getRegisterOptions(), - ExecuteCommandOptions.class); - List newCommands = executeCommandOptions.getCommands(); - if (!newCommands.isEmpty()) { - addRegistration(reg, () -> unregisterCommands(newCommands)); - registerCommands(newCommands); - } - } else if ("textDocument/formatting".equals(reg.getMethod())) { //$NON-NLS-1$ - final Either documentFormattingProvider = serverCapabilities.getDocumentFormattingProvider(); - if (documentFormattingProvider == null || documentFormattingProvider.isLeft()) { - serverCapabilities.setDocumentFormattingProvider(Boolean.TRUE); - addRegistration(reg, () -> serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider )); - } else { - serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider.getRight()); - addRegistration(reg, () -> serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider )); - } - } else if ("textDocument/rangeFormatting".equals(reg.getMethod())) { //$NON-NLS-1$ - final Either documentRangeFormattingProvider = serverCapabilities.getDocumentRangeFormattingProvider(); - if (documentRangeFormattingProvider == null || documentRangeFormattingProvider.isLeft()) { - serverCapabilities.setDocumentRangeFormattingProvider(Boolean.TRUE); - addRegistration(reg, () -> serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider )); - } else { - serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider.getRight()); - addRegistration(reg, () -> serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider )); - } - } else if ("textDocument/codeAction".equals(reg.getMethod())) { //$NON-NLS-1$ - final Either beforeRegistration = serverCapabilities.getCodeActionProvider(); - serverCapabilities.setCodeActionProvider(Boolean.TRUE); - addRegistration(reg, () -> serverCapabilities.setCodeActionProvider(beforeRegistration)); - } - }); - }); - } - - private void addRegistration(@Nonnull Registration reg, @Nonnull Runnable unregistrationHandler) { - String regId = reg.getId(); - synchronized (dynamicRegistrations) { - assert !dynamicRegistrations.containsKey(regId):"Registration id is not unique"; //$NON-NLS-1$ - dynamicRegistrations.put(regId, unregistrationHandler); - } - } - - synchronized void setWorkspaceFoldersEnablement(boolean enable) { - if (serverCapabilities == null) { - this.serverCapabilities = new ServerCapabilities(); - } - WorkspaceServerCapabilities workspace = serverCapabilities.getWorkspace(); - if (workspace == null) { - workspace = new WorkspaceServerCapabilities(); - serverCapabilities.setWorkspace(workspace); - } - WorkspaceFoldersOptions folders = workspace.getWorkspaceFolders(); - if (folders == null) { - folders = new WorkspaceFoldersOptions(); - workspace.setWorkspaceFolders(folders); - } - folders.setSupported(enable); - } - - synchronized void registerCommands(List newCommands) { - ServerCapabilities caps = this.getServerCapabilities(); - if (caps != null) { - ExecuteCommandOptions commandProvider = caps.getExecuteCommandProvider(); - if (commandProvider == null) { - commandProvider = new ExecuteCommandOptions(new ArrayList<>()); - caps.setExecuteCommandProvider(commandProvider); - } - List existingCommands = commandProvider.getCommands(); - for (String newCmd : newCommands) { - assert !existingCommands.contains(newCmd):"Command already registered '" + newCmd + "'"; //$NON-NLS-1$ //$NON-NLS-2$ - existingCommands.add(newCmd); - } - } else { - throw new IllegalStateException("Dynamic command registration failed! Server not yet initialized?"); //$NON-NLS-1$ - } - } - - void unregisterCapability(UnregistrationParams params) { - params.getUnregisterations().forEach(reg -> { - String id = reg.getId(); - Runnable unregistrator; - synchronized (dynamicRegistrations) { - unregistrator = dynamicRegistrations.get(id); - dynamicRegistrations.remove(id); - } - if (unregistrator != null) { - unregistrator.run(); - } - }); - } - - void unregisterCommands(List cmds) { - ServerCapabilities caps = this.getServerCapabilities(); - if (caps != null) { - ExecuteCommandOptions commandProvider = caps.getExecuteCommandProvider(); - if (commandProvider != null) { - List existingCommands = commandProvider.getCommands(); - existingCommands.removeAll(cmds); - } - } - } - - int getVersion(VirtualFile file) { - if (file != null && LSPIJUtils.toUri(file) != null) { - DocumentContentSynchronizer documentContentSynchronizer = connectedDocuments.get(LSPIJUtils.toUri(file)); - if (documentContentSynchronizer != null) { - return documentContentSynchronizer.getVersion(); - } - } - return -1; - } - - public boolean canOperate(@Nonnull Document document) { - if (this.isConnectedTo(LSPIJUtils.toUri(document))) { - return true; - } - if (this.initialProject == null && this.connectedDocuments.isEmpty()) { - return true; - } - VirtualFile file = LSPIJUtils.getFile(document); - if (file != null && file.exists() && canOperate(LSPIJUtils.getProject(file))) { - return true; - } - return serverDefinition.isSingleton || supportsWorkspaceFolderCapability(); - } - -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServersRegistry.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServersRegistry.java deleted file mode 100644 index a2a467859..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServersRegistry.java +++ /dev/null @@ -1,259 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -public class LanguageServersRegistry { - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServersRegistry.class); - - public abstract static class LanguageServerDefinition { - public final @Nonnull String id; - public final @Nonnull String label; - public final boolean isSingleton; - public final @Nonnull Map languageIdMappings; - public final Map languageFilePatternMappings; - - public LanguageServerDefinition(@Nonnull String id, @Nonnull String label, boolean isSingleton) { - this.id = id; - this.label = label; - this.isSingleton = isSingleton; - this.languageIdMappings = new ConcurrentHashMap<>(); - this.languageFilePatternMappings = new ConcurrentHashMap<>(); - } - - public void registerAssociation(@Nonnull Language language, @Nonnull String languageId, String filePattern) { - this.languageIdMappings.put(language, languageId); - if (filePattern != null) { - this.languageFilePatternMappings.put(language, filePattern); - } - } - - public abstract StreamConnectionProvider createConnectionProvider(); - - public LanguageClientImpl createLanguageClient(Project project) { - return new LanguageClientImpl(project); - } - - public Class getServerInterface() { - return LanguageServer.class; - } - - } - - static class ExtensionLanguageServerDefinition extends LanguageServerDefinition { - private ServerExtensionPointBean extension; - - public ExtensionLanguageServerDefinition(ServerExtensionPointBean element) { - super(element.id, element.label, element.singleton); - this.extension = element; - } - - @Override - public StreamConnectionProvider createConnectionProvider() { - try { - return extension.getInstance(); - } catch (Exception e) { - throw new RuntimeException( - "Exception occurred while creating an instance of the stream connection provider", e); //$NON-NLS-1$ - } - } - - @Override - public LanguageClientImpl createLanguageClient(Project project) { - String clientImpl = extension.clientImpl; - if (clientImpl != null && !clientImpl.isEmpty()) { - try { - return (LanguageClientImpl) project.instantiateClass(extension.getClientImpl(), - extension.getPluginDescriptor().getPluginId()); - } catch (ClassNotFoundException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - return super.createLanguageClient(project); - } - - @SuppressWarnings("unchecked") - @Override - public Class getServerInterface() { - String serverInterface = extension.serverInterface; - if (serverInterface != null && !serverInterface.isEmpty()) { - try { - return (Class) (Class)extension.getServerInterface(); - } catch (ClassNotFoundException exception) { - LOGGER.warn(exception.getLocalizedMessage(), exception); - } - } - return super.getServerInterface(); - } - } - - private static LanguageServersRegistry INSTANCE = null; - public static LanguageServersRegistry getInstance() { - if (INSTANCE == null) { - INSTANCE = new LanguageServersRegistry(); - } - return INSTANCE; - } - - private List connections = new ArrayList<>(); - - private LanguageServersRegistry() { - initialize(); - } - - private void initialize() { - LOGGER.info("***** Initializing Language Servers *****"); - Map servers = new HashMap<>(); - List languageMappings = new ArrayList<>(); - for (ServerExtensionPointBean server : ServerExtensionPointBean.EP_NAME.getExtensions()) { - if (server.id != null && !server.id.isEmpty()) { - servers.put(server.id, new ExtensionLanguageServerDefinition(server)); - } - } - for (LanguageMappingExtensionPointBean extension : LanguageMappingExtensionPointBean.EP_NAME.getExtensions()) { - Language language = Language.findLanguageByID(extension.language); - if (language != null) { - languageMappings.add(new LanguageMapping(language, extension.id, extension.serverId, extension.filePattern)); - } - } - - for (LanguageMapping mapping : languageMappings) { - LanguageServerDefinition lsDefinition = servers.get(mapping.languageId); - if (lsDefinition != null) { - LOGGER.info("Registering language server '" + lsDefinition.id + "' with language: " + mapping.language.getID() + " and file pattern: " + lsDefinition.languageFilePatternMappings); - registerAssociation(mapping.language, lsDefinition, mapping.languageId, mapping.filePattern); - } else { - LOGGER.warn("Language server '" + mapping.id + "' not available"); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - - LOGGER.info("**********"); - } - - /** - * @param contentType - * @return the {@link LanguageServerDefinition}s directly associated to the given content-type. - * This does not include the one that match transitively as per content-type hierarchy - */ - List findProviderFor(final @NonNull Language contentType) { - return connections.stream() - .filter(entry -> contentType.isKindOf(entry.getKey())) - .collect(Collectors.toList()); - } - - - public void registerAssociation(@Nonnull Language language, - @Nonnull LanguageServerDefinition serverDefinition, @Nullable String languageId, @Nullable String filePattern) { - if (languageId != null) { - serverDefinition.registerAssociation(language, languageId, filePattern); - } - - connections.add(new ContentTypeToLanguageServerDefinition(language, serverDefinition)); - } - - public List getContentTypeToLSPExtensions() { - return this.connections.stream().filter(mapping -> mapping.getValue() instanceof ExtensionLanguageServerDefinition).collect(Collectors.toList()); - } - - public @Nullable LanguageServerDefinition getDefinition(@NonNull String languageServerId) { - for (ContentTypeToLanguageServerDefinition mapping : this.connections) { - if (mapping.getValue().id.equals(languageServerId)) { - return mapping.getValue(); - } - } - return null; - } - - /** - * internal class to capture content-type mappings for language servers - */ - private static class LanguageMapping { - - @Nonnull public final String id; - @Nonnull public final Language language; - @Nullable public final String languageId; - @Nullable public final String filePattern; - - public LanguageMapping(@Nonnull Language language, @Nonnull String id, @Nullable String languageId, @Nullable String filePattern) { - this.language = language; - this.id = id; - this.languageId = languageId; - this.filePattern = filePattern; - } - - } - - /** - * @param file - * @param serverDefinition - * @return whether the given serverDefinition is suitable for the file - */ - public boolean matches(@Nonnull VirtualFile file, @NonNull LanguageServerDefinition serverDefinition, - Project project) { - return getAvailableLSFor(file, project).contains(serverDefinition); - } - - /** - * @param document - * @param serverDefinition - * @return whether the given serverDefinition is suitable for the file - */ - public boolean matches(@Nonnull Document document, @Nonnull LanguageServerDefinition serverDefinition, - Project project) { - return getAvailableLSFor(document, project).contains(serverDefinition); - } - - private Set getAvailableLSFor(Document document, Project project) { - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - return getAvailableLSFor(file, project); - } - - private Set getAvailableLSFor(VirtualFile file, Project project) { - Language language = LSPIJUtils.getFileLanguage(file, project); - Set res = new HashSet<>(); - if (language != null) { - for (ContentTypeToLanguageServerDefinition mapping : this.connections) { - if (language.isKindOf(mapping.getKey())) { - LanguageServerDefinition lsDef = mapping.getValue(); - if (!lsDef.languageFilePatternMappings.isEmpty() && lsDef.languageFilePatternMappings.containsKey(language)) { - // check if document matches file pattern - Path path = Paths.get(file.getPath()); - final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + lsDef.languageFilePatternMappings.get(language)); - if (matcher.matches(path)) { - LOGGER.trace("Available language server: " + mapping.getValue().id + " for file: " + file); - res.add(mapping.getValue()); - } - } else { - LOGGER.trace("Available language server: " + mapping.getValue().id + " for file: " + file); - res.add(mapping.getValue()); - } - } - } - } - return res; - } -} - diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServiceAccessor.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServiceAccessor.java deleted file mode 100644 index 625d0e4e2..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LanguageServiceAccessor.java +++ /dev/null @@ -1,628 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.lang.LanguageUtil; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.Paths; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * The entry-point to retrieve a Language Server for a given resource/project. - * Deals with instantiations and caching of underlying - * {@link LanguageServerWrapper}. - * - */ -public class LanguageServiceAccessor { - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServiceAccessor.class); - private final Project project; - - public static LanguageServiceAccessor getInstance(@NotNull Project project) { - return project.getService(LanguageServiceAccessor.class); - } - - private LanguageServiceAccessor(Project project) { - this.project = project; - } - - private Set startedServers = new HashSet<>(); - private Map providersToLSDefinitions = new HashMap<>(); - - /** - * This is meant for test code to clear state that might have leaked from other - * tests. It isn't meant to be used in production code. - */ - public void clearStartedServers() { - synchronized (startedServers) { - startedServers.forEach(LanguageServerWrapper::stop); - startedServers.clear(); - } - } - - - /** - * A bean storing association of a Document/File with a language server. - */ - public static class LSPDocumentInfo { - - private final @Nonnull - URI fileUri; - private final @Nonnull - Document document; - private final @Nonnull - LanguageServerWrapper wrapper; - - private LSPDocumentInfo(@Nonnull URI fileUri, @Nonnull Document document, - @Nonnull LanguageServerWrapper wrapper) { - this.fileUri = fileUri; - this.document = document; - this.wrapper = wrapper; - } - - public @Nonnull - Document getDocument() { - return this.document; - } - - /** - * TODO consider directly returning a {@link TextDocumentIdentifier} - * - * @return - */ - public @Nonnull - URI getFileUri() { - return this.fileUri; - } - - /** - * Returns the language server, regardless of if it is initialized. - * - * @deprecated use {@link #getInitializedLanguageClient()} instead. - */ - @Deprecated - public LanguageServer getLanguageClient() { - try { - return this.wrapper.getInitializedServer().get(); - } catch (ExecutionException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return this.wrapper.getServer(); - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - return this.wrapper.getServer(); - } - } - - public int getVersion() { - return wrapper.getVersion(LSPIJUtils.getFile(document)); - } - - public CompletableFuture getInitializedLanguageClient() { - return this.wrapper.getInitializedServer(); - } - - public @Nullable - ServerCapabilities getCapabilites() { - return this.wrapper.getServerCapabilities(); - } - - public boolean isActive() { - return this.wrapper.isActive(); - } - } - - - public @Nonnull - List> getInitializedLanguageServers(@Nonnull VirtualFile file, - @Nullable Predicate request) throws IOException { - synchronized (startedServers) { - Collection wrappers = getLSWrappers(file, request); - return wrappers.stream().map(wrapper -> wrapper.getInitializedServer().thenApplyAsync(server -> { - try { - wrapper.connect(file, null); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return server; - })).collect(Collectors.toList()); - } - } - - public void disableLanguageServerContentType( - @Nonnull ContentTypeToLanguageServerDefinition contentTypeToLSDefinition) { - Optional result = startedServers.stream() - .filter(server -> server.serverDefinition.equals(contentTypeToLSDefinition.getValue())).findFirst(); - if (result.isPresent()) { - Language language = contentTypeToLSDefinition.getKey(); - if (language != null) { - result.get().disconnectContentType(language); - } - } - - } - - public void enableLanguageServerContentType( - @Nonnull ContentTypeToLanguageServerDefinition contentTypeToLSDefinition, - @Nonnull Editor[] editors) { - for (Editor editor : editors) { - VirtualFile editorFile = LSPIJUtils.getFile(editor.getDocument()); - Language language = contentTypeToLSDefinition.getKey(); - LanguageServersRegistry.LanguageServerDefinition lsDefinition = contentTypeToLSDefinition.getValue(); - Language contentLanguage = LanguageUtil.getLanguageForPsi(project, editorFile); - if (contentTypeToLSDefinition.isEnabled() && language != null && contentLanguage != null - && contentLanguage.isKindOf(language) - && lsDefinition != null) { - try { - getInitializedLanguageServer(editorFile, lsDefinition, capabilities -> true); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - } - - } - - /** - * Get the requested language server instance for the given file. Starts the language server if not already started. - * - * @param file - * @param lsDefinition - * @param capabilitiesPredicate a predicate to check capabilities - * @return a LanguageServer for the given file, which is defined with provided server ID and conforms to specified request - * @deprecated use {@link #getInitializedLanguageServer(IFile, LanguageServerDefinition, Predicate)} instead. - */ - @Deprecated - public LanguageServer getLanguageServer(@Nonnull VirtualFile file, @Nonnull LanguageServersRegistry.LanguageServerDefinition lsDefinition, - Predicate capabilitiesPredicate) - throws IOException { - LanguageServerWrapper wrapper = getLSWrapperForConnection(LSPIJUtils.getProject(file), lsDefinition, LSPIJUtils.toUri(file)); - if (capabilitiesPredicate == null - || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ - || capabilitiesPredicate.test(wrapper.getServerCapabilities())) { - wrapper.connect(file, null); - return wrapper.getServer(); - } - return null; - } - - /** - * Get the requested language server instance for the given file. Starts the language server if not already started. - * - * @param file - * @param lsDefinition - * @param capabilitiesPredicate a predicate to check capabilities - * @return a LanguageServer for the given file, which is defined with provided server ID and conforms to specified request - */ - public CompletableFuture getInitializedLanguageServer(@Nonnull VirtualFile file, - @Nonnull LanguageServersRegistry.LanguageServerDefinition lsDefinition, - Predicate capabilitiesPredicate) - throws IOException { - LanguageServerWrapper wrapper = getLSWrapperForConnection(LSPIJUtils.getProject(file), lsDefinition, LSPIJUtils.toUri(file)); - if (capabilitiesPredicate == null - || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ - || capabilitiesPredicate.test(wrapper.getServerCapabilities())) { - wrapper.connect(file, null); - return wrapper.getInitializedServer(); - } - return null; - } - - /** - * Get the requested language server instance for the given document. Starts the - * language server if not already started. - * - * @param document the document for which the initialized LanguageServer shall be returned - * @param lsDefinition the ID of the LanguageServer to be returned - * @param capabilitiesPredicate - * an object to check the capabilities of the language server wrapper - * @return a LanguageServer for the given file, which is defined with provided - * server ID and conforms to specified request. If - * {@code capabilitesPredicate} does not test positive for the server's - * capabilities, {@code null} is returned. - */ - public CompletableFuture getInitializedLanguageServer(Document document, - LanguageServersRegistry.LanguageServerDefinition lsDefinition, - Predicate capabilitiesPredicate) - throws IOException { - URI initialPath = LSPIJUtils.toUri(document); - LanguageServerWrapper wrapper = getLSWrapperForConnection(document, lsDefinition, initialPath); - if (capabilitiesComply(wrapper, capabilitiesPredicate)) { - wrapper.connect(document); - return wrapper.getInitializedServer(); - } - return null; - } - - /** - * Checks if the given {@code wrapper}'s capabilities comply with the given - * {@code capabilitiesPredicate}. - * - * @param wrapper - * the server that's capabilities are tested with - * {@code capabilitiesPredicate} - * @param capabilitiesPredicate - * predicate testing the capabilities of {@code wrapper}. - * @return The result of applying the capabilities of {@code wrapper} to - * {@code capabilitiesPredicate}, or {@code false} if - * {@code capabilitiesPredicate == null} or - * {@code wrapper.getServerCapabilities() == null} - */ - private static boolean capabilitiesComply(LanguageServerWrapper wrapper, - Predicate capabilitiesPredicate) { - return capabilitiesPredicate == null - || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ - || capabilitiesPredicate.test(wrapper.getServerCapabilities()); - } - - - - - /** - * TODO we need a similar method for generic IDocument (enabling non-IFiles) - * - * @param file - * @param request - * @return - * @throws IOException - * @noreference This method is currently internal and should only be referenced - * for testing - */ - @Nonnull - public Collection getLSWrappers(@Nonnull VirtualFile file, - @Nullable Predicate request) throws IOException { - LinkedHashSet res = new LinkedHashSet<>(); - Module project = LSPIJUtils.getProject(file); - if (project == null) { - return res; - } - - res.addAll(getMatchingStartedWrappers(file, request)); - - // look for running language servers via content-type - Queue contentTypes = new LinkedList<>(); - Set addedContentTypes = new HashSet<>(); - contentTypes.add(LSPIJUtils.getFileLanguage(file, project.getProject())); - addedContentTypes.addAll(contentTypes); - - while (!contentTypes.isEmpty()) { - Language contentType = contentTypes.poll(); - if (contentType == null) { - continue; - } - for (ContentTypeToLanguageServerDefinition mapping : LanguageServersRegistry.getInstance().findProviderFor(contentType)) { - if (mapping != null && mapping.getValue() != null && mapping.isEnabled()) { - LanguageServerWrapper wrapper = getLSWrapperForConnection(project, mapping.getValue(), LSPIJUtils.toUri(file)); - if (request == null - || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ - || request.test(wrapper.getServerCapabilities())) { - res.add(wrapper); - } - } - } - } - return res; - } - - @Nonnull - private Collection getLSWrappers(@Nonnull Document document) { - LinkedHashSet res = new LinkedHashSet<>(); - VirtualFile file = LSPIJUtils.getFile(document); - URI uri = LSPIJUtils.toUri(document); - if (uri == null) { - return Collections.emptyList(); - } - URI path = uri; - - // look for running language servers via content-type - Queue contentTypes = new LinkedList<>(); - Set processedContentTypes = new HashSet<>(); - contentTypes.add(LSPIJUtils.getDocumentLanguage(document, project)); - - synchronized (startedServers) { - // already started compatible servers that fit request - res.addAll(startedServers.stream() - .filter(wrapper -> { - try { - return wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(document, wrapper.serverDefinition, project); - } catch (Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return false; - } - }) - .filter(wrapper -> wrapper.canOperate(document)) - .collect(Collectors.toList())); - while (!contentTypes.isEmpty()) { - Language contentType = contentTypes.poll(); - if (contentType == null || processedContentTypes.contains(contentType)) { - continue; - } - for (ContentTypeToLanguageServerDefinition mapping : LanguageServersRegistry.getInstance() - .findProviderFor(contentType)) { - if (mapping == null || !mapping.isEnabled()) { - continue; - } - LanguageServersRegistry.LanguageServerDefinition serverDefinition = mapping.getValue(); - if (serverDefinition == null) { - continue; - } - - if (startedServers.stream().anyMatch(wrapper -> wrapper.serverDefinition.equals(serverDefinition) - && wrapper.canOperate(document))) { - // we already checked a compatible LS with this definition - continue; - } - final Module fileProject = file != null ? LSPIJUtils.getProject(file) : null; - if (fileProject != null) { - boolean patternMappingsEmpty = serverDefinition.languageFilePatternMappings.isEmpty(); - if (!patternMappingsEmpty && serverDefinition.languageFilePatternMappings.containsKey(contentType)) { - // check if document matches file pattern - Path filePath = Paths.get(file.getPath()); - final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + serverDefinition.languageFilePatternMappings.get(contentType)); - if (matcher.matches(filePath)) { - // only start language server if the language and file pattern matches the language server definition - LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition); - startedServers.add(wrapper); - res.add(wrapper); - } - } else if (patternMappingsEmpty || !serverDefinition.languageFilePatternMappings.containsKey(contentType)) { - // no file patterns to filter language servers on, or none applicable for this language - LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition); - startedServers.add(wrapper); - res.add(wrapper); - } - } - } - processedContentTypes.add(contentType); - } - return res; - } - } - - /** - * Return existing {@link LanguageServerWrapper} for the given connection. If - * not found, create a new one with the given connection and register it for - * this project/content-type. - * - * @param project - * @param serverDefinition - * @return - * @throws IOException - * @Deprecated will be made private soon - * @noreference will be made private soon - * @deprecated - */ - @Deprecated - public LanguageServerWrapper getLSWrapperForConnection(@Nonnull Module project, - @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition) throws IOException { - return getLSWrapperForConnection(project, serverDefinition, null); - } - - @Deprecated - private LanguageServerWrapper getLSWrapperForConnection(@Nonnull Module project, - @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @Nullable URI initialPath) throws IOException { - LanguageServerWrapper wrapper = null; - - synchronized (startedServers) { - for (LanguageServerWrapper startedWrapper : getStartedLSWrappers(project)) { - if (startedWrapper.serverDefinition.equals(serverDefinition)) { - wrapper = startedWrapper; - break; - } - } - if (wrapper == null) { - wrapper = project != null ? new LanguageServerWrapper(project, serverDefinition) : - new LanguageServerWrapper(serverDefinition, initialPath); - wrapper.start(); - } - - startedServers.add(wrapper); - } - return wrapper; - } - - private LanguageServerWrapper getLSWrapperForConnection(Document document, - LanguageServersRegistry.LanguageServerDefinition serverDefinition, URI initialPath) throws IOException { - LanguageServerWrapper wrapper = null; - synchronized (startedServers) { - for (LanguageServerWrapper startedWrapper : getStartedLSWrappers(document)) { - if (startedWrapper.serverDefinition.equals(serverDefinition)) { - wrapper = startedWrapper; - break; - } - } - if (wrapper == null) { - wrapper = new LanguageServerWrapper(serverDefinition, initialPath); - wrapper.start(); - } - - startedServers.add(wrapper); - } - return wrapper; - } - - private @Nonnull - List getStartedLSWrappers( - @Nonnull Module project) { - return startedServers.stream().filter(wrapper -> wrapper.canOperate(project)) - .collect(Collectors.toList()); - // TODO multi-root: also return servers which support multi-root? - } - - private List getStartedLSWrappers( - Document document) { - return getStartedLSWrappers(wrapper -> wrapper.canOperate(document)); - } - - private List getStartedLSWrappers(Predicate predicate) { - return startedServers.stream().filter(predicate) - .collect(Collectors.toList()); - // TODO multi-root: also return servers which support multi-root? - } - - - /** - * Gets the matching started language server wrappers given the VirtualFile - * - * @param file - * @param request - * @return started language server wrappers - */ - public Collection getMatchingStartedWrappers(@Nonnull VirtualFile file, - @Nullable Predicate request) { - synchronized (startedServers) { - return startedServers.stream().filter(wrapper -> wrapper.isConnectedTo(LSPIJUtils.toUri(file)) - || (LanguageServersRegistry.getInstance().matches(file, wrapper.serverDefinition, project) - && wrapper.canOperate(LSPIJUtils.getProject(file)))).filter(wrapper -> request == null - || (wrapper.getServerCapabilities() == null || request.test(wrapper.getServerCapabilities()))) - .collect(Collectors.toList()); - } - } - - /** - * Gets list of running LS satisfying a capability predicate. This does not - * start any matching language servers, it returns the already running ones. - * - * @param request - * @return list of Language Servers - */ - @Nonnull - public List getActiveLanguageServers(Predicate request) { - return getLanguageServers(null, request, true); - } - - /** - * Gets list of LS initialized for given project. - * - * @param project - * @param request - * @return list of Language Servers - */ - @Nonnull - public List getLanguageServers(@Nonnull Module project, - Predicate request) { - return getLanguageServers(project, request, false); - } - - /** - * Gets list of LS initialized for given project - * - * @param onlyActiveLS true if this method should return only the already running - * language servers, otherwise previously started language servers - * will be re-activated - * @return list of Language Servers - */ - @Nonnull - public List getLanguageServers(@Nullable Module project, - Predicate request, boolean onlyActiveLS) { - List serverInfos = new ArrayList<>(); - for (LanguageServerWrapper wrapper : startedServers) { - if ((!onlyActiveLS || wrapper.isActive()) && (project == null || wrapper.canOperate(project))) { - @Nullable - LanguageServer server = wrapper.getServer(); - if (server == null) { - continue; - } - if (request == null - || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */ - || request.test(wrapper.getServerCapabilities())) { - serverInfos.add(server); - } - } - } - return serverInfos; - } - - protected LanguageServersRegistry.LanguageServerDefinition getLSDefinition(@Nonnull StreamConnectionProvider provider) { - return providersToLSDefinitions.get(provider); - } - - @Nonnull - public List getLSPDocumentInfosFor(@Nonnull Document document, @Nonnull Predicate capabilityRequest) { - URI fileUri = LSPIJUtils.toUri(document); - List res = new ArrayList<>(); - try { - getLSWrappers(document).stream().filter(wrapper -> wrapper.getServerCapabilities() == null - || capabilityRequest.test(wrapper.getServerCapabilities())).forEach(wrapper -> { - try { - wrapper.connect(document); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - res.add(new LSPDocumentInfo(fileUri, document, wrapper)); - }); - } catch (final Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return res; - } - - /** - * Gets a list of language servers. Will start language servers if they are not already running on the corresponding document. - * - * @param document - * @param filter - * @return - * @since 0.9 - */ - @Nonnull - public CompletableFuture> getLanguageServers(@Nonnull Document document, - Predicate filter) { - URI uri = LSPIJUtils.toUri(document); - if (uri == null) { - return CompletableFuture.completedFuture(Collections.emptyList()); - } - final List res = Collections.synchronizedList(new ArrayList<>()); - try { - return CompletableFuture.allOf(getLSWrappers(document).stream().map(wrapper -> - wrapper.getInitializedServer().thenComposeAsync(server -> { - if (server != null && (filter == null || filter.test(wrapper.getServerCapabilities()))) { - try { - return wrapper.connect(document); - } catch (IOException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - } - return CompletableFuture.completedFuture(null); - }).thenAccept(server -> { - if (server != null) { - res.add(server); - } - })).toArray(CompletableFuture[]::new)).thenApply(theVoid -> res); - } catch (final Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return CompletableFuture.completedFuture(Collections.emptyList()); - } - - public boolean checkCapability(LanguageServer languageServer, Predicate condition) { - return startedServers.stream().filter(wrapper -> wrapper.isActive() && wrapper.getServer() == languageServer) - .anyMatch(wrapper -> condition.test(wrapper.getServerCapabilities())); - } - - public Optional resolveServerDefinition(LanguageServer languageServer) { - synchronized (startedServers) { - return startedServers.stream().filter(wrapper -> languageServer.equals(wrapper.getServer())).findFirst().map(wrapper -> wrapper.serverDefinition); - } - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LoggingStreamConnectionProviderProxy.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LoggingStreamConnectionProviderProxy.java deleted file mode 100644 index 1f0a66302..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/LoggingStreamConnectionProviderProxy.java +++ /dev/null @@ -1,202 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.File; -import java.io.FilterInputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.StandardOpenOption; - -//TODO: implement LoggingStreamConnectionProviderProxy fully (preferences) -public class LoggingStreamConnectionProviderProxy implements StreamConnectionProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(LoggingStreamConnectionProviderProxy.class); - - private final StreamConnectionProvider provider; - private InputStream inputStream; - private OutputStream outputStream; - private InputStream errorStream; - private final String id; - private File logFile; - private boolean logToFile = true; - private boolean logToConsole = false; - - - /** - * Returns whether currently created connections should be logged to file or the - * standard error stream. - * - * @return If connections should be logged - */ - public static boolean shouldLog(String serverId) { - return Boolean.getBoolean("open-liberty.intellij.trace"); - } - - public LoggingStreamConnectionProviderProxy(StreamConnectionProvider provider, String serverId) { - this.provider = provider; - this.id = serverId; - this.logFile = getLogFile(); - } - - @Override - public void start() throws IOException { - provider.start(); - } - - @Override - public InputStream getInputStream() { - if (inputStream != null) { - return inputStream; - } - if (provider.getInputStream() != null) { - inputStream = new FilterInputStream(provider.getInputStream()) { - @Override - public int read(byte[] b, int off, int len) throws IOException { - int bytes = super.read(b, off, len); - byte[] payload = new byte[bytes]; - System.arraycopy(b, off, payload, 0, bytes); - if (logToConsole || logToFile) { - String s = "\n[t=" + System.currentTimeMillis() + "] " + id + " to LSP4E:\n" + new String(payload); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if (logToConsole) { - logToConsole(s); - } - if (logToFile) { - logToFile(s); - } - } - return bytes; - } - }; - } - return inputStream; - } - - @Override - public OutputStream getOutputStream() { - if (outputStream != null) { - return outputStream; - } - if (provider.getOutputStream() != null) { - outputStream = new FilterOutputStream(provider.getOutputStream()) { - @Override - public void write(byte[] b) throws IOException { - if (logToConsole || logToFile) { - String s = "\n[t=" + System.currentTimeMillis() + "] LSP4E to " + id + ":\n" + new String(b); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if (logToConsole) { - logToConsole(s); - } - if (logToFile) { - logToFile(s); - } - } - super.write(b); - } - }; - } - return outputStream; - } - - @Nullable - @Override - public InputStream getErrorStream() { - if (errorStream != null) { - return errorStream; - } - if (provider.getErrorStream() != null) { - errorStream = new FilterInputStream(provider.getErrorStream()) { - @Override - public int read(byte[] b, int off, int len) throws IOException { - int bytes = super.read(b, off, len); - byte[] payload = new byte[bytes]; - System.arraycopy(b, off, payload, 0, bytes); - if (logToConsole || logToFile) { - String s = "\n[t=" + System.currentTimeMillis() + "] Error from " + id + ":\n" + new String(payload); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if (logToConsole) { - logToConsole(s); - } - if (logToFile) { - logToFile(s); - } - } - return bytes; - } - }; - } - return errorStream; - } - - @Override - public void stop() { - provider.stop(); - } - - @Override - public InputStream forwardCopyTo(InputStream input, OutputStream output) { - return provider.forwardCopyTo(input, output); - } - - @Override - public String getTrace(URI rootUri) { - return provider.getTrace(rootUri); - } - - @Override - public Object getInitializationOptions(URI rootUri) { - return provider.getInitializationOptions(rootUri); - } - - @Override - public Object getExperimentalFeaturesPOJO() { - return provider.getExperimentalFeaturesPOJO(); - } - - @Override - public void handleMessage(Message message, LanguageServer languageServer, URI rootURI) { - provider.handleMessage(message, languageServer, rootURI); - } - - private void logToConsole(String string) { - System.out.println(string); - } - - private void logToFile(String string) { - if (logFile == null) { - return; - } - if (!logFile.exists()) { - try { - if (!logFile.createNewFile()) { - throw new IOException(String.format("Failed to create file %s", logFile.toString())); //$NON-NLS-1$ - } - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - try { - Files.write(logFile.toPath(), string.getBytes(), StandardOpenOption.APPEND); - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - - private File getLogFile() { - if (logFile != null) { - return logFile; - } - File file = new File(id + ".log"); //$NON-NLS-1$ - if (file.exists() && !(file.isFile() && file.canWrite())) { - return null; - } - return file.getAbsoluteFile(); - } - -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerExtensionPointBean.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerExtensionPointBean.java deleted file mode 100644 index dd4398025..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerExtensionPointBean.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import org.jetbrains.annotations.Nullable; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.serviceContainer.BaseKeyedLazyInstance; -import com.intellij.util.xmlb.annotations.Attribute; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.server.StreamConnectionProvider; - -public class ServerExtensionPointBean extends BaseKeyedLazyInstance { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("open-liberty.intellij.server"); - - @Attribute("id") - public String id; - - @Attribute("label") - public String label; - - @Attribute("class") - public String clazz; - - @Attribute("clientImpl") - public String clientImpl; - private Class clientClass; - - @Attribute("serverInterface") - public String serverInterface; - private Class serverClass; - - @Attribute("singleton") - public boolean singleton; - - public Class getClientImpl() throws ClassNotFoundException { - if (clientClass == null) { - clientClass = getPluginDescriptor().getPluginClassLoader().loadClass(clientImpl); - } - return clientClass; - } - - public Class getServerInterface() throws ClassNotFoundException { - if (serverClass == null) { - serverClass = getPluginDescriptor().getPluginClassLoader().loadClass(serverInterface); - } - return serverClass; - } - - @Override - protected @Nullable String getImplementationClassName() { - return clazz; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerMessageHandler.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerMessageHandler.java deleted file mode 100644 index 5d0cfc43a..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ServerMessageHandler.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij; - -import com.intellij.icons.AllIcons; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.ui.Messages; -import org.eclipse.lsp4j.MessageActionItem; -import org.eclipse.lsp4j.MessageParams; -import org.eclipse.lsp4j.MessageType; -import org.eclipse.lsp4j.ShowMessageRequestParams; - -import javax.swing.Icon; -import java.util.concurrent.CompletableFuture; - -public class ServerMessageHandler { - private ServerMessageHandler() { - // this class shouldn't be instantiated - } - - private static final String NAME_PATTERN = "%s (%s)"; //$NON-NLS-1$ - - - public static void logMessage(LanguageServerWrapper wrapper, MessageParams params) { - //TODO: implements message to console - } - - private static Icon messageTypeToIcon(MessageType type) { - Icon result = null; - - switch (type) { - case Error: - result = AllIcons.General.Error; - break; - case Info: - case Log: - result = AllIcons.General.Information; - break; - case Warning: - result = AllIcons.General.Warning; - } - return result; - } - - private static NotificationType messageTypeToNotificationType(MessageType type) { - NotificationType result = null; - - switch (type) { - case Error: - result = NotificationType.ERROR; - break; - case Info: - case Log: - result = NotificationType.INFORMATION; - break; - case Warning: - result = NotificationType.WARNING; - } - return result; - } - - - public static void showMessage(String title, MessageParams params) { - Notification notification = new Notification("Language Server Protocol", messageTypeToIcon(params.getType()), title, null, params.getMessage(), messageTypeToNotificationType(params.getType()), null); - Notifications.Bus.notify(notification); - } - - public static CompletableFuture showMessageRequest(LanguageServerWrapper wrapper, ShowMessageRequestParams params) { - String options[] = params.getActions().stream().map(MessageActionItem::getTitle).toArray(String[]::new); - CompletableFuture future = new CompletableFuture<>(); - - ApplicationManager.getApplication().invokeLater(() -> { - MessageActionItem result = new MessageActionItem(); - int dialogResult = Messages.showIdeaMessageDialog(null, params.getMessage(), wrapper.serverDefinition.label, options, 0, Messages.getInformationIcon(), null); - if (dialogResult != -1) { - result.setTitle(options[dialogResult]); - } - future.complete(result); - }); - return future; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/command/internal/CommandExecutor.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/command/internal/CommandExecutor.java deleted file mode 100644 index bec46f9a6..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/command/internal/CommandExecutor.java +++ /dev/null @@ -1,268 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 - * Fraunhofer FOKUS - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.command.internal; - - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.ex.ActionUtil; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServersRegistry; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.ExecuteCommandOptions; -import org.eclipse.lsp4j.ExecuteCommandParams; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.WorkspaceEdit; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -/** - * This class provides methods to execute {@link Command} instances. - */ -public class CommandExecutor { - private static final Logger LOGGER = LoggerFactory.getLogger(CommandExecutor.class); - - private static final String LSP_COMMAND_CATEGORY_ID = "org.eclipse.lsp4e.commandCategory"; //$NON-NLS-1$ - private static final String LSP_COMMAND_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.commandParameterType"; //$NON-NLS-1$ - private static final String LSP_PATH_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.pathParameterType"; //$NON-NLS-1$ - - /** - * Will execute the given {@code command} either on a language server, - * supporting the command, or on the client, if an {@link IHandler} is - * registered for the ID of the command (see {@link LSPCommandHandler}). If - * {@code command} is {@code null}, then this method will do nothing. If neither - * the server, nor the client are able to handle the command explicitly, a - * heuristic method will try to interpret the command locally. - * - * @param command - * the LSP Command to be executed. If {@code null} this method will - * do nothing. - * @param document - * the document for which the command was created - * @param languageServerId - * the ID of the language server for which the {@code command} is - * applicable. If {@code null}, the command will not be executed on - * the language server. - */ - public static void executeCommand(Project project, Command command, Document document, - String languageServerId) { - if (command == null) { - return; - } - if (executeCommandServerSide(project, command, languageServerId, document)) { - return; - } - if (executeCommandClientSide(command, document)) { - return; - } - // tentative fallback - if (command.getArguments() != null) { - WorkspaceEdit edit = createWorkspaceEdit(command.getArguments(), document); - LSPIJUtils.applyWorkspaceEdit(edit); - } - } - - private static boolean executeCommandServerSide(Project project, Command command, String languageServerId, - Document document) { - if (languageServerId == null) { - return false; - } - LanguageServersRegistry.LanguageServerDefinition languageServerDefinition = LanguageServersRegistry.getInstance() - .getDefinition(languageServerId); - if (languageServerDefinition == null) { - return false; - } - - try { - CompletableFuture languageServerFuture = getLanguageServerForCommand(project, command, document, - languageServerDefinition); - if (languageServerFuture == null) { - return false; - } - // Server can handle command - languageServerFuture.thenAcceptAsync(server -> { - ExecuteCommandParams params = new ExecuteCommandParams(); - params.setCommand(command.getCommand()); - params.setArguments(command.getArguments()); - server.getWorkspaceService().executeCommand(params); - }); - return true; - } catch (IOException e) { - // log and let the code fall through for LSPEclipseUtils to handle - LOGGER.warn(e.getLocalizedMessage(), e); - return false; - } - - } - - private static CompletableFuture getLanguageServerForCommand(Project project, - Command command, - Document document, LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) throws IOException { - CompletableFuture languageServerFuture = LanguageServiceAccessor.getInstance(project) - .getInitializedLanguageServer(document, languageServerDefinition, serverCapabilities -> { - ExecuteCommandOptions provider = serverCapabilities.getExecuteCommandProvider(); - return provider != null && provider.getCommands().contains(command.getCommand()); - }); - return languageServerFuture; - } - - @SuppressWarnings("unused") // ECJ compiler for some reason thinks handlerService == null is always false - private static boolean executeCommandClientSide(Command command, Document document) { - Application workbench = ApplicationManager.getApplication(); - if (workbench == null) { - return false; - } - URI context = LSPIJUtils.toUri(document); - AnAction parameterizedCommand = createEclipseCoreCommand(command, context, workbench); - if (parameterizedCommand == null) { - return false; - } - DataContext dataContext = createDataContext(command, context, workbench); - ActionUtil.invokeAction(parameterizedCommand, dataContext, ActionPlaces.UNKNOWN, null, null); - return true; - } - - private static AnAction createEclipseCoreCommand(Command command, URI context, - Application workbench) { - // Usually commands are defined via extension point, but we synthesize one on - // the fly for the command ID, since we do not want downstream users - // having to define them. - String commandId = command.getCommand(); - return ActionManager.getInstance().getAction(commandId); - } - - private static DataContext createDataContext(Command command, URI context, - Application workbench) { - - return new DataContext() { - @Nullable - @Override - public Object getData(@NotNull String dataId) { - if (LSP_COMMAND_PARAMETER_TYPE_ID.equals(dataId)) { - return command; - } else if (LSP_PATH_PARAMETER_TYPE_ID.equals(dataId)) { - return context; - } - return null; - } - }; - } - - // TODO consider using Entry/SimpleEntry instead - private static final class Pair { - K key; - V value; - - Pair(K key, V value) { - this.key = key; - this.value = value; - } - } - - // this method may be turned public if needed elsewhere - /** - * Very empirical and unsafe heuristic to turn unknown command arguments into a - * workspace edit... - */ - private static WorkspaceEdit createWorkspaceEdit(List commandArguments, Document document) { - WorkspaceEdit res = new WorkspaceEdit(); - Map> changes = new HashMap<>(); - res.setChanges(changes); - URI initialUri = LSPIJUtils.toUri(document); - Pair> currentEntry = new Pair<>(initialUri, new ArrayList<>()); - commandArguments.stream().flatMap(item -> { - if (item instanceof List) { - return ((List) item).stream(); - } else { - return Collections.singleton(item).stream(); - } - }).forEach(arg -> { - if (arg instanceof String) { - changes.put(currentEntry.key.toString(), currentEntry.value); - VirtualFile resource = LSPIJUtils.findResourceFor((String) arg); - if (resource != null) { - currentEntry.key = LSPIJUtils.toUri(resource); - currentEntry.value = new ArrayList<>(); - } - } else if (arg instanceof WorkspaceEdit) { - changes.putAll(((WorkspaceEdit) arg).getChanges()); - } else if (arg instanceof TextEdit) { - currentEntry.value.add((TextEdit) arg); - } else if (arg instanceof Map) { - Gson gson = new Gson(); // TODO? retrieve the GSon used by LS - TextEdit edit = gson.fromJson(gson.toJson(arg), TextEdit.class); - if (edit != null) { - currentEntry.value.add(edit); - } - } else if (arg instanceof JsonPrimitive) { - JsonPrimitive json = (JsonPrimitive) arg; - if (json.isString()) { - changes.put(currentEntry.key.toString(), currentEntry.value); - VirtualFile resource = LSPIJUtils.findResourceFor(json.getAsString()); - if (resource != null) { - currentEntry.key = LSPIJUtils.toUri(resource); - currentEntry.value = new ArrayList<>(); - } - } - } else if (arg instanceof JsonArray) { - Gson gson = new Gson(); // TODO? retrieve the GSon used by LS - JsonArray array = (JsonArray) arg; - array.forEach(elt -> { - TextEdit edit = gson.fromJson(gson.toJson(elt), TextEdit.class); - if (edit != null) { - currentEntry.value.add(edit); - } - }); - } else if (arg instanceof JsonObject) { - Gson gson = new Gson(); // TODO? retrieve the GSon used by LS - WorkspaceEdit wEdit = gson.fromJson((JsonObject) arg, WorkspaceEdit.class); - Map> entries = wEdit.getChanges(); - if (wEdit != null && !entries.isEmpty()) { - changes.putAll(entries); - } else { - TextEdit edit = gson.fromJson((JsonObject) arg, TextEdit.class); - if (edit != null && edit.getRange() != null) { - currentEntry.value.add(edit); - } - } - } - }); - if (!currentEntry.value.isEmpty()) { - changes.put(currentEntry.key.toString(), currentEntry.value); - } - return res; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codeactions/LSPCodeActionIntentionAction.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codeactions/LSPCodeActionIntentionAction.java deleted file mode 100644 index 8796a5614..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codeactions/LSPCodeActionIntentionAction.java +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.codeactions; - -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInspection.util.IntentionFamilyName; -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiFile; -import com.intellij.util.DocumentUtil; -import com.intellij.util.IncorrectOperationException; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServerWrapper; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; - -/** - * LSP code action for Intellij. - */ -public class LSPCodeActionIntentionAction implements IntentionAction { - private final String title; - private final LanguageServerWrapper languageServerWrapper; - private CodeAction codeAction; - private Command command; - - public LSPCodeActionIntentionAction(Either action, LanguageServerWrapper languageServerWrapper) { - this.languageServerWrapper = languageServerWrapper; - if (action.isRight()) { - codeAction = action.getRight(); - title = action.getRight().getTitle(); - } else { - command = action.getLeft(); - title = action.getRight().getTitle(); - } - } - - @Override - public @IntentionName - @NotNull String getText() { - return title; - } - - @Override - public @NotNull - @IntentionFamilyName String getFamilyName() { - return getText(); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return true; - } - - private boolean isCodeActionResolveSupported() { - ServerCapabilities capabilities = this.languageServerWrapper.getServerCapabilities(); - if (capabilities != null) { - Either caProvider = capabilities.getCodeActionProvider(); - if (caProvider.isLeft()) { - // It is wrong, but we need to parse the registerCapability - return caProvider.getLeft(); - } else if (caProvider.isRight()) { - CodeActionOptions options = caProvider.getRight(); - return options.getResolveProvider().booleanValue(); - } - } - return false; - } - - @Override - public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - if (codeAction != null) { - if (codeAction.getEdit() == null && codeAction.getCommand() == null && isCodeActionResolveSupported()) { - // Unresolved code action "edit" property. Resolve it. - languageServerWrapper.getInitializedServer() - .thenApply(ls -> - ls.getTextDocumentService().resolveCodeAction(codeAction) - .thenAccept(resolved -> { - ApplicationManager.getApplication().invokeLater(() -> { - DocumentUtil.writeInRunUndoTransparentAction(() -> { - apply(resolved != null ? resolved : codeAction, project); - }); - }); - }) - ); - } else { - apply(codeAction, project); - } - } else if (command != null) { - executeCommand(command, project); - } else { - // Should never get here - } - } - - private void apply(CodeAction codeaction, @NotNull Project project) { - if (codeaction != null) { - if (codeaction.getEdit() != null) { - LSPIJUtils.applyWorkspaceEdit(codeaction.getEdit(), codeaction.getTitle()); - } - if (codeaction.getCommand() != null) { - executeCommand(codeaction.getCommand(), project); - } - } - } - - private void executeCommand(Command command, @NotNull Project project) { - if (!canSupportCommand(command)) { - return; - } - ExecuteCommandParams params = new ExecuteCommandParams(); - params.setCommand(command.getCommand()); - params.setArguments(command.getArguments()); - languageServerWrapper - .getInitializedServer() - .thenApply(ls -> ls.getWorkspaceService().executeCommand(params) - ); - } - - private boolean canSupportCommand(Command command) { - ServerCapabilities capabilities = this.languageServerWrapper.getServerCapabilities(); - if (capabilities != null) { - ExecuteCommandOptions provider = capabilities.getExecuteCommandProvider(); - return (provider != null && provider.getCommands().contains(command.getCommand())); - } - return false; - } - - @Override - public boolean startInWriteAction() { - return true; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codelens/LSPInlayProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codelens/LSPInlayProvider.java deleted file mode 100644 index 014ab6ada..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/codelens/LSPInlayProvider.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.codelens; - -import com.intellij.codeInsight.hints.ChangeListener; -import com.intellij.codeInsight.hints.FactoryInlayHintsCollector; -import com.intellij.codeInsight.hints.ImmediateConfigurable; -import com.intellij.codeInsight.hints.InlayHintsCollector; -import com.intellij.codeInsight.hints.InlayHintsProvider; -import com.intellij.codeInsight.hints.InlayHintsSink; -import com.intellij.codeInsight.hints.NoSettings; -import com.intellij.codeInsight.hints.SettingsKey; -import com.intellij.codeInsight.hints.presentation.InlayPresentation; -import com.intellij.codeInsight.hints.presentation.MouseButton; -import com.intellij.codeInsight.hints.presentation.PresentationFactory; -import com.intellij.codeInsight.hints.presentation.SequencePresentation; -import com.intellij.ide.DataManager; -import com.intellij.lang.Language; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.actionSystem.impl.SimpleDataContext; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.ui.layout.LCFlags; -import com.intellij.ui.layout.LayoutKt; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.CodeLensParams; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.JComponent; -import java.awt.Component; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -public class LSPInlayProvider implements InlayHintsProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPInlayProvider.class); - - public static final DataKey LSP_COMMAND = DataKey.create("open-liberty.intellij.lsp4ij.command"); - private static final long TIMEOUT = 5L; - - private SettingsKey key = new SettingsKey<>("LSP.hints"); - - @Override - public boolean isVisibleInSettings() { - return true; - } - - @NotNull - @Override - public SettingsKey getKey() { - return key; - } - - @NotNull - @Override - public String getName() { - return "LSP"; - } - - @Nullable - @Override - public String getPreviewText() { - return "Preview"; - } - - @NotNull - @Override - public ImmediateConfigurable createConfigurable(@NotNull NoSettings o) { - return new ImmediateConfigurable() { - @NotNull - @Override - public JComponent createComponent(@NotNull ChangeListener changeListener) { - return LayoutKt.panel(new LCFlags[0], "LSP", builder -> { - return null; - }); - } - }; - } - - @NotNull - @Override - public NoSettings createSettings() { - return new NoSettings(); - } - - @Nullable - @Override - public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile, @NotNull Editor editor, @NotNull NoSettings o, @NotNull InlayHintsSink inlayHintsSink) {return new FactoryInlayHintsCollector(editor) { - @Override - public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) { - try { - URI docURI = LSPIJUtils.toUri(editor.getDocument()); - if (docURI != null) { - CodeLensParams param = new CodeLensParams(new TextDocumentIdentifier(docURI.toString())); - BlockingDeque> pairs = new LinkedBlockingDeque<>(); - List>> codelenses = new ArrayList<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(psiElement.getProject()) - .getLanguageServers(editor.getDocument(), capabilities -> capabilities.getCodeLensProvider() != null) - .thenComposeAsync(languageServers -> CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> languageServer.getTextDocumentService().codeLens(param) - .thenAcceptAsync(codeLenses -> { - // textDocument/codeLens may return null - if (codeLenses != null) { - codeLenses.stream().filter(Objects::nonNull) - .forEach(codeLens -> pairs.add(new Pair(codeLens, languageServer))); - } - })) - .toArray(CompletableFuture[]::new))); - while (!future.isDone() || !pairs.isEmpty()) { - ProgressManager.checkCanceled(); - Pair pair = pairs.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - int offset = LSPIJUtils.toOffset(pair.getFirst().getRange().getStart(), editor.getDocument()); - codelenses.add(Pair.create(offset, pair)); - } - } - Map>>> elements = codelenses.stream().collect(Collectors.groupingBy(p -> p.first)); - elements.forEach((offset,list) -> inlayHintsSink.addBlockElement(offset, true, - true, 0, toPresentation(editor, offset, list, getFactory()))); - //inlayHintsSink.addBlockElement(offset, true, true, 0, toPresentation(editor, offset, pair.getSecond(), getFactory(), pair.getFirst())); - } - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - return false; - } - }; - } - - private InlayPresentation toPresentation(Editor editor, int offset, - List>> elements, - PresentationFactory factory) { - int line = editor.getDocument().getLineNumber(offset); - int column = offset - editor.getDocument().getLineStartOffset(line); - List presentations = new ArrayList<>(); - presentations.add(factory.textSpacePlaceholder(column, true)); - elements.forEach(p -> { - presentations.add(factory.onClick(factory.text(getCodeLensString(p.second.first)), MouseButton.Left, (event, point) -> { - executeClientCommand(p.second.second, p.second.first, (Component) event.getSource(), editor.getProject()); - return null; - })); - presentations.add(factory.textSpacePlaceholder(1, true)); - }); - return new SequencePresentation(presentations); - } - - private void executeClientCommand(LanguageServer languageServer, CodeLens codeLens, Component source, Project project) { - if (LanguageServiceAccessor.getInstance(project).checkCapability(languageServer, - capabilites -> Boolean.TRUE.equals(capabilites.getCodeLensProvider().getResolveProvider()))) { - languageServer.getTextDocumentService().resolveCodeLens(codeLens).thenAcceptAsync(resolvedCodeLens -> { - executeClientCommand(source, resolvedCodeLens); - }); - } else { - executeClientCommand(source, codeLens); - } - } - - private void executeClientCommand(Component source, CodeLens resolvedCodeLens) { - if (resolvedCodeLens.getCommand() != null) { - AnAction action = ActionManager.getInstance().getAction(resolvedCodeLens.getCommand().getCommand()); - if (action != null) { - DataContext context = SimpleDataContext.getSimpleContext(LSP_COMMAND, resolvedCodeLens.getCommand(), DataManager.getInstance().getDataContext(source)); - action.actionPerformed(new AnActionEvent(null, context, - ActionPlaces.UNKNOWN, new Presentation(), - ActionManager.getInstance(), 0)); - } - } - } - - private String getCodeLensString(CodeLens codeLens) { - Command command = codeLens.getCommand(); - if (command == null || command.getTitle().isEmpty()) { - return null; - } - return command.getTitle(); - } - - @Override - public boolean isLanguageSupported(@NotNull Language language) { - return true; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/CompletionProposalTools.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/CompletionProposalTools.java deleted file mode 100644 index cf4a1bd7e..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/CompletionProposalTools.java +++ /dev/null @@ -1,198 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2018 Red Hat Inc. and others. - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Lucas Bullen (Red Hat Inc.) - initial implementation - *******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.completion; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; - -public final class CompletionProposalTools { - - private CompletionProposalTools() { - // to avoid instances, requested by sonar - } - - /** - * The portion of the document leading up to the cursor that is being used as a - * filter for requesting completion assist - * - * @param document - * @param cursorOffset - * @param completionItemFilter - * @param completionInsertionOffset - * @return The longest prefix to the current cursor position that is found - * within the completion's filter regardless of character spacing - */ - public static String getFilterFromDocument(Document document, int cursorOffset, String completionItemFilter, - int completionInsertionOffset) { - if (completionInsertionOffset >= cursorOffset) { - return ""; //$NON-NLS-1$ - } - int prefixToCursorLength = cursorOffset - completionInsertionOffset; - String prefixToCursor = document.getText(new TextRange(completionInsertionOffset, completionInsertionOffset + prefixToCursorLength)); - int i; - for (i = 0; i < prefixToCursor.length(); i++) { - if (!isSubstringFoundOrderedInString( - prefixToCursor.substring(prefixToCursorLength - i - 1, prefixToCursorLength), - completionItemFilter)) { - break; - } - } - return prefixToCursor.substring(prefixToCursor.length() - i); - } - - /** - * If each of the character in the subString are within the given string in - * order - * - * @param subString - * @param string - */ - public static boolean isSubstringFoundOrderedInString(String subString, String string) { - int lastIndex = 0; - subString = subString.toLowerCase(); - string = string.toLowerCase(); - for (Character c : subString.toCharArray()) { - int index = string.indexOf(c, lastIndex); - if (index < 0) { - return false; - } else { - lastIndex = index + 1; - } - } - return true; - } - - /** - * Uses the document's filter and the completion's filter to decided which - * category the match is.
- * Category 1:
- * The full completion filter is found within the document filter without a word - * characters as it's prefix or suffix
- * Category 2:
- * The full completion filter is found within the document filter without a word - * characters as it's prefix
- * Category 3:
- * The full completion filter is found within the document filter
- * Category 4:
- * {@link isSubstringFoundOrderedInString}(documentFilter, completionFilter) == - * true
- * Category 5:
- * Catch all case, usually when all the document's filter's characters are not - * found within the completion filter - * - * @param documentFilter - * @param completionFilter - * @return the category integer - */ - public static int getCategoryOfFilterMatch(String documentFilter, String completionFilter) { - if (documentFilter.isEmpty()) { - return 5; - } - documentFilter = documentFilter.toLowerCase(); - completionFilter = completionFilter.toLowerCase(); - int subIndex = completionFilter.indexOf(documentFilter); - int topCategory = 5; - if (subIndex == -1) { - return isSubstringFoundOrderedInString(documentFilter, completionFilter) ? 4 : 5; - } - while (subIndex != -1) { - if (subIndex > 0 && Character.isLetterOrDigit(completionFilter.charAt(subIndex - 1))) { - topCategory = Math.min(topCategory, 3); - } else if (subIndex + documentFilter.length() < completionFilter.length() - 1 - && Character.isLetterOrDigit(completionFilter.charAt(subIndex + documentFilter.length() + 1))) { - topCategory = Math.min(topCategory, 2); - } else { - topCategory = 1; - } - if (topCategory == 1) { - break; - } - subIndex = completionFilter.indexOf(documentFilter, subIndex + 1); - } - return topCategory; - } - - /** - * Uses the document's filter and the completion's filter to decided how - * successful the match is and gives it a score.
- * The score is decided by the number of character that prefix each of the - * document's filter's characters locations in the competion's filter excluding - * document filter characters that follow other document filter characters.
- *
- * ex.
- * documentFilter: abc
- * completionFilter: xaxxbc
- * result: 5
- * logic:
- * There is 1 character before the 'a' and there is 4 charachters before the - * 'b', because the 'c' is directly after the 'b', it's prefix is ignored,
- * 1+4=5 - * - * @param documentFilter - * @param completionFilter - * @return score of the match where the lower the number, the better the score - * and -1 mean there was no match - */ - public static int getScoreOfFilterMatch(String documentFilter, String completionFilter) { - documentFilter = documentFilter.toLowerCase(); - completionFilter = completionFilter.toLowerCase(); - return getScoreOfFilterMatchHelper(0, documentFilter, completionFilter); - } - - private static int getScoreOfFilterMatchHelper(int prefixLength, String documentFilter, String completionFilter) { - if (documentFilter == null || documentFilter.isEmpty()) { - return 0; - } - char searchChar = documentFilter.charAt(0); - int i = completionFilter.indexOf(searchChar); - if (i == -1) { - return -1; - } - if (documentFilter.length() == 1) { - return i + prefixLength; - } - - int matchLength = lengthOfPrefixMatch(documentFilter, completionFilter.substring(i)); - if (matchLength == documentFilter.length()) { - return i + prefixLength; - } - int bestScore = i + getScoreOfFilterMatchHelper(prefixLength + i + matchLength, - documentFilter.substring(matchLength), - completionFilter.substring(i + matchLength)); - - i = completionFilter.indexOf(searchChar, i + 1); - while (i != -1) { - matchLength = lengthOfPrefixMatch(documentFilter, completionFilter.substring(i)); - if (matchLength == documentFilter.length()) { - return i + prefixLength; - } - int score = i + getScoreOfFilterMatchHelper(prefixLength + i + matchLength, - documentFilter.substring(matchLength), - completionFilter.substring(i + matchLength)); - if (score == i - 1) { - break; - } - bestScore = Math.min(bestScore, score); - i = completionFilter.indexOf(searchChar, i + 1); - } - return prefixLength + bestScore; - } - - private static int lengthOfPrefixMatch(String first, String second) { - int i; - for (i = 0; i < Math.min(first.length(), second.length()); i++) { - if (first.charAt(i) != second.charAt(i)) - break; - } - return i; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSCompletionProposal.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSCompletionProposal.java deleted file mode 100644 index 0071e042e..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSCompletionProposal.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.completion; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.event.DocumentEvent; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.services.LanguageServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LSCompletionProposal extends LSIncompleteCompletionProposal { - private static final Logger LOGGER = LoggerFactory.getLogger(LSCompletionProposal.class); - - public LSCompletionProposal(Editor editor, int offset, CompletionItem item, LanguageServer languageServer) { - super(editor, offset, item, languageServer); - } - - @Override - public boolean validate(Document document, int offset, DocumentEvent event) { - if (item.getLabel() == null || item.getLabel().isEmpty()) { - return false; - } - if (offset < this.bestOffset) { - return false; - } - try { - String documentFilter = getDocumentFilter(offset); - if (!documentFilter.isEmpty()) { - return CompletionProposalTools.isSubstringFoundOrderedInString(documentFilter, getFilterString()); - } else if (item.getTextEdit() != null) { - if (item.getTextEdit().isLeft()) { - return offset == LSPIJUtils.toOffset(item.getTextEdit().getLeft().getRange().getStart(), document); - } else { - return offset == LSPIJUtils.toOffset(item.getTextEdit().getRight().getInsert().getStart(), document); - } - } - } catch (IndexOutOfBoundsException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return true; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSContentAssistProcessor.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSContentAssistProcessor.java deleted file mode 100644 index 27ac7db0c..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSContentAssistProcessor.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.completion; - -import com.intellij.codeInsight.completion.CompletionContributor; -import com.intellij.codeInsight.completion.CompletionParameters; -import com.intellij.codeInsight.completion.CompletionResultSet; -import com.intellij.codeInsight.completion.PrioritizedLookupElement; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionList; -import org.eclipse.lsp4j.CompletionOptions; -import org.eclipse.lsp4j.CompletionParams; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -public class LSContentAssistProcessor extends CompletionContributor { - private static final Logger LOGGER = LoggerFactory.getLogger(LSContentAssistProcessor.class); - - @Override - public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { - Document document = parameters.getEditor().getDocument(); - Editor editor = parameters.getEditor(); - Project project = parameters.getOriginalFile().getProject(); - int offset = parameters.getOffset(); - CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(project, document); - CompletionParams param; - try { - /* - process the responses out of the completable loop as it may cause deadlock if user is typing - more characters as toProposals will require as read lock that this thread already have and - async processing is occuring on a separate thread. - */ - param = LSPIJUtils.toCompletionParams(LSPIJUtils.toUri(document), offset, document); - BlockingDeque, CompletionList>, LanguageServer>> proposals = new LinkedBlockingDeque<>(); - CompletableFuture future = completionLanguageServersFuture - .thenComposeAsync(languageServers -> CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> languageServer.getTextDocumentService().completion(param) - .thenAcceptAsync(completion -> proposals.add(new Pair<>(completion, languageServer)))) - .toArray(CompletableFuture[]::new))); - while (!future.isDone() || !proposals.isEmpty()) { - ProgressManager.checkCanceled(); - Pair, CompletionList>, LanguageServer> pair = proposals.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - result.addAllElements(toProposals(project, editor, document, offset, pair.getFirst(), - pair.getSecond())); - } - - } - } catch (RuntimeException | InterruptedException e) { - if (e instanceof ProcessCanceledException) { - // quietly log if the process was cancelled - LOGGER.trace("LSPContentAssistProcessor process cancelled: ", (ProcessCanceledException) e); - } else { - LOGGER.warn(e.getLocalizedMessage(), e); - } - result.addElement(createErrorProposal(offset, e)); - } - super.fillCompletionVariants(parameters, result); - } - - private Collection toProposals(Project project, Editor editor, Document document, - int offset, Either, - CompletionList> completion, LanguageServer languageServer) { - if (completion != null) { - List items = completion.isLeft()?completion.getLeft():completion.getRight().getItems(); - boolean isIncomplete = completion.isLeft()?false:completion.getRight().isIncomplete(); - // Added check for when completion item kind is null, needed for Liberty LS integration - return items.stream().map(item -> createLookupItem(project, editor, offset, item, isIncomplete, languageServer)). - filter(item -> item.validate(document, offset, null)). - map(item -> PrioritizedLookupElement.withGrouping(item, item.getItem().getKind() != null ? item.getItem().getKind().getValue() : 0)). - collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - private LSIncompleteCompletionProposal createLookupItem(Project project, Editor editor, int offset, - CompletionItem item, boolean isIncomplete, - LanguageServer languageServer) { - return isIncomplete?new LSIncompleteCompletionProposal(editor, offset, item, languageServer): - new LSCompletionProposal(editor, offset, item, languageServer); - } - - - private LookupElement createErrorProposal(int offset, Exception ex) { - return LookupElementBuilder.create("Error while computing completion", ""); - } - - private CompletableFuture> initiateLanguageServers(Project project, Document document) { - return LanguageServiceAccessor.getInstance(project).getLanguageServers(document, - capabilities -> { - CompletionOptions provider = capabilities.getCompletionProvider(); - if (provider != null) { - return true; - } - return false; - }); - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSIncompleteCompletionProposal.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSIncompleteCompletionProposal.java deleted file mode 100644 index 86367a4c5..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/completion/LSIncompleteCompletionProposal.java +++ /dev/null @@ -1,450 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 Red Hat, Inc. and others - * 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 - * IBM Corporation - add variable handling - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.completion; - -import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; -import com.intellij.codeInsight.completion.CompletionInitializationContext; -import com.intellij.codeInsight.completion.InsertionContext; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.util.TextRange; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.command.internal.CommandExecutor; -import org.apache.commons.lang.StringUtils; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.InsertReplaceEdit; -import org.eclipse.lsp4j.InsertTextFormat; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class LSIncompleteCompletionProposal extends LookupElement { - private static final Logger LOGGER = LoggerFactory.getLogger(LSIncompleteCompletionProposal.class); - - // Those variables should be defined in LSP4J and reused here whenever done there - // See https://github.com/eclipse/lsp4j/issues/149 - /** The currently selected text or the empty string */ - private static final String TM_SELECTED_TEXT = "TM_SELECTED_TEXT"; //$NON-NLS-1$ - /** The contents of the current line */ - private static final String TM_CURRENT_LINE = "TM_CURRENT_LINE"; //$NON-NLS-1$ - /** The contents of the word under cursor or the empty string */ - private static final String TM_CURRENT_WORD = "TM_CURRENT_WORD"; //$NON-NLS-1$ - /** The zero-index based line number */ - private static final String TM_LINE_INDEX = "TM_LINE_INDEX"; //$NON-NLS-1$ - /** The one-index based line number */ - private static final String TM_LINE_NUMBER = "TM_LINE_NUMBER"; //$NON-NLS-1$ - /** The filename of the current document */ - private static final String TM_FILENAME = "TM_FILENAME"; //$NON-NLS-1$ - /** The filename of the current document without its extensions */ - private static final String TM_FILENAME_BASE = "TM_FILENAME_BASE"; //$NON-NLS-1$ - /** The directory of the current document */ - private static final String TM_DIRECTORY = "TM_DIRECTORY"; //$NON-NLS-1$ - /** The full file path of the current document */ - private static final String TM_FILEPATH = "TM_FILEPATH"; //$NON-NLS-1$ - - protected final CompletionItem item; - protected final int initialOffset; - protected int currentOffset; - protected int bestOffset; - protected final Editor editor; - private Integer rankCategory; - private Integer rankScore; - private String documentFilter; - private String documentFilterAddition = ""; //$NON-NLS-1$ - protected final LanguageServer languageServer; - - public LSIncompleteCompletionProposal(Editor editor, int offset, CompletionItem item, LanguageServer languageServer) { - this.item = item; - this.editor = editor; - this.languageServer = languageServer; - this.initialOffset = offset; - this.currentOffset = offset; - this.bestOffset = getPrefixCompletionStart(editor.getDocument(), offset); - //this.bestOffset = offset; - putUserData(CodeCompletionHandlerBase.DIRECT_INSERTION, true); - } - - /** - * See {@link CompletionProposalTools.getFilterFromDocument} for filter - * generation logic - * - * @return The document filter for the given offset - */ - public String getDocumentFilter(int offset) throws StringIndexOutOfBoundsException { - if (documentFilter != null) { - if (offset != currentOffset) { - documentFilterAddition = editor.getDocument().getText(new TextRange(initialOffset, offset)); - rankScore = null; - rankCategory = null; - currentOffset = offset; - } - return documentFilter + documentFilterAddition; - } - currentOffset = offset; - return getDocumentFilter(); - } - - /** - * See {@link CompletionProposalTools.getFilterFromDocument} for filter - * generation logic - * - * @return The document filter for the last given offset - */ - public String getDocumentFilter() { - if (documentFilter != null) { - return documentFilter + documentFilterAddition; - } - documentFilter = CompletionProposalTools.getFilterFromDocument(editor.getDocument(), currentOffset, - getFilterString(), bestOffset); - documentFilterAddition = ""; //$NON-NLS-1$ - return documentFilter; - } - - - protected String getInsertText() { - String insertText = this.item.getInsertText(); - Either eitherTextEdit = this.item.getTextEdit(); - if (eitherTextEdit != null) { - if(eitherTextEdit.isLeft()) { - insertText = eitherTextEdit.getLeft().getNewText(); - } else { - insertText = eitherTextEdit.getRight().getNewText(); - } - } - if (insertText == null) { - insertText = this.item.getLabel(); - } - return insertText; - } - - public int getPrefixCompletionStart(Document document, int completionOffset) { - Either textEdit = this.item.getTextEdit(); - if (textEdit != null) { - if (textEdit.isLeft()) { - try { - return LSPIJUtils.toOffset(this.item.getTextEdit().getLeft().getRange().getStart(), document); - } catch (RuntimeException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } else { - try { - return LSPIJUtils.toOffset(this.item.getTextEdit().getRight().getInsert().getStart(), document); - } catch (RuntimeException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - } - String insertText = getInsertText(); - try { - int startOffset = Math.max(0, completionOffset - insertText.length()); - int endOffset = Math.min(startOffset + insertText.length(), completionOffset); - String subDoc = document.getText(new TextRange(startOffset, endOffset)); - for (int i = 0; i < insertText.length() && i < completionOffset; i++) { - String tentativeCommonString = subDoc.substring(i); - if (insertText.startsWith(tentativeCommonString)) { - return completionOffset - tentativeCommonString.length(); - } - } - } catch (RuntimeException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return completionOffset; - } - - - @NotNull - @Override - public String getLookupString() { - String lookup = StringUtils.isNotBlank(item.getSortText())?item.getSortText():item.getLabel(); - if (lookup.charAt(0) == '@') { - return lookup.substring(1); - } - return lookup; - } - - private boolean isDeprecated() { - return item.getDeprecated() != null && item.getDeprecated().booleanValue(); - } - - @Override - public void renderElement(LookupElementPresentation presentation) { - presentation.setItemText(item.getLabel()); - if (isDeprecated()) { - presentation.setStrikeout(true); - } - } - - protected void apply(Document document, char trigger, int stateMask, int offset) { - String insertText = null; - Either eitherTextEdit = item.getTextEdit(); - TextEdit textEdit = null; - if (eitherTextEdit != null) { - if(eitherTextEdit.isLeft()) { - textEdit = eitherTextEdit.getLeft(); - } else { - // trick to partially support the new InsertReplaceEdit from LSP 3.16. Reuse previously code for TextEdit. - InsertReplaceEdit insertReplaceEdit = eitherTextEdit.getRight(); - textEdit = new TextEdit(insertReplaceEdit.getInsert(), insertReplaceEdit.getNewText()); - } - } - try { - if (textEdit == null) { - insertText = getInsertText(); - Position start = LSPIJUtils.toPosition(this.bestOffset, document); - Position end = LSPIJUtils.toPosition(offset, document); // need 2 distinct objects - textEdit = new TextEdit(new Range(start, end), insertText); - } else if (offset > this.initialOffset) { - // characters were added after completion was activated - int shift = offset - this.initialOffset; - textEdit.getRange().getEnd().setCharacter(textEdit.getRange().getEnd().getCharacter() + shift); - } - { // workaround https://github.com/Microsoft/vscode/issues/17036 - Position start = textEdit.getRange().getStart(); - Position end = textEdit.getRange().getEnd(); - if (start.getLine() > end.getLine() || (start.getLine() == end.getLine() && start.getCharacter() > end.getCharacter())) { - textEdit.getRange().setEnd(start); - textEdit.getRange().setStart(end); - } - } - { // allow completion items to be wrong with a too wide range - Position documentEnd = LSPIJUtils.toPosition(document.getTextLength(), document); - Position textEditEnd = textEdit.getRange().getEnd(); - if (documentEnd.getLine() < textEditEnd.getLine() - || (documentEnd.getLine() == textEditEnd.getLine() && documentEnd.getCharacter() < textEditEnd.getCharacter())) { - textEdit.getRange().setEnd(documentEnd); - } - } - - if (insertText != null) { - // try to reuse existing characters after completion location - int shift = offset - this.bestOffset; - int commonSize = 0; - while (commonSize < insertText.length() - shift - && document.getTextLength() > offset + commonSize - && document.getText().charAt(this.bestOffset + shift + commonSize) == insertText.charAt(commonSize + shift)) { - commonSize++; - } - textEdit.getRange().getEnd().setCharacter(textEdit.getRange().getEnd().getCharacter() + commonSize); - } - insertText = textEdit.getNewText(); - int insertionOffset = LSPIJUtils.toOffset(textEdit.getRange().getStart(), document); - insertionOffset = computeNewOffset(item.getAdditionalTextEdits(), insertionOffset, document); - if (item.getInsertTextFormat() == InsertTextFormat.Snippet) { - int currentSnippetOffsetInInsertText = 0; - while ((currentSnippetOffsetInInsertText = insertText.indexOf('$', currentSnippetOffsetInInsertText)) != -1) { - StringBuilder keyBuilder = new StringBuilder(); - boolean isChoice = false; - boolean isVariable = false; - List snippetProposals = new ArrayList<>(); - int offsetInSnippet = 1; - while (currentSnippetOffsetInInsertText + offsetInSnippet < insertText.length() && Character.isDigit(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet))) { - // handle numbered substitutions like '$3' - keyBuilder.append(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet)); - offsetInSnippet++; - } - if (keyBuilder.length() == 0 && insertText.substring(currentSnippetOffsetInInsertText).startsWith("${")) { //$NON-NLS-1$ - offsetInSnippet = 2; - while (currentSnippetOffsetInInsertText + offsetInSnippet < insertText.length() && Character.isDigit(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet))) { - // handle numbered substitutions with defaults or choices e.g. ${3:def} - keyBuilder.append(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet)); - offsetInSnippet++; - } - while (currentSnippetOffsetInInsertText + offsetInSnippet < insertText.length() && - (Character.isLetter(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet)) || - insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet) == '_')) { //$NON-NLS-1$ - // Allow variables of the form ${Abc_Def} as recognized by getVariableValue() - isVariable = true; - keyBuilder.append(insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet)); - offsetInSnippet++; - } - if (currentSnippetOffsetInInsertText + offsetInSnippet < insertText.length()) { - char currentChar = insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet); - if (currentChar == ':' || currentChar == '|') { - isChoice |= currentChar == '|'; - offsetInSnippet++; - } - } - boolean close = false; - StringBuilder valueBuilder = new StringBuilder(); - while (currentSnippetOffsetInInsertText + offsetInSnippet < insertText.length() && !close) { - char currentChar = insertText.charAt(currentSnippetOffsetInInsertText + offsetInSnippet); - if (valueBuilder.length() > 0 && - ((isChoice && (currentChar == ',' || currentChar == '|') || currentChar == '}'))) { - String value = valueBuilder.toString(); - if (value.startsWith("$")) { //$NON-NLS-1$ - String varValue = getVariableValue(value.substring(1)); - if (varValue != null) { - value = varValue; - } - } - snippetProposals.add(value); - valueBuilder = new StringBuilder(); - } else if (currentChar != '}') { - valueBuilder.append(currentChar); - } - close = currentChar == '}'; - offsetInSnippet++; - } - if (isVariable) { // the variable's closing '}' is handled in the previous step - String value = getVariableValue(keyBuilder.toString()); - if (value != null) { - snippetProposals.add(value); - } - } - } - String defaultProposal = snippetProposals.isEmpty() ? "" : snippetProposals.get(0); //$NON-NLS-1$ - if (keyBuilder.length() > 0) { - String key = keyBuilder.toString(); - insertText = insertText.substring(0, currentSnippetOffsetInInsertText) + defaultProposal + insertText.substring(currentSnippetOffsetInInsertText + offsetInSnippet); - currentSnippetOffsetInInsertText += defaultProposal.length(); - } else { - currentSnippetOffsetInInsertText++; - } - } - } - textEdit.setNewText(insertText); // insertText now has placeholder removed - List additionalEdits = item.getAdditionalTextEdits(); - if (additionalEdits != null && !additionalEdits.isEmpty()) { - List allEdits = new ArrayList<>(); - allEdits.add(textEdit); - allEdits.addAll(additionalEdits); - LSPIJUtils.applyEdits(editor, document, allEdits); - } else { - LSPIJUtils.applyEdits(editor, document, Collections.singletonList(textEdit)); - } - - LanguageServiceAccessor.getInstance(editor.getProject()).resolveServerDefinition(languageServer).map(definition -> definition.id) - .ifPresent(id -> { - Command command = item.getCommand(); - if (command == null) { - return; - } - CommandExecutor.executeCommand(editor.getProject(), command, document, id); - }); - } catch (RuntimeException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - } - - private int computeNewOffset(List additionalTextEdits, int insertionOffset, Document doc) { - if (additionalTextEdits != null && !additionalTextEdits.isEmpty()) { - int adjustment = 0; - for (TextEdit edit : additionalTextEdits) { - try { - Range rng = edit.getRange(); - int start = LSPIJUtils.toOffset(rng.getStart(), doc); - if (start <= insertionOffset) { - int end = LSPIJUtils.toOffset(rng.getEnd(), doc); - int orgLen = end - start; - int newLeng = edit.getNewText().length(); - int editChange = newLeng - orgLen; - adjustment += editChange; - } - } catch (RuntimeException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } - return insertionOffset + adjustment; - } - return insertionOffset; - } - - private String getVariableValue(String variableName) { - Document document = editor.getDocument(); - switch (variableName) { - case TM_FILENAME_BASE: - String fileName = LSPIJUtils.getFile(document).getNameWithoutExtension(); - return fileName != null ? fileName : ""; //$NON-NLS-1$ - case TM_FILENAME: - return LSPIJUtils.getFile(document).getName(); - case TM_FILEPATH: - return LSPIJUtils.getFile(document).getPath(); - case TM_DIRECTORY: - return LSPIJUtils.getFile(document).getParent().getPath(); - case TM_LINE_INDEX: - int lineIndex = getTextEditRange().getStart().getLine(); - return Integer.toString(lineIndex); - case TM_LINE_NUMBER: - int lineNumber = getTextEditRange().getStart().getLine(); - return Integer.toString(lineNumber + 1); - case TM_CURRENT_LINE: - int currentLineIndex = getTextEditRange().getStart().getLine(); - try { - int lineOffsetStart = document.getLineStartOffset(currentLineIndex); - int lineOffsetEnd = document.getLineEndOffset(currentLineIndex); - String line = document.getText(new TextRange(lineOffsetStart, lineOffsetEnd)); - return line; - } catch (RuntimeException e) { - LOGGER.warn(e.getMessage(), e); - return ""; //$NON-NLS-1$ - } - case TM_SELECTED_TEXT: - Range selectedRange = getTextEditRange(); - try { - int startOffset = LSPIJUtils.toOffset(selectedRange.getStart(), document); - int endOffset = LSPIJUtils.toOffset(selectedRange.getEnd(), document); - String selectedText = document.getText(new TextRange(startOffset, endOffset)); - return selectedText; - } catch (RuntimeException e) { - LOGGER.warn(e.getMessage(), e); - return ""; //$NON-NLS-1$ - } - case TM_CURRENT_WORD: - return ""; //$NON-NLS-1$ - default: - return null; - } - } - - private Range getTextEditRange() { - if (item.getTextEdit().isLeft()) { - return item.getTextEdit().getLeft().getRange(); - } else { - // here providing insert range, currently do not know if insert or replace is requested - return item.getTextEdit().getRight().getInsert(); - } - } - - public String getFilterString() { - if (item.getFilterText() != null && !item.getFilterText().isEmpty()) { - return item.getFilterText(); - } - return item.getLabel(); - } - - @Override - public void handleInsert(@NotNull InsertionContext context) { - apply(context.getDocument(), context.getCompletionChar(), 0, context.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET)); - } - - public boolean validate(Document document, int offset, DocumentEvent event) { - return true; - } - - public CompletionItem getItem() { - return item; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java deleted file mode 100644 index 1f3e26917..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.lang.annotation.AnnotationBuilder; -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.ExternalAnnotator; -import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPVirtualFileWrapper; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; - -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.List; - -/** - * Intellij {@link ExternalAnnotator} implementation which get the current LSP diagnostics for a given file and translate - * them into Intellij {@link com.intellij.lang.annotation.Annotation}. - */ -public class LSPDiagnosticAnnotator extends ExternalAnnotator { - - @Nullable - @Override - public LSPVirtualFileWrapper collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { - try { - return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file.getVirtualFile()); - } catch (Exception e) { - return null; - } - } - - @Override - public @Nullable LSPVirtualFileWrapper doAnnotate(LSPVirtualFileWrapper wrapper) { - return wrapper; - } - - @Override - public void apply(@NotNull PsiFile file, LSPVirtualFileWrapper editorWrapper, @NotNull AnnotationHolder holder) { - // Get current LSP diagnostics of the current file - LSPVirtualFileWrapper fileWrapper = LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file.getVirtualFile()); - final Collection diagnosticsPerServer = fileWrapper.getAllDiagnostics(); - Document document = LSPIJUtils.getDocument(file.getVirtualFile()); - - // Loop for language server which report diagnostics for the given file - diagnosticsPerServer.forEach(ds -> { - boolean codeActionsLoading = false; - // Loop for LSP diagnostics to transform it to Intellij annotation. - for (Diagnostic diagnostic : ds.getDiagnostics()) { - codeActionsLoading = codeActionsLoading | createAnnotation(diagnostic, document, ds, holder); - } - if (codeActionsLoading) { - // QuickFixes are loading, refresh them in a background thread - ds.refreshQuickFixesIfNeeded(); - } - }); - } - - private static boolean createAnnotation(Diagnostic diagnostic, Document document, LSPDiagnosticsForServer diagnosticsForServer, AnnotationHolder holder) { - final int start = LSPIJUtils.toOffset(diagnostic.getRange().getStart(), document); - final int end = LSPIJUtils.toOffset(diagnostic.getRange().getEnd(), document); - if (start >= end) { - // Language server reports invalid diagnostic, ignore it. - return false; - } - // Collect information required to create Intellij Annotations - HighlightSeverity severity = toHighlightSeverity(diagnostic.getSeverity()); - TextRange range = new TextRange(start, end); - String message = diagnostic.getMessage(); - List fixes = diagnosticsForServer.getQuickFixesFor(diagnostic); - - // Create Intellij Annotation from the given LSP diagnostic - AnnotationBuilder builder = holder - .newAnnotation(severity, message) - .range(range); - - // Register quick fixes if there are available - boolean codeActionsLoading = LSPDiagnosticsForServer.isCodeActionsLoading(fixes); - if (!codeActionsLoading) { - for (IntentionAction fix : fixes) { - builder.withFix(fix); - } - } - builder.create(); - return codeActionsLoading; - } - - private static HighlightSeverity toHighlightSeverity(DiagnosticSeverity severity) { - if (severity == null) { - return HighlightSeverity.ERROR; - } - switch (severity) { - case Warning: - return HighlightSeverity.WEAK_WARNING; - case Hint: - case Information: - return HighlightSeverity.INFORMATION; - default: - return HighlightSeverity.ERROR; - } - } - -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java deleted file mode 100644 index 18f1cc2e9..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPVirtualFileWrapper; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServerWrapper; -import org.eclipse.lsp4j.PublishDiagnosticsParams; - -import java.util.function.Consumer; - -/** - * Utility class which receive LSP {@link PublishDiagnosticsParams} - * from a language server and refresh the Annotation of the Intellij editor. - * - * @author Angelo ZERR - */ -public class LSPDiagnosticHandler implements Consumer { - - private final LanguageServerWrapper languageServerWrapper; - - public LSPDiagnosticHandler(LanguageServerWrapper languageServerWrapper) { - this.languageServerWrapper = languageServerWrapper; - } - - @Override - public void accept(PublishDiagnosticsParams params) { - ApplicationManager.getApplication().runReadAction(() -> { - VirtualFile file = LSPIJUtils.findResourceFor(params.getUri()); - if (file == null) { - return; - } - Module module = LSPIJUtils.getProject(file); - if (module == null) { - return; - } - Project project = module.getProject(); - if (project.isDisposed()) { - return; - } - final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - if (psiFile == null) { - return; - } - LSPVirtualFileWrapper wrapper = LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file); - synchronized (wrapper) { - // Update LSP diagnostic reported by the language server id - wrapper.updateDiagnostics(params.getDiagnostics(), languageServerWrapper); - } - // Trigger Intellij validation to execute - // {@link com.redhat.devtools.intellij.quarkus.lsp4ij.operations.diagnostics.LSPDiagnosticAnnotator}. - // which translates LSP Diagnostics into Intellij Annotation - DaemonCodeAnalyzer.getInstance(module.getProject()).restart(psiFile); - }); - } -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java deleted file mode 100644 index 5e17fcbb2..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java +++ /dev/null @@ -1,266 +0,0 @@ -/******************************************************************************* - * 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.CompletableFutures; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServerWrapper; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.codeactions.LSPCodeActionIntentionAction; -import org.eclipse.lsp4j.*; - -import java.net.URI; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -/** - * LSP diagnostics holder for a file reported by a language server. This class holds: - * - *
    - *
  • the current LSP diagnostics reported by the language server.
  • - *
  • load for each diagnostic the available LSP code actions (QuickFix)
  • - *
- * - * @author Angelo ZERR - */ -public class LSPDiagnosticsForServer { - - private static final List CODE_ACTIONS_LOADING = new ArrayList<>(); - - private final LanguageServerWrapper languageServerWrapper; - - private final boolean codeActionSupported; - - private final VirtualFile file; - - // Map which contains all current diagnostics (as key) and future which load associated quick fixes (as value) - private Map>> diagnostics; - - // Future which refreshes Intellij QuickFixes when all code actions of all diagnostics are loaded. - private CompletableFuture refreshQuickFixes; - - public LSPDiagnosticsForServer(LanguageServerWrapper languageServerWrapper, VirtualFile file) { - this.languageServerWrapper = languageServerWrapper; - this.codeActionSupported = isCodeActionSupported(languageServerWrapper); - this.file = file; - this.diagnostics = Collections.emptyMap(); - } - - private static boolean isCodeActionSupported(LanguageServerWrapper languageServerWrapper) { - if (!languageServerWrapper.isActive()) { - // This use-case comes from when a diagnostics is published and the language server is stopped - // We cannot use here languageServerWrapper.getServerCapabilities() otherwise it will restart the language server. - return false; - } - ServerCapabilities serverCapabilities = languageServerWrapper.getServerCapabilities(); - return serverCapabilities != null && LSPIJUtils.hasCapability(serverCapabilities.getCodeActionProvider()); - } - - /** - * Update the new LSP published diagnosics. - * - * @param diagnostics the new LSP published diagnosics - */ - public void update(List diagnostics) { - // cancel futures - cancel(diagnostics); - // initialize diagnostics map - this.diagnostics = toMap(diagnostics, this.diagnostics); - } - - private static Map>> toMap(List diagnostics, Map>> existingsDiagnostics) { - Map>> map = new HashMap<>(diagnostics.size()); - for (Diagnostic diagnostic : diagnostics) { - // Try to get existing code actions for the given diagnostic to avoid reloading them - CompletableFuture> existingQuickFixesFuture = existingsDiagnostics.get(diagnostic); - if (existingQuickFixesFuture != null && (existingQuickFixesFuture.isCancelled() || existingQuickFixesFuture.isCompletedExceptionally())) { - // The existing quickfixes future has been cancelled or completed with exception,don't reuse it. - existingQuickFixesFuture = null; - } - map.put(diagnostic, existingQuickFixesFuture); - } - return map; - } - - private void cancel(List newDiagnostics) { - // Cancel futures which have loaded code actions - for (Map.Entry>> entry : this.diagnostics.entrySet()) { - Diagnostic diagnostic = entry.getKey(); - CompletableFuture> quickFixesFuture = entry.getValue(); - if (shouldCancelQuickFixFuture(quickFixesFuture, diagnostic, newDiagnostics)) { - quickFixesFuture.cancel(true); - } - } - - // Cancel the future which refreshes the validation again to takes care of QuickFix which are loaded. - if (refreshQuickFixes != null) { - refreshQuickFixes.cancel(true); - refreshQuickFixes = null; - } - } - - private static boolean shouldCancelQuickFixFuture(CompletableFuture> quickFixesFuture, Diagnostic diagnostic, List newDiagnostics) { - if (quickFixesFuture == null) { - // The quickfix future is null, don't cancel it. - return false; - } - if (!newDiagnostics.contains(diagnostic)) { - // The quickfix future doesn't belong to the new reported diagnostics, cancel it. - return true; - } - // Reuse the quickfix future - return false; - } - - /** - * Refresh Intellij QuickFixes when all code actions for all diagnostics are loaded. - */ - public void refreshQuickFixesIfNeeded() { - refreshQuickFixes = CompletableFutures - .computeAsyncCompose(cancelChecker -> { - return CompletableFuture.allOf(diagnostics - .values() - .toArray(new CompletableFuture[diagnostics.values().size()])) - .thenRun(() -> { - // All quickfix are loaded - if (cancelChecker.isCanceled()) { - return; - } - ApplicationManager.getApplication().runReadAction(() -> { - Project project = LSPIJUtils.getProject(file).getProject(); - if (project.isDisposed()) { - return; - } - Document document = LSPIJUtils.getDocument(file); - - final PsiFile file = PsiDocumentManager.getInstance(project) - .getCachedPsiFile(document); - if (file == null) { - return; - } - if (cancelChecker.isCanceled()) { - return; - } - // Refresh the Intellij validation to update quickfixes - DaemonCodeAnalyzer.getInstance(project).restart(file); - }); - }); - }); - - } - - /** - * Returns the current diagnostics for the file reported by the language server. - * - * @return - */ - public Set getDiagnostics() { - return diagnostics.keySet(); - } - - /** - * Returns Intellij quickfixes for the given diagnostic if there available. - * - * @param diagnostic the diagnostic. - * @return Intellij quickfixes for the given diagnostic if there available. - */ - public List getQuickFixesFor(Diagnostic diagnostic) { - if (!codeActionSupported || diagnostics.isEmpty()) { - return Collections.emptyList(); - } - // Get future which load QuickFix for the given diagnostic - CompletableFuture> fixes = diagnostics.get(diagnostic); - if (!CompletableFutures.isDoneNormally(fixes)) { - // Load code actions - synchronized (diagnostics) { - fixes = loadCodeActionsFor(diagnostic); - diagnostics.put(diagnostic, fixes); - } - } - // Try to get code action from the future now - List result = fixes.getNow(null); - if (result != null) { - return result; - } - // Specify that code actions are loading. - return CODE_ACTIONS_LOADING; - } - - /** - * Returns true if the given result is a code actions loading and false otherwise. - * - * @param result the code actions. - * - * @return true if the given result is a code actions loading and false otherwise. - */ - public static boolean isCodeActionsLoading(List result) { - return result == CODE_ACTIONS_LOADING; - } - - /** - * load code actions for the given diagnostic. - * - * @param diagnostic the LSP diagnostic. - * @return list of Intellij {@link IntentionAction} which are used to create Intellij QuickFix. - */ - private CompletableFuture> loadCodeActionsFor(Diagnostic diagnostic) { - return CompletableFutures - .computeAsyncCompose(cancelChecker -> { - return languageServerWrapper - .getInitializedServer() - .thenCompose(ls -> { - // Language server is initialized here - cancelChecker.checkCanceled(); - - // Collect code action for the given file by using the language server - CodeActionParams params = createCodeActionParams(diagnostic, file); - return ls.getTextDocumentService() - .codeAction(params) - .thenApply(codeActions -> { - // Code action are collected here - cancelChecker.checkCanceled(); - if (codeActions == null || codeActions.isEmpty()) { - return Collections.emptyList(); - } - // Translate LSP code action into Intellij IntentionAction - return codeActions - .stream() - .map(ca -> new LSPCodeActionIntentionAction(ca, languageServerWrapper)) - .collect(Collectors.toList()); - }); - }); - }); - } - - - private static CodeActionParams createCodeActionParams(Diagnostic diagnostic, VirtualFile file) { - CodeActionParams params = new CodeActionParams(); - URI fileUri = LSPIJUtils.toUri(file); - params.setTextDocument(LSPIJUtils.toTextDocumentIdentifier(fileUri)); - Range range = diagnostic.getRange(); - params.setRange(range); - - CodeActionContext context = new CodeActionContext(Arrays.asList(diagnostic)); - params.setContext(context); - return params; - } -} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPInspectionToolProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPInspectionToolProvider.java deleted file mode 100644 index a84d3573c..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPInspectionToolProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.codeInspection.InspectionToolProvider; -import org.jetbrains.annotations.NotNull; - -public class LSPInspectionToolProvider implements InspectionToolProvider { - @NotNull - @Override - public Class[] getInspectionClasses() { - return new Class[] { LSPLocalInspectionTool.class}; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPLocalInspectionTool.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPLocalInspectionTool.java deleted file mode 100644 index 7d7171111..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPLocalInspectionTool.java +++ /dev/null @@ -1,112 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 Red Hat, Inc. and others - * 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 - * IBM Corp - update checkFile() - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.codeInspection.InspectionManager; -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.ProblemHighlightType; -import com.intellij.codeInspection.ex.ExternalAnnotatorBatchInspection; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiUtilCore; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServerWrapper; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class LSPLocalInspectionTool extends LocalInspectionTool implements ExternalAnnotatorBatchInspection { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPLocalInspectionTool.class); - - @Nls - @NotNull - @Override - public String getDisplayName() { - return "LSP"; - } - - @Nls - @NotNull - @Override - public String getGroupDisplayName() { - return "LSP"; - } - - @Override - public boolean isEnabledByDefault() { - return true; - } - - @Nullable - @Override - public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { - // VirtualFile virtualFile = file.getVirtualFile(); - // if (virtualFile != null) { - // Editor editor = LSPIJUtils.editorForFile(virtualFile); - // if (editor != null) { - // // initializes language servers and connects them to the given document if they haven't already started, do not wait for them to start (avoiding blocking the main thread) - // LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(editor.getDocument(), - // capabilities -> true); - // List problemDescriptors = new ArrayList<>(); - // // get the started language servers and check if there are any valid markers (diagnostics) to create problemDescriptors for - // for (LanguageServerWrapper wrapper : LanguageServiceAccessor.getInstance(file.getProject()).getMatchingStartedWrappers(virtualFile, serverCapabilities -> true)) { - // RangeHighlighter[] highlighters = LSPDiagnosticsToMarkers.getMarkers(editor, wrapper.serverDefinition.id); - // if (highlighters != null) { - // for (RangeHighlighter highlighter : highlighters) { - // PsiElement element; - // if (highlighter.getEndOffset() - highlighter.getStartOffset() > 0) { - // element = new LSPPSiElement(editor.getProject(), file, highlighter.getStartOffset(), highlighter.getEndOffset(), editor.getDocument().getText(new TextRange(highlighter.getStartOffset(), highlighter.getEndOffset()))); - // } else { - // element = PsiUtilCore.getElementAtOffset(file, highlighter.getStartOffset()); - // } - // ProblemHighlightType highlightType = getHighlightType(((Diagnostic) highlighter.getErrorStripeTooltip()).getSeverity()); - // problemDescriptors.add(manager.createProblemDescriptor(element, ((Diagnostic) highlighter.getErrorStripeTooltip()).getMessage(), true, highlightType, isOnTheFly)); - // } - // } - // } - // return problemDescriptors.toArray(new ProblemDescriptor[problemDescriptors.size()]); - // } - // } - return super.checkFile(file, manager, isOnTheFly); - } - - private ProblemHighlightType getHighlightType(DiagnosticSeverity severity) { - if (severity == null) { - // if severity is not set, default to Error - return ProblemHighlightType.ERROR; - } - switch (severity) { - case Error: - return ProblemHighlightType.ERROR; - case Hint: - case Information: - return ProblemHighlightType.INFORMATION; - case Warning: - return ProblemHighlightType.WARNING; - } - return ProblemHighlightType.INFORMATION; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPSiElement.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPSiElement.java deleted file mode 100644 index de17349e1..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPSiElement.java +++ /dev/null @@ -1,332 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.openapi.fileTypes.PlainTextLanguage; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiInvalidElementAccessException; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiReference; -import com.intellij.psi.ResolveState; -import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchScope; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.Icon; -import java.util.HashMap; -import java.util.Map; - -public class LSPPSiElement implements PsiElement { - private final Project project; - private final PsiFile file; - private final int start; - private final int end; - private String text; - private Map userData = new HashMap<>(); - private Map copyableUserData = new HashMap<>(); - private final PsiReference reference = new LSPPsiReference(this); - - public LSPPSiElement(Project project, PsiFile file, int start, int end, String text) { - this.project = project; - this.file = file; - this.start = start; - this.end = end; - this.text = text; - } - - @NotNull - @Override - public Project getProject() throws PsiInvalidElementAccessException { - return project; - } - - @NotNull - @Override - public Language getLanguage() { - return PlainTextLanguage.INSTANCE; - } - - @Override - public PsiManager getManager() { - return PsiManager.getInstance(project); - } - - @NotNull - @Override - public PsiElement[] getChildren() { - return new PsiElement[0]; - } - - @Override - public PsiElement getParent() { - return getContainingFile(); - } - - @Override - public PsiElement getFirstChild() { - return null; - } - - @Override - public PsiElement getLastChild() { - return null; - } - - @Override - public PsiElement getNextSibling() { - return null; - } - - @Override - public PsiElement getPrevSibling() { - return null; - } - - @Override - public PsiFile getContainingFile() throws PsiInvalidElementAccessException { - return file; - } - - @Override - public TextRange getTextRange() { - return new TextRange(start, end); - } - - @Override - public int getStartOffsetInParent() { - return start; - } - - @Override - public int getTextLength() { - return end - start; - } - - @Nullable - @Override - public PsiElement findElementAt(int offset) { - return null; - } - - @Nullable - @Override - public PsiReference findReferenceAt(int offset) { - return null; - } - - @Override - public int getTextOffset() { - return start; - } - - @Override - public String getText() { - return text; - } - - @NotNull - @Override - public char[] textToCharArray() { - return getText().toCharArray(); - } - - @Override - public PsiElement getNavigationElement() { - return this; - } - - @Override - public PsiElement getOriginalElement() { - return this; - } - - @Override - public boolean textMatches(@NotNull CharSequence text) { - return text.equals(this.text); - } - - @Override - public boolean textMatches(@NotNull PsiElement element) { - return getText().equals(element.getText()); - } - - @Override - public boolean textContains(char c) { - return getText().indexOf(c) != -1; - } - - @Override - public void accept(@NotNull PsiElementVisitor visitor) { - visitor.visitElement(this); - - } - - @Override - public void acceptChildren(@NotNull PsiElementVisitor visitor) { - } - - @Override - public PsiElement copy() { - return null; - } - - @Override - public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement addBefore(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement addAfter(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public void checkAdd(@NotNull PsiElement element) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement addRange(PsiElement first, PsiElement last) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement addRangeBefore(@NotNull PsiElement first, @NotNull PsiElement last, PsiElement anchor) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement addRangeAfter(PsiElement first, PsiElement last, PsiElement anchor) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public void delete() throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public void checkDelete() throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public void deleteChildRange(PsiElement first, PsiElement last) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public PsiElement replace(@NotNull PsiElement newElement) throws IncorrectOperationException { - throw new IncorrectOperationException(); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public boolean isWritable() { - return true; - } - - @Nullable - @Override - public PsiReference getReference() { - return reference; - } - - @NotNull - @Override - public PsiReference[] getReferences() { - return new PsiReference[] { reference }; - } - - @Nullable - @Override - public T getCopyableUserData(Key key) { - return (T) copyableUserData.get(key); - } - - @Override - public void putCopyableUserData(Key key, @Nullable T value) { - copyableUserData.put(key, value); - } - - @Override - public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable PsiElement lastParent, @NotNull PsiElement place) { - return false; - } - - @Nullable - @Override - public PsiElement getContext() { - return null; - } - - @Override - public boolean isPhysical() { - return true; - } - - @NotNull - @Override - public GlobalSearchScope getResolveScope() { - return getContainingFile().getResolveScope(); - } - - @NotNull - @Override - public SearchScope getUseScope() { - return getContainingFile().getUseScope(); - } - - @Override - public ASTNode getNode() { - return null; - } - - @Override - public boolean isEquivalentTo(PsiElement another) { - return this == another; - } - - @Override - public Icon getIcon(int flags) { - return null; - } - - @Nullable - @Override - public T getUserData(@NotNull Key key) { - return (T) userData.get(key); - } - - @Override - public void putUserData(@NotNull Key key, @Nullable T value) { - userData.put(key, value); - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPsiReference.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPsiReference.java deleted file mode 100644 index ffcbc9904..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/diagnostics/LSPPsiReference.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.diagnostics; - -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiReference; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class LSPPsiReference implements PsiReference { - private PsiElement element; - - public LSPPsiReference(PsiElement element) { - this.element = element; - } - - @NotNull - @Override - public PsiElement getElement() { - return element; - } - - @NotNull - @Override - public TextRange getRangeInElement() { - return new TextRange(0, element.getText().length()); - } - - @Nullable - @Override - public PsiElement resolve() { - return element; - } - - @NotNull - @Override - public String getCanonicalText() { - return element.getText(); - } - - @Override - public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException { - return element; - } - - @Override - public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { - this.element = element; - return element; - } - - @Override - public boolean isReferenceTo(@NotNull PsiElement element) { - return this.element == element; - } - - @Override - public boolean isSoft() { - return false; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java deleted file mode 100644 index ae8d43874..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.highlight; - -import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; -import com.intellij.openapi.editor.Editor; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.util.Consumer; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.logging.Logger; - -public class LSPHighlightUsagesHandler extends HighlightUsagesHandlerBase { - private static final Logger LOGGER = Logger.getLogger(LSPHighlightUsagesHandler.class.getName()); - private final List targets; - - public LSPHighlightUsagesHandler(Editor editor, PsiFile file, List targets) { - super(editor, file); - this.targets = targets; - } - - @Override - public @NotNull List getTargets() { - return targets; - } - - @Override - protected void selectTargets(@NotNull List targets, - @NotNull Consumer> selectionConsumer) { - selectionConsumer.consume(targets); - } - - @Override - public void computeUsages(@NotNull List targets) { - targets.forEach(target -> myReadUsages.add(target.getTextRange())); - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java deleted file mode 100644 index e5791f862..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.highlight; - -import com.intellij.codeInsight.TargetElementUtil; -import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; -import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactory; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.DocumentHighlight; -import org.eclipse.lsp4j.DocumentHighlightParams; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class LSPHighlightUsagesHandlerFactory implements HighlightUsagesHandlerFactory { - private static final Logger LOGGER = Logger.getLogger(LSPHighlightUsagesHandlerFactory.class.getName()); - - @Override - public @Nullable HighlightUsagesHandlerBase createHighlightUsagesHandler(@NotNull Editor editor, @NotNull PsiFile file) { - List targets = getTargets(editor, file); - return targets.isEmpty()?null:new LSPHighlightUsagesHandler(editor, file, targets); - } - - private List getTargets(Editor editor, PsiFile file) { - List elements = new ArrayList<>(); - try { - int offset = TargetElementUtil.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset()); - Document document = editor.getDocument(); - Position position; - position = LSPIJUtils.toPosition(offset, document); - URI uri = LSPIJUtils.toUri(document); - if(uri == null) { - return Collections.emptyList(); - } - ProgressManager.checkCanceled(); - TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri.toString()); - DocumentHighlightParams params = new DocumentHighlightParams(identifier, position); - BlockingDeque highlights = new LinkedBlockingDeque<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(document, - capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) - .thenAcceptAsync(languageServers -> - CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> languageServer.getTextDocumentService().documentHighlight(params)) - .map(request -> request.thenAcceptAsync(result -> { - if (result != null) { - result.forEach(hightlight -> highlights.add(hightlight)); - } - })).toArray(CompletableFuture[]::new))); - while (!future.isDone() || !highlights.isEmpty()) { - ProgressManager.checkCanceled(); - DocumentHighlight highlight = highlights.poll(25, TimeUnit.MILLISECONDS); - if (highlight != null) { - int highlightOffset = LSPIJUtils.toOffset(highlight.getRange().getStart(), document); - PsiElement element = file.findElementAt(highlightOffset); - if (element != null) { - elements.add(element); - } - } - } - } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, e, e::getLocalizedMessage); - } - return elements; - - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/hover/LSPTextHover.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/hover/LSPTextHover.java deleted file mode 100644 index 9f606bfe8..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/hover/LSPTextHover.java +++ /dev/null @@ -1,309 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020, 2023 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.hover; - -import com.intellij.lang.documentation.DocumentationProviderEx; -import com.intellij.lang.documentation.ExternalDocumentationHandler; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.parser.Parser; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.MarkedString; -import org.eclipse.lsp4j.MarkupContent; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.SwingUtilities; -import java.awt.Color; -import java.awt.MouseInfo; -import java.awt.Point; -import java.awt.PointerInfo; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class LSPTextHover extends DocumentationProviderEx implements ExternalDocumentationHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPTextHover.class); - - private static final String HEAD = ""; //$NON-NLS-1$ - - private static final Parser PARSER = Parser.builder().build(); - private static final HtmlRenderer RENDERER = HtmlRenderer.builder().build(); - - private PsiElement lastElement; - private int lastOffset = -1; - private CompletableFuture> request,lspRequest; - - public LSPTextHover() { - LOGGER.info("LSPTextHover"); - } - - public static String styleHtml(Editor editor, String html) { - if (html == null || html.isEmpty()) { - return html; - } - Color background = editor.getColorsScheme().getDefaultBackground(); - Color foreground = editor.getColorsScheme().getDefaultForeground(); - // put CSS styling to match Eclipse style - String style = ""; //$NON-NLS-1$ - - /*int headIndex = html.indexOf(HEAD); - StringBuilder builder = new StringBuilder(html.length() + style.length()); - builder.append(html.substring(0, headIndex + HEAD.length())); - builder.append(style); - builder.append(html.substring(headIndex + HEAD.length())); - return builder.toString();*/ - StringBuilder builder = new StringBuilder(style); - builder.append(html).append(""); - return builder.toString(); - } - - private static String toHTMLrgb(Color rgb) { - StringBuilder builder = new StringBuilder(7); - builder.append('#'); - appendAsHexString(builder, rgb.getRed()); - appendAsHexString(builder, rgb.getGreen()); - appendAsHexString(builder, rgb.getBlue()); - return builder.toString(); - } - - private static void appendAsHexString(StringBuilder buffer, int intValue) { - String hexValue= Integer.toHexString(intValue); - if (hexValue.length() == 1) { - buffer.append('0'); - } - buffer.append(hexValue); - } - - @Nullable - @Override - public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { - return generateDoc(element, originalElement); - } - - @Nullable - @Override - public List getUrlFor(PsiElement element, PsiElement originalElement) { - return null; - } - - private CompletableFuture getCursorOffset(Editor editor) { - CompletableFuture future = new CompletableFuture<>(); - ApplicationManager.getApplication().invokeLater(() -> { - int offset = -1; - PointerInfo info = MouseInfo.getPointerInfo(); - if (info != null/* && EditorUtil.isPointOverText(editor, info.getLocation())*/) { - Point location = info.getLocation(); - SwingUtilities.convertPointFromScreen(location, editor.getContentComponent()); - LogicalPosition position = editor.xyToLogicalPosition(location); - offset = editor.logicalPositionToOffset(position); - } - future.complete(offset); - }); - return future; - } - - @Nullable - @Override - public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { - PsiElement elem = null; - Editor editor = null; - if (element != null) { - elem = element; - editor = LSPIJUtils.editorForElement(elem); - } - if (editor == null && originalElement != null) { - // for some files (e.g. xml) element cannot resolve the associated VirtualFile and Editor, so we try to resolve again with originalElement - editor = LSPIJUtils.editorForElement(originalElement); - elem = originalElement; - LOGGER.info("Cannot resolve VirtualFile and Editor for element: " + element.getText() + ". Using originalElement: " + originalElement.getText() + " for hover request."); - } - if (editor != null) { - initiateHoverRequest(elem, editor); - try { - String result = request.get(5, TimeUnit.SECONDS).stream() - .filter(Objects::nonNull) - .map(LSPTextHover::getHoverString) - .filter(Objects::nonNull) - .collect(Collectors.joining("\n\n")) //$NON-NLS-1$ - .trim(); - if (!result.isEmpty()) { - return styleHtml(editor, RENDERER.render(PARSER.parse(result))); - } - } catch (ExecutionException | TimeoutException e) { - String fileName = elem.getContainingFile().getVirtualFile() != null ? String.valueOf(elem.getContainingFile().getVirtualFile()) : String.valueOf(elem.getContainingFile()); - LOGGER.warn(String.format("Unable to generate documentation for %s. ", fileName) + e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - } - return null; - } - - protected static @Nullable String getHoverString(Hover hover) { - Either>, MarkupContent> hoverContent = hover.getContents(); - if (hoverContent.isLeft()) { - List> contents = hoverContent.getLeft(); - if (contents == null || contents.isEmpty()) { - return null; - } - return contents.stream().map(content -> { - if (content.isLeft()) { - return content.getLeft(); - } else if (content.isRight()) { - MarkedString markedString = content.getRight(); - // TODO this won't work fully until markup parser will support syntax - // highlighting but will help display - // strings with language tags, e.g. without it things after ) String::isEmpty).negate()).collect(Collectors.joining("\n\n")); //$NON-NLS-1$ ) - } else { - return hoverContent.getRight().getValue(); - } - } - - - /** - * Initialize hover requests with hover (if available) and codelens (if - * available). - * - * @param element - * the PSI element. - * @param editor - * the editor. - */ - private void initiateHoverRequest(PsiElement element, Editor editor) { - PsiDocumentManager manager = PsiDocumentManager.getInstance(element.getProject()); - final Document document = manager.getDocument(element.getContainingFile()); - this.request = getCursorOffset(editor).thenComposeAsync(offset -> { - if (offset != -1 && (this.lspRequest == null || !element.equals(this.lastElement) || offset != this.lastOffset)) { - ApplicationManager.getApplication().runReadAction(() -> { // make sure no writes to the project are underway - this.lastElement = element; - this.lastOffset = offset; - this.lspRequest = LanguageServiceAccessor.getInstance(element.getProject()) - .getLanguageServers(document, capabilities -> isHoverCapable(capabilities)) - .thenApplyAsync(languageServers -> // Async is very important here, otherwise the LS Client thread is in - // deadlock and doesn't read bytes from LS - { - return languageServers.stream() - .map(languageServer -> { - try { - return languageServer.getTextDocumentService() - .hover(LSPIJUtils.toHoverParams(offset, document)).get(); - } catch (ExecutionException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return null; - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); - }); - }); - } - return this.lspRequest; - }); - } - - private boolean isHoverCapable(ServerCapabilities capabilities) { - return (capabilities.getHoverProvider().isLeft() && capabilities.getHoverProvider().getLeft()) || capabilities.getHoverProvider().isRight(); - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { - return null; - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { - return null; - } - - @Nullable - @Override - public PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement) { - return null; - } - - @Override - public boolean handleExternal(PsiElement element, PsiElement originalElement) { - return false; - } - - @Override - public boolean handleExternalLink(PsiManager psiManager, String link, PsiElement context) { - VirtualFile file = getFile(link); - if (file != null) { - // use invokeLater to avoid write-unsafe context error - ApplicationManager.getApplication().invokeLater(()->{ - FileEditorManager.getInstance(psiManager.getProject()).openFile(file, true, true); - }, psiManager.getProject().getDisposed()); - return true; - } - return false; - } - - @Override - public boolean canFetchDocumentationLink(String link) { - return false; - } - - private VirtualFile getFile(String link) { - try { - return VfsUtil.findFileByURL(new URL(link)); - } catch (MalformedURLException e) { - return null; - } - } - - @Override - public @NotNull String fetchExternalDocumentation(@NotNull String link, @Nullable PsiElement element) { - return null; - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java deleted file mode 100644 index b88d73871..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 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 http://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.inlayhint; - -import com.intellij.codeInsight.hints.FactoryInlayHintsCollector; -import com.intellij.codeInsight.hints.InlayHintsCollector; -import com.intellij.codeInsight.hints.InlayHintsSink; -import com.intellij.codeInsight.hints.NoSettings; -import com.intellij.codeInsight.hints.presentation.InlayPresentation; -import com.intellij.codeInsight.hints.presentation.MouseButton; -import com.intellij.codeInsight.hints.presentation.PresentationFactory; -import com.intellij.codeInsight.hints.presentation.SequencePresentation; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.AbstractLSPInlayProvider; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.InlayHint; -import org.eclipse.lsp4j.InlayHintLabelPart; -import org.eclipse.lsp4j.InlayHintParams; -import org.eclipse.lsp4j.InlayHintRegistrationOptions; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.awt.Component; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class LSPInlayHintInlayProvider extends AbstractLSPInlayProvider { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPInlayHintInlayProvider.class); - - @Nullable - @Override - public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile, - @NotNull Editor editor, - @NotNull NoSettings o, - @NotNull InlayHintsSink inlayHintsSink) { - return new FactoryInlayHintsCollector(editor) { - @Override - public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) { - try { - URI docURI = LSPIJUtils.toUri(editor.getDocument()); - if (docURI != null) { - Range viewPortRange = new Range(new Position(0, 0), new Position(0,0)); - InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(docURI.toString()), viewPortRange); - BlockingDeque> pairs = new LinkedBlockingDeque<>(); - List>> inlayhints = new ArrayList<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(psiElement.getProject()) - .getLanguageServers(editor.getDocument(), capabilities -> capabilities.getInlayHintProvider() != null) - .thenComposeAsync(languageServers -> CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> languageServer.getTextDocumentService().inlayHint(param) - .thenAcceptAsync(inlayHints -> { - // textDocument/codeLens may return null - if (inlayHints != null) { - inlayHints.stream().filter(Objects::nonNull) - .forEach(inlayHint -> pairs.add(new Pair(inlayHint, languageServer))); - } - })) - .toArray(CompletableFuture[]::new))); - while (!future.isDone() || !pairs.isEmpty()) { - ProgressManager.checkCanceled(); - Pair pair = pairs.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - int offset = LSPIJUtils.toOffset(pair.getFirst().getPosition(), editor.getDocument()); - inlayhints.add(Pair.create(offset, pair)); - } - } - Map>>> elements = inlayhints.stream().collect(Collectors.groupingBy(p -> p.first)); - elements.forEach((offset,list) -> inlayHintsSink.addInlineElement(offset, false, - toPresentation(editor, offset, list, getFactory()), false)); - } - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - return false; - } - }; - } - - private InlayPresentation toPresentation(Editor editor, int offset, - List>> elements, - PresentationFactory factory) { - List presentations = new ArrayList<>(); - elements.forEach(p -> { - Either> label = p.second.first.getLabel(); - if (label.isLeft()) { - presentations.add(factory.smallText(label.getLeft())); - } else { - int index = 0; - for(InlayHintLabelPart part : label.getRight()) { - InlayPresentation presentation = factory.smallText(part.getValue()); - if (part.getCommand() != null) { - int finalIndex = index; - presentation = factory.onClick(presentation, MouseButton.Left, (event, point) -> { - executeClientCommand(p.second.second, p.second.first, finalIndex, (Component) event.getSource(), editor.getProject()); - return null; - }); - if (part.getTooltip() != null && part.getTooltip().isLeft()) { - presentation = factory.withTooltip(part.getTooltip().getLeft(), presentation); - } - } - presentations.add(presentation); - index++; - } - } - }); - return factory.roundWithBackground(new SequencePresentation(presentations)); - } - - private void executeClientCommand(LanguageServer languageServer, InlayHint inlayHint, int index, Component source, - Project project) { - if (LanguageServiceAccessor.getInstance(project).checkCapability(languageServer, - capabilites -> isResolveSupported(capabilites.getInlayHintProvider()))) { - languageServer.getTextDocumentService().resolveInlayHint(inlayHint).thenAcceptAsync(resolvedInlayHint -> { - executeClientCommand(source, resolvedInlayHint.getLabel().getRight().get(index).getCommand()); - }); - } else { - executeClientCommand(source, inlayHint.getLabel().getRight().get(index).getCommand()); - } - } - - private boolean isResolveSupported(Either provider) { - return provider.isRight() && provider.getRight().getResolveProvider(); - } - - - private String getInlayHintString(InlayHint inlayHint) { - Either> label = inlayHint.getLabel(); - return label.map(Function.identity(), parts -> { - return parts==null?null:parts.stream().map(InlayHintLabelPart::getValue).collect(Collectors.joining()); - }); - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java deleted file mode 100644 index d8c63e0f3..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.navigation; - -import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LanguageServiceAccessor; -import io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; -import org.eclipse.lsp4j.DefinitionParams; -import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.LocationLink; -import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -public class LSPGotoDeclarationHandler implements GotoDeclarationHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPGotoDeclarationHandler.class); - - @Nullable - @Override - public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { - try { - URI uri = LSPIJUtils.toUri(editor.getDocument()); - if (uri != null) { - DefinitionParams parms = new DefinitionParams(new TextDocumentIdentifier(uri.toString()), LSPIJUtils.toPosition(offset, editor.getDocument())); - Set targets = new HashSet<>(); - try { - LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(editor.getDocument(), capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())).thenComposeAsync(servers -> - - CompletableFuture.allOf(servers.stream().map(server -> server.getTextDocumentService().definition(parms).thenAcceptAsync(definitions -> targets.addAll(toElements(editor.getProject(), definitions)))) - .toArray(CompletableFuture[]::new))).get(1_000, TimeUnit.MILLISECONDS); - } catch (ExecutionException | TimeoutException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return targets.toArray(new PsiElement[targets.size()]); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.warn(e.getLocalizedMessage(), e); - } - return new PsiElement[0]; - } - - private List toElements(Project project, Either, List> definitions) { - List locations = definitions!=null?toLocation(definitions): Collections.emptyList(); - return locations.stream().map(location -> toElement(project, location)).filter(Objects::nonNull).collect(Collectors.toList()); - } - - private PsiElement toElement(Project project, Location location) { - return ApplicationManager.getApplication().runReadAction((Computable) () -> { - PsiElement element = null; - try { - VirtualFile file = PsiUtilsLSImpl.getInstance(project).findFile(location.getUri()); - if (file != null) { - PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - if (psiFile != null) { - Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile); - if (document != null) { - element = psiFile.findElementAt(LSPIJUtils.toOffset(location.getRange().getStart(), document)); - } - } - } - } catch (IOException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - return element; - }); - } - - /** - * Unify the definition result has a list of Location. - * - * @param definitions the definition result - * @return the list of locations - */ - private List toLocation(Either, List> definitions) { - if (definitions.isLeft()) { - return definitions.getLeft(); - } else { - return definitions.getRight().stream().map(link -> new Location(link.getTargetUri(), link.getTargetRange())).collect(Collectors.toList()); - } - } -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/ProcessStreamConnectionProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/ProcessStreamConnectionProvider.java deleted file mode 100644 index 741d548bf..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/ProcessStreamConnectionProvider.java +++ /dev/null @@ -1,164 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.server; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.io.*; -import java.util.List; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public abstract class ProcessStreamConnectionProvider implements StreamConnectionProvider{ - private static final Logger LOGGER = LoggerFactory.getLogger(ProcessStreamConnectionProvider.class); - - private @Nullable Process process; - private List commands; - private @Nullable String workingDir; - - public ProcessStreamConnectionProvider() { - } - - public ProcessStreamConnectionProvider(List commands) { - this.commands = commands; - } - - public ProcessStreamConnectionProvider(List commands, String workingDir) { - this.commands = commands; - this.workingDir = workingDir; - } - - @Override - public void start() throws IOException { - if (this.commands == null || this.commands.isEmpty() || this.commands.stream().anyMatch(Objects::isNull)) { - throw new IOException("Unable to start language server: " + this.toString()); //$NON-NLS-1$ - } - - ProcessBuilder builder = createProcessBuilder(); - Process p = builder.start(); - this.process = p; - if (!p.isAlive()) { - throw new IOException("Unable to start language server: " + this.toString()); //$NON-NLS-1$ - } else { - LOGGER.info("Starting language server: " + this.toString()); - } - } - - protected ProcessBuilder createProcessBuilder() { - ProcessBuilder builder = new ProcessBuilder(getCommands()); - if (getWorkingDirectory() != null) { - builder.directory(new File(getWorkingDirectory())); - } - builder.redirectError(ProcessBuilder.Redirect.INHERIT); - return builder; - } - - @Override - public @Nullable InputStream getInputStream() { - Process p = process; - return p == null ? null : p.getInputStream(); - } - - @Override - public @Nullable InputStream getErrorStream() { - Process p = process; - return p == null ? null : p.getErrorStream(); - } - - @Override - public @Nullable OutputStream getOutputStream() { - Process p = process; - return p == null ? null : p.getOutputStream(); - } - - @Override - public void stop() { - Process p = process; - if (p != null) { - p.destroy(); - } - } - - protected List getCommands() { - return commands; - } - - public void setCommands(List commands) { - this.commands = commands; - } - - protected @Nullable String getWorkingDirectory() { - return workingDir; - } - - public void setWorkingDirectory(String workingDir) { - this.workingDir = workingDir; - } - - protected boolean checkJavaVersion(String javaHome, int expectedVersion) { - final ProcessBuilder builder = new ProcessBuilder(javaHome + - File.separator + "bin" + File.separator + "java", "-version"); - try { - final Process p = builder.start(); - final Reader r = new InputStreamReader(p.getErrorStream()); - final StringBuilder sb = new StringBuilder(); - int i; - while ((i = r.read()) != -1) { - sb.append((char) i); - } - return parseMajorJavaVersion(sb.toString()) >= expectedVersion; - } - catch (IOException ioe) {} - return false; - } - - private int parseMajorJavaVersion(String content) { - final String versionRegex = "version \"(.*)\""; - Pattern p = Pattern.compile(versionRegex); - Matcher m = p.matcher(content); - if (!m.find()) { - return 0; - } - String version = m.group(1); - - // Ignore '1.' prefix for legacy Java versions - if (version.startsWith("1.")) { - version = version.substring(2); - } - - // Extract the major version number. - final String numberRegex = "\\d+"; - p = Pattern.compile(numberRegex); - m = p.matcher(version); - if (!m.find()) { - return 0; - } - return Integer.parseInt(m.group()); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof ProcessStreamConnectionProvider)) { - return false; - } - ProcessStreamConnectionProvider other = (ProcessStreamConnectionProvider) obj; - return Objects.equals(this.getCommands(), other.getCommands()) - && Objects.equals(this.getWorkingDirectory(), other.getWorkingDirectory()); - } - - @Override - public int hashCode() { - return Objects.hash(this.getCommands(), this.getWorkingDirectory()); - } - - @Override - public String toString() { - return "ProcessStreamConnectionProvider [commands=" + this.getCommands() + ", workingDir=" //$NON-NLS-1$//$NON-NLS-2$ - + this.getWorkingDirectory() + "]"; //$NON-NLS-1$ - } - -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/StreamConnectionProvider.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/StreamConnectionProvider.java deleted file mode 100644 index 4e9827862..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/server/StreamConnectionProvider.java +++ /dev/null @@ -1,122 +0,0 @@ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.server; - -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.services.LanguageServer; - -import javax.annotation.Nullable; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; - -public interface StreamConnectionProvider { - public void start() throws IOException; - - public InputStream getInputStream(); - - public OutputStream getOutputStream(); - - /** - * Returns the {@link InputStream} connected to the error output of the process - * running the language server. If the error output is redirected to standard - * output it returns null. - * - * @return the {@link InputStream} connected to the error output of the language - * server process or null if it's redirected or process not - * yet started. - */ - public @Nullable InputStream getErrorStream(); - - /** - * Forwards a copy of an {@link InputStream} to an {@link OutputStream}. - * - * @param input - * the {@link InputStream} that will be copied - * @param output - * the {@link OutputStream} to forward the copy to - * @return a newly created {@link InputStream} that copies all data to the - * provided {@link OutputStream} - */ - public default InputStream forwardCopyTo(InputStream input, OutputStream output) { - if (input == null) - return null; - if (output == null) - return input; - - FilterInputStream filterInput = new FilterInputStream(input) { - @Override - public int read() throws IOException { - int res = super.read(); - System.err.print((char) res); - return res; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - int bytes = super.read(b, off, len); - byte[] payload = new byte[bytes]; - System.arraycopy(b, off, payload, 0, bytes); - output.write(payload, 0, payload.length); - return bytes; - } - - @Override - public int read(byte[] b) throws IOException { - int bytes = super.read(b); - byte[] payload = new byte[bytes]; - System.arraycopy(b, 0, payload, 0, bytes); - output.write(payload, 0, payload.length); - return bytes; - } - }; - - return filterInput; - } - - /** - * User provided initialization options. - */ - public default Object getInitializationOptions(URI rootUri){ - return null; - } - - /** - * Returns an object that describes the experimental features supported - * by the client. - * @implNote The returned object gets serialized by LSP4J, which itself uses - * GSon, so a GSon object can work too. - * @since 0.12 - * @return an object whose fields represent the different experimental features - * supported by the client. - */ - public default Object getExperimentalFeaturesPOJO() { - return null; - } - - /** - * Provides trace level to be set on language server initialization.
- * Legal values: "off" | "messages" | "verbose". - * - * @param rootUri - * the workspace root URI. - * - * @return the initial trace level to set - * @see "https://microsoft.github.io/language-server-protocol/specification#initialize" - */ - public default String getTrace(URI rootUri) { - return "off"; //$NON-NLS-1$ - } - - public void stop(); - - /** - * Allows to hook custom behavior on messages. - * @param message a message - * @param languageServer the language server receiving/sending the message. - * @param rootUri - */ - public default void handleMessage(Message message, LanguageServer languageServer, URI rootURI) {} - - -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ui/Messages.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ui/Messages.java deleted file mode 100644 index bcb82db19..000000000 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp/lsp4ij/ui/Messages.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Red Hat Inc. and others. - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Mickael Istria (Red Hat Inc.) - initial implementation - * Angelo Zerr - Bug 525400 - [rename] improve rename support with ltk UI - * Jan Koehnlein (TypeFox) add rename empty message - *******************************************************************************/ -package io.openliberty.tools.intellij.lsp4mp.lsp4ij.ui; - -public class Messages { - - public static String hyperlinkLabel; - public static String PreferencesPage_Intro; - public static String PreferencesPage_staticServers; - public static String PreferencesPage_manualServers; - public static String PreferencesPage_LaunchConfiguration; - public static String PreferencesPage_LaunchMode; - public static String PreferencesPage_Add; - public static String PreferencesPage_Remove; - public static String PreferencesPage_contentType; - public static String PreferencesPage_languageServer; - public static String PreferencesPage_Enabled; - public static String PreferencesPage_enablementCondition; - public static String PreferencePage_enablementCondition_true; - public static String PreferencePage_enablementCondition_false; - public static String PreferencePage_enablementCondition_enableAll; - public static String PreferencePage_enablementCondition_disableAll; - public static String PreferencesPage_logging_toFile_title; - public static String PreferencesPage_logging_toFile_description; - public static String PreferencesPage_logging_toConsole_title; - public static String PreferencesPage_logging_toConsole_description; - public static String preferencesPage_logging_info; - public static String preferencesPage_logging_fileLogsLocation; - public static String PreferencesPage_restartWarning_title; - public static String PreferencesPage_restartWarning_message; - public static String PreferencesPage_restartWarning_restart; - public static String NewContentTypeLSPLaunchDialog_associateContentType; - public static String NewContentTypeLSPLaunchDialog_withLSPLaunch; - public static String codeActions_description; - public static String codeActions_label; - public static String codeActions_emptyMenu; - public static String codeLens_emptyMenu; - public static String updateCodeActions_menu; - public static String initializeLanguageServer_job = "Initialize language server"; - public static String computing; - public static String notImplemented; - public static String LSPSymbolInWorkspaceDialog_DialogLabel; - public static String LSPSymbolInWorkspaceDialog_DialogTitle; - public static String updateCodelensMenu_job; - public static String outline_computingSymbols; - public static String rename_title; - public static String rename_label; - public static String rename_processor_name; - public static String rename_processor_required; - public static String serverEdit; - public static String rename_empty_message; - public static String rename_invalidated; - public static String completionError; - public static String linkWithEditor_label; - public static String linkWithEditor_description; - public static String linkWithEditor_tooltip; - public static String LSSearchQuery_label; - public static String LSSearchQuery_singularReference; - public static String LSSearchQuery_pluralReferences; - public static String enableDisableLSJob; - public static String edit_CreateFile; - public static String workspaceSymbols; -} diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java index 4097e8b94..e47feee6c 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java @@ -4,7 +4,7 @@ import com.intellij.ide.BrowserUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.operations.codelens.LSPInlayProvider; +import org.microshed.lsp4ij.operations.codelens.LSPInlayProvider; import java.util.List; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProject.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProject.java index ff2a3fb3c..33d52a453 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProject.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProject.java @@ -13,7 +13,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.VirtualFile; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.DocumentContentSynchronizer; +import org.microshed.lsp4ij.DocumentContentSynchronizer; import io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.project.ConfigSourcePropertiesProvider; import org.eclipse.lsp4mp.commons.utils.ConfigSourcePropertiesProviderUtils; import org.eclipse.lsp4mp.commons.utils.IConfigSourcePropertiesProvider; diff --git a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java index fc84b4a1c..f7c41c725 100644 --- a/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java +++ b/src/main/java/io/openliberty/tools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java @@ -24,7 +24,7 @@ import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.JsonRpcHelpers; import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.PsiUtils; import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; +import org.microshed.lsp4ij.LSPIJUtils; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4mp.commons.ClasspathKind; diff --git a/src/main/resources/META-INF/lsp.xml b/src/main/resources/META-INF/lsp.xml index 04f4afbd1..85ebd1d57 100644 --- a/src/main/resources/META-INF/lsp.xml +++ b/src/main/resources/META-INF/lsp.xml @@ -9,14 +9,7 @@ --> - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - io.openliberty.tools.intellij.lsp4mp.lsp4ij.ConnectDocumentToLanguageServerSetupParticipant - - \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ed8be132d..1321f08f1 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -18,7 +18,9 @@ com.intellij.properties org.jetbrains.idea.maven com.intellij.gradle - + + org.microshed.lsp4ij + - \ No newline at end of file + From b5bcff1438f416ea8ec8df430264020e21eac398 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 15 Sep 2023 15:49:01 -0400 Subject: [PATCH 02/15] Add a script to build a plugin zip file which contains liberty-tools-intellij and its dependent lsp4ij. Signed-off-by: Paul Gooderham --- makeDualZip | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 makeDualZip diff --git a/makeDualZip b/makeDualZip new file mode 100755 index 000000000..efcfb42ad --- /dev/null +++ b/makeDualZip @@ -0,0 +1,25 @@ +set -x +#Must start in the liberty-tools-intellij directory +#The lsp4ij source must be cloned in the same parent directory. +export LSP4IJ_DIR=intellij-lsp-common-provider/lsp4ij +export TMP_DIR=p +export LT_INTELLIJ_ZIP=liberty-tools-intellij-23.0.7-SNAPSHOT.zip +export LT_INTELLIJ_UBER_ZIP=$LT_INTELLIJ_ZIP.uber.zip # yes two zips +export LSP4IJ_ZIP=lsp4ij-0.0.4.zip +export JETBRAINS_DIST_DIR=build/distributions + +gradle clean buildPlugin +# Run the other build and return to this directory +(cd ../$LSP4IJ_DIR && gradle clean buildPlugin) + +# Clean up +[ ! -d $TMP_DIR ] && mkdir $TMP_DIR +rm -rf $TMP_DIR/* +cd $TMP_DIR + +# Parent directory is liberty-tools-intellij directory +unzip ../$JETBRAINS_DIST_DIR/$LT_INTELLIJ_ZIP +# Intermingle files +unzip ../../$LSP4IJ_DIR/$JETBRAINS_DIST_DIR/$LSP4IJ_ZIP +zip -r $LT_INTELLIJ_UBER_ZIP liberty-tools-intellij lsp4ij +cp $LT_INTELLIJ_UBER_ZIP ../$JETBRAINS_DIST_DIR From 316665282c697f201d013f2bb30127905d35c73c Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 15 Sep 2023 15:57:36 -0400 Subject: [PATCH 03/15] Update the build script to build in the proper order and add build instructions to DEVELOPING.md Signed-off-by: Paul Gooderham --- DEVELOPING.md | 5 +++-- makeDualZip | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index c0d47d8c6..e65cb035c 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -16,13 +16,14 @@ This extension is built using the [gradle-intellij-plugin](https://github.com/JetBrains/gradle-intellij-plugin/). -1. Clone this repository: `git clone git@github.com:OpenLiberty/liberty-tools-intellij.git && cd liberty-tools-intellij` +1. Clone this repository: `git clone git@github.com:OpenLiberty/liberty-tools-intellij.git` +2. Clone the lsp4ij repository: `git clone git@github.ibm.com:liberty-dev-ex/intellij-lsp-common-provider.git` 2. Import this repository as a Gradle project in IntelliJ IDEA 3. Run `./gradlew runIde --stacktrace`. A new IntelliJ IDEA window will launch with the Liberty Tools plugin installed to it. You can connect the IntelliJ IDEA debugger to this process to debug the plugin. OR - Run `./gradlew buildPlugin` to build an installable zip in `build/distributions/liberty-tools-intellij-xxx.zip`. You can install this zip in IntelliJ IDEA through **Preferences > Plugins > Gear icon > Install Plugin from Disk...** and select the `liberty-tools-intellij-xxx.zip`. + Run `./makeDualZip` to build an installable zip in `build/distributions/liberty-tools-intellij-xxx.uber.zip`. You can install this zip in IntelliJ IDEA through **Preferences > Plugins > Gear icon > Install Plugin from Disk...** and select the `liberty-tools-intellij-xxx.uber.zip`. ## Language Servers diff --git a/makeDualZip b/makeDualZip index efcfb42ad..c82e127fc 100755 --- a/makeDualZip +++ b/makeDualZip @@ -1,6 +1,5 @@ -set -x -#Must start in the liberty-tools-intellij directory -#The lsp4ij source must be cloned in the same parent directory. +# Must start in the liberty-tools-intellij directory +# The lsp4ij source must be cloned in the same parent directory. export LSP4IJ_DIR=intellij-lsp-common-provider/lsp4ij export TMP_DIR=p export LT_INTELLIJ_ZIP=liberty-tools-intellij-23.0.7-SNAPSHOT.zip @@ -8,10 +7,12 @@ export LT_INTELLIJ_UBER_ZIP=$LT_INTELLIJ_ZIP.uber.zip # yes two zips export LSP4IJ_ZIP=lsp4ij-0.0.4.zip export JETBRAINS_DIST_DIR=build/distributions -gradle clean buildPlugin # Run the other build and return to this directory (cd ../$LSP4IJ_DIR && gradle clean buildPlugin) +# Build the main plugin. +gradle clean buildPlugin + # Clean up [ ! -d $TMP_DIR ] && mkdir $TMP_DIR rm -rf $TMP_DIR/* From f1501f7f2a715b2cacee86b1bb1b215e7ac148b9 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 15 Sep 2023 17:14:50 -0400 Subject: [PATCH 04/15] Restore the until attribute in plugin.xml. Signed-off-by: Paul Gooderham --- src/main/resources/META-INF/plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1321f08f1..7f73db26f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -20,7 +20,7 @@ com.intellij.gradle org.microshed.lsp4ij - + Date: Mon, 23 Oct 2023 19:22:42 -0400 Subject: [PATCH 05/15] Provide pluginNamespace.properties and lsp4ij.xml for use by lsp4ij dependency. Signed-off-by: Paul Gooderham --- build.gradle | 4 +- src/main/resources/META-INF/lsp.xml | 2 +- src/main/resources/META-INF/lsp4ij.xml | 49 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 8 ++- src/main/resources/pluginNamespace.properties | 1 + 5 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/META-INF/lsp4ij.xml create mode 100644 src/main/resources/pluginNamespace.properties diff --git a/build.gradle b/build.gradle index 531dbff93..614490ed3 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,7 @@ configurations { dependencies { // LSP4IJ dependencies + implementation 'org.microshed:lsp4ij:0.0.5' implementation ('org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:0.6.0') { exclude group: 'org.eclipse.lsp4j' } @@ -138,8 +139,7 @@ intellij { // For a full list of IntelliJ IDEA releases please see https://www.jetbrains.com/intellij-repository/releases version = '2023.1.2' - plugins = ['java', 'maven', 'gradle-java', 'properties', 'terminal', 'org.jetbrains.idea.maven', 'com.intellij.gradle', - file("../intellij-lsp-common-provider/lsp4ij/build/libs/lsp4ij-0.0.4.uber.jar")] + plugins = ['java', 'maven', 'gradle-java', 'properties', 'terminal', 'org.jetbrains.idea.maven', 'com.intellij.gradle'] updateSinceUntilBuild = false } diff --git a/src/main/resources/META-INF/lsp.xml b/src/main/resources/META-INF/lsp.xml index 85ebd1d57..e7d6f6a50 100644 --- a/src/main/resources/META-INF/lsp.xml +++ b/src/main/resources/META-INF/lsp.xml @@ -9,7 +9,7 @@ --> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.microshed.lsp4ij.ConnectDocumentToLanguageServerSetupParticipant + + + diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 7f73db26f..1cc47b632 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -18,8 +18,6 @@ com.intellij.properties org.jetbrains.idea.maven com.intellij.gradle - - org.microshed.lsp4ij @@ -254,6 +252,12 @@ + + + + + diff --git a/src/main/resources/pluginNamespace.properties b/src/main/resources/pluginNamespace.properties new file mode 100644 index 000000000..f7e4e125b --- /dev/null +++ b/src/main/resources/pluginNamespace.properties @@ -0,0 +1 @@ +pluginNamespace=open-liberty.intellij \ No newline at end of file From d3a923884ccf87720a00cc6388cadbf647c3e53f Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Tue, 24 Oct 2023 17:54:40 -0400 Subject: [PATCH 06/15] Add LSPIJUtils from lsp4ij. Signed-off-by: Paul Gooderham --- .../tools/intellij/liberty/lsp/LibertyCustomConfigListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyCustomConfigListener.java b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyCustomConfigListener.java index 794f15f85..902fe59e0 100644 --- a/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyCustomConfigListener.java +++ b/src/main/java/io/openliberty/tools/intellij/liberty/lsp/LibertyCustomConfigListener.java @@ -12,9 +12,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; -import io.openliberty.tools.intellij.lsp4mp.lsp4ij.LSPIJUtils; import org.jetbrains.annotations.NotNull; +import org.microshed.lsp4ij.LSPIJUtils; import java.util.List; From 954e22c84a0e0168ff8654a9c8c06da1c08ae8c5 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Tue, 24 Oct 2023 18:18:04 -0400 Subject: [PATCH 07/15] Update the build instructions in DEVELOPING.md Signed-off-by: Paul Gooderham --- DEVELOPING.md | 6 ++++-- makeDualZip | 26 -------------------------- 2 files changed, 4 insertions(+), 28 deletions(-) delete mode 100755 makeDualZip diff --git a/DEVELOPING.md b/DEVELOPING.md index e65cb035c..a9a3e928c 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -17,13 +17,15 @@ This extension is built using the [gradle-intellij-plugin](https://github.com/JetBrains/gradle-intellij-plugin/). 1. Clone this repository: `git clone git@github.com:OpenLiberty/liberty-tools-intellij.git` -2. Clone the lsp4ij repository: `git clone git@github.ibm.com:liberty-dev-ex/intellij-lsp-common-provider.git` +2. Clone the lsp4ij repository: `git clone git@github.com:MicroShed/lsp4ij.git` +3. Build lsp4ij: `./gradlew jar` +4. Save it in your local Maven repository: `mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true;` 2. Import this repository as a Gradle project in IntelliJ IDEA 3. Run `./gradlew runIde --stacktrace`. A new IntelliJ IDEA window will launch with the Liberty Tools plugin installed to it. You can connect the IntelliJ IDEA debugger to this process to debug the plugin. OR - Run `./makeDualZip` to build an installable zip in `build/distributions/liberty-tools-intellij-xxx.uber.zip`. You can install this zip in IntelliJ IDEA through **Preferences > Plugins > Gear icon > Install Plugin from Disk...** and select the `liberty-tools-intellij-xxx.uber.zip`. + Run `./gradlew buildPlugin` to build an installable zip in `build/distributions/liberty-tools-intellij-xxx.zip`. You can install this zip in IntelliJ IDEA through **Preferences > Plugins > Gear icon > Install Plugin from Disk...** and select the `liberty-tools-intellij-xxx.zip`. ## Language Servers diff --git a/makeDualZip b/makeDualZip deleted file mode 100755 index c82e127fc..000000000 --- a/makeDualZip +++ /dev/null @@ -1,26 +0,0 @@ -# Must start in the liberty-tools-intellij directory -# The lsp4ij source must be cloned in the same parent directory. -export LSP4IJ_DIR=intellij-lsp-common-provider/lsp4ij -export TMP_DIR=p -export LT_INTELLIJ_ZIP=liberty-tools-intellij-23.0.7-SNAPSHOT.zip -export LT_INTELLIJ_UBER_ZIP=$LT_INTELLIJ_ZIP.uber.zip # yes two zips -export LSP4IJ_ZIP=lsp4ij-0.0.4.zip -export JETBRAINS_DIST_DIR=build/distributions - -# Run the other build and return to this directory -(cd ../$LSP4IJ_DIR && gradle clean buildPlugin) - -# Build the main plugin. -gradle clean buildPlugin - -# Clean up -[ ! -d $TMP_DIR ] && mkdir $TMP_DIR -rm -rf $TMP_DIR/* -cd $TMP_DIR - -# Parent directory is liberty-tools-intellij directory -unzip ../$JETBRAINS_DIST_DIR/$LT_INTELLIJ_ZIP -# Intermingle files -unzip ../../$LSP4IJ_DIR/$JETBRAINS_DIST_DIR/$LSP4IJ_ZIP -zip -r $LT_INTELLIJ_UBER_ZIP liberty-tools-intellij lsp4ij -cp $LT_INTELLIJ_UBER_ZIP ../$JETBRAINS_DIST_DIR From 386f1ac4bd50e39a82a0add1185321a1cf9c595c Mon Sep 17 00:00:00 2001 From: Michael Glavassevich Date: Thu, 26 Oct 2023 13:56:59 -0400 Subject: [PATCH 08/15] Integrating lsp4ij into the Liberty Tools build. Signed-off-by: Michael Glavassevich --- .github/workflows/build.yaml | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index af72c2212..957c558da 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -31,6 +31,11 @@ jobs: minimum-size: 8GB maximum-size: 10GB disk-root: "C:" + - name: 'Checkout lsp4ij' + uses: actions/checkout@v3 + with: + repository: MicroShed/lsp4ij + path: lsp4ij - name: 'Checkout liberty-tools-intellij' uses: actions/checkout@v3 with: @@ -40,16 +45,12 @@ jobs: - name: 'Install required integration test software' working-directory: ./liberty-tools-intellij run: bash ./src/test/resources/ci/scripts/setup.sh - - name: 'Run UI integration tests' - working-directory: ./liberty-tools-intellij - run: bash ./src/test/resources/ci/scripts/run.sh - - name: 'Archive Test logs and reports' - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.reportName }} - path: | - liberty-tools-intellij/build/reports/ + - name: 'Build lsp4ij' + working-directory: ./lsp4ij + run: bash ./gradlew jar + - name: 'Install lsp4ij jar in local Maven repository' + working-directory: ./lsp4ij + run: mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true - name: 'Build Liberty-Tools-Intellij' working-directory: ./liberty-tools-intellij run: bash ./gradlew buildPlugin @@ -63,3 +64,13 @@ jobs: ./**/libs/*liberty-tools-intellij*.jar if-no-files-found: warn retention-days: 7 + - name: 'Run UI integration tests' + working-directory: ./liberty-tools-intellij + run: bash ./src/test/resources/ci/scripts/run.sh + - name: 'Archive Test logs and reports' + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.reportName }} + path: | + liberty-tools-intellij/build/reports/ From 1a40318a55b6a777ed33329d85748b23a2f6e017 Mon Sep 17 00:00:00 2001 From: Michael Glavassevich Date: Thu, 26 Oct 2023 14:13:35 -0400 Subject: [PATCH 09/15] Try running the mvn command using bash. Signed-off-by: Michael Glavassevich --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 957c558da..83273ef66 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,7 +50,7 @@ jobs: run: bash ./gradlew jar - name: 'Install lsp4ij jar in local Maven repository' working-directory: ./lsp4ij - run: mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true + run: bash ./mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true - name: 'Build Liberty-Tools-Intellij' working-directory: ./liberty-tools-intellij run: bash ./gradlew buildPlugin From ee48392d0e95dd083abcb3d8c3306bd28e6a4d8f Mon Sep 17 00:00:00 2001 From: Michael Glavassevich Date: Thu, 26 Oct 2023 14:20:23 -0400 Subject: [PATCH 10/15] Second attempt to call mvn through bash. Signed-off-by: Michael Glavassevich --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 83273ef66..c623cd624 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,7 +50,7 @@ jobs: run: bash ./gradlew jar - name: 'Install lsp4ij jar in local Maven repository' working-directory: ./lsp4ij - run: bash ./mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true + run: bash mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true - name: 'Build Liberty-Tools-Intellij' working-directory: ./liberty-tools-intellij run: bash ./gradlew buildPlugin From 57f79f1fd67416ad673d9746ee311c62c8cf7d00 Mon Sep 17 00:00:00 2001 From: Michael Glavassevich Date: Thu, 26 Oct 2023 14:29:19 -0400 Subject: [PATCH 11/15] Third attempt to call mvn through bash. Signed-off-by: Michael Glavassevich --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c623cd624..f581646dc 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,7 +50,7 @@ jobs: run: bash ./gradlew jar - name: 'Install lsp4ij jar in local Maven repository' working-directory: ./lsp4ij - run: bash mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true + run: bash ./mvnw install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true - name: 'Build Liberty-Tools-Intellij' working-directory: ./liberty-tools-intellij run: bash ./gradlew buildPlugin From 95d3f7f9b58b1510a3f92e0302a6c34df649e5f5 Mon Sep 17 00:00:00 2001 From: Michael Glavassevich Date: Fri, 27 Oct 2023 12:05:47 -0400 Subject: [PATCH 12/15] Try using Gradle to publish to the local Maven repository. Signed-off-by: Michael Glavassevich --- .github/workflows/build.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f581646dc..8dcf2f83e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,12 +45,9 @@ jobs: - name: 'Install required integration test software' working-directory: ./liberty-tools-intellij run: bash ./src/test/resources/ci/scripts/setup.sh - - name: 'Build lsp4ij' + - name: 'Build lsp4ij jar and publish to local Maven repository' working-directory: ./lsp4ij - run: bash ./gradlew jar - - name: 'Install lsp4ij jar in local Maven repository' - working-directory: ./lsp4ij - run: bash ./mvnw install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true + run: bash ./gradlew publishToMavenLocal - name: 'Build Liberty-Tools-Intellij' working-directory: ./liberty-tools-intellij run: bash ./gradlew buildPlugin From c4bf04757d5c25dc2b24a6fd96b8e8eedd435ed6 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 27 Oct 2023 17:26:29 -0400 Subject: [PATCH 13/15] Change lsp4ij to 0.0.1. Signed-off-by: Paul Gooderham --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 583952598..1436451a0 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,7 @@ configurations { dependencies { implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.15.0' - implementation 'org.microshed:lsp4ij:0.0.5' // includes org.eclipse.lsp4j + implementation 'org.microshed:lsp4ij:0.0.1' // includes org.eclipse.lsp4j implementation ('org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:0.6.0') { exclude group: 'org.eclipse.lsp4j' } From f9a8b10bf4b53e24174f3581a2661f1f349f6794 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Fri, 27 Oct 2023 17:33:45 -0400 Subject: [PATCH 14/15] Remove attempt to import xml file. Signed-off-by: Paul Gooderham --- src/main/resources/META-INF/plugin.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2c29b9981..9600d9365 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -366,7 +366,4 @@ - - - From 6570cceb831981a3f7ee11b069b7aaa8a9b7df73 Mon Sep 17 00:00:00 2001 From: Paul Gooderham Date: Mon, 30 Oct 2023 16:59:32 -0400 Subject: [PATCH 15/15] Update the task to save the jar to local Maven dir. Signed-off-by: Paul Gooderham --- DEVELOPING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index a9a3e928c..3c3b6e70b 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -19,7 +19,7 @@ This extension is built using the [gradle-intellij-plugin](https://github.com/Je 1. Clone this repository: `git clone git@github.com:OpenLiberty/liberty-tools-intellij.git` 2. Clone the lsp4ij repository: `git clone git@github.com:MicroShed/lsp4ij.git` 3. Build lsp4ij: `./gradlew jar` -4. Save it in your local Maven repository: `mvn install:install-file -Dfile=build/libs/lsp4ij-0.0.5.jar -DgroupId=org.microshed -DartifactId=lsp4ij -Dversion=0.0.5 -Dpackaging=jar -DgeneratePom=true;` +4. Save it in your local Maven repository: `./gradlew publishToMavenLocal` (this also runs the `jar` task) 2. Import this repository as a Gradle project in IntelliJ IDEA 3. Run `./gradlew runIde --stacktrace`. A new IntelliJ IDEA window will launch with the Liberty Tools plugin installed to it. You can connect the IntelliJ IDEA debugger to this process to debug the plugin.