diff --git a/README.md b/README.md index 358150ce5..018120415 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ ## Description A plugin aimed at Quarkus based development, providing easy bootstrapping and code assist from [Quarkus](https://quarkus.io/) related assets and [Qute](https://quarkus.io/guides/qute-reference). + +To provide those support, the plugin consumes: + + * [MicroProfile Language Server](https://github.com/eclipse/lsp4mp/tree/master/microprofile.ls) + * [Qute Language Server](https://github.com/redhat-developer/quarkus-ls/tree/master/qute.ls) + +by using [LSP4IJ (Language Server Protocol for Intellij)](https://github.com/redhat-developer/lsp4ij). + ### application.properties support diff --git a/build.gradle.kts b/build.gradle.kts index f94ebbd09..c7916f664 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,6 @@ val quarkusVersion = prop("quarkusVersion") val lsp4mpVersion = prop("lsp4mpVersion") val quarkusLsVersion = prop("lsp4mpVersion") val quteLsVersion = prop("quteLsVersion") - // Configure project's dependencies repositories { mavenLocal() @@ -64,10 +63,11 @@ sourceSets { } // Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog + dependencies { implementation("org.zeroturnaround:zt-zip:1.14") - implementation("com.kotcrab.remark:remark:1.2.0") - implementation("org.jsoup:jsoup:1.14.2") + implementation("com.kotcrab.remark:remark:1.2.0") //FIXME use lsp4ij's flexmark instead + implementation("org.jsoup:jsoup:1.14.2") //FIXME use lsp4ij's jsoup instead implementation("io.quarkus:quarkus-core:$quarkusVersion") { isTransitive = false } @@ -86,18 +86,20 @@ dependencies { implementation("io.quarkus:quarkus-arc:$quarkusVersion") { isTransitive = false } - implementation("org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:$lsp4mpVersion") - implementation("org.eclipse.lsp4j:org.eclipse.lsp4j:0.15.0") - // 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.62.2") + + implementation("org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:$lsp4mpVersion") { + exclude("org.eclipse.lsp4j") + } + // Exclude all lsp4j dependencies to use LSP4J from LSP4IJ + implementation("com.redhat.microprofile:com.redhat.qute.ls:$quteLsVersion") { + exclude("org.eclipse.lsp4j") + } lsp("org.eclipse.lsp4mp:org.eclipse.lsp4mp.ls:$lsp4mpVersion:uber") { isTransitive = false } lsp("com.redhat.microprofile:com.redhat.quarkus.ls:$quarkusLsVersion") { isTransitive = false } - implementation("com.redhat.microprofile:com.redhat.qute.ls:$quteLsVersion") lsp("com.redhat.microprofile:com.redhat.qute.ls:$quteLsVersion:uber") { isTransitive = false } @@ -107,7 +109,6 @@ dependencies { testImplementation("com.redhat.devtools.intellij:intellij-common-ui-test-library:0.2.0") testImplementation("org.assertj:assertj-core:3.19.0") - } // Set the JVM language level used to build the project. Use Java 11 for 2020.3+, and Java 17 for 2022.2+. @@ -124,8 +125,23 @@ intellij { version = properties("platformVersion") type = properties("platformType") updateSinceUntilBuild = false + // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file. - plugins = properties("platformPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) } + val platformPlugins = ArrayList() + //Need to manually run ./gradlew updateLsp4ijDistribution before building, + //I can't figure out how to ensure it's run automatically before intellij plugin is configured + val localLsp4ij = file(layout.buildDirectory.dir("LSP4IJ")) + if (localLsp4ij.isDirectory) { + platformPlugins.add(file(localLsp4ij)) + } else { + if (!environment("CI").isPresent && file("../lsp4ij").exists()) { + println("Run './gradlew updateLsp4ijDistribution' to use locally built LSP4IJ") + } + platformPlugins.add("com.redhat.devtools.lsp4ij:0.0.1-20231206-143458@nightly") + } + platformPlugins.addAll(properties("platformPlugins").map { it.split(',').map(String::trim).filter(String::isNotEmpty) }.get()) + println("platformPlugins: $platformPlugins") + plugins = platformPlugins } configurations { @@ -184,6 +200,25 @@ tasks.register("integrationTest") { mustRunAfter(tasks["test"]) } +tasks.register("updateLsp4ijDistribution") { + val buildDir = layout.buildDirectory + val zipFileTree = project.fileTree("../lsp4ij/build/distributions/") { + include("LSP4IJ-*.zip") + } + val matchingZipFiles = zipFileTree.files.sortedByDescending { it.lastModified() } + if (matchingZipFiles.isNotEmpty()) { + val destinationDir = buildDir.dir("LSP4IJ/lib").get().asFile + destinationDir.deleteRecursively() + val latestZipFile = matchingZipFiles.first() + from(zipTree(latestZipFile)) + into(buildDir) + doLast { + val numFilesCopied = destinationDir.listFiles()?.size ?: 0 + logger.quiet("Copied $numFilesCopied JARs from LSP4IJ distribution to ${destinationDir}.") + } + } +} + tasks.register("copyDeps") { val serverDir = layout.buildDirectory.dir("server/server") from(lsp) diff --git a/gradle.properties b/gradle.properties index 6ed7b2709..4230ef583 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ platformType=IC platformVersion=2022.2.3 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 -platformPlugins=com.intellij.java, maven, gradle-java, properties, yaml, com.redhat.devtools.intellij.telemetry:1.0.0.44 +platformPlugins=com.intellij.java, maven, gradle-java, properties, yaml, com.redhat.devtools.intellij.telemetry:1.0.0.44 # Gradle Releases -> https://github.com/gradle/gradle/releases gradleVersion=8.4 channel=nightly diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1930c0808..f2d658759 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ annotations = "24.0.1" testlogger = "3.2.0" kotlin = "1.9.10" changelog = "2.2.0" -gradleIntelliJPlugin = "1.16.0" +gradleIntelliJPlugin = "1.16.1" [libraries] diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java deleted file mode 100644 index eca72e645..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractDocumentMatcher.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.internal.PromiseToCompletableFuture; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.CompletableFuture; - -/** - * Abstract document matcher which prevent the execute of the match in an read action and when IJ is not indexing. - */ -public abstract class AbstractDocumentMatcher implements DocumentMatcher { - - private class CompletableFutureWrapper extends PromiseToCompletableFuture { - - public CompletableFutureWrapper(@NotNull VirtualFile file, @NotNull Project project) { - super(indicator -> { - return AbstractDocumentMatcher.this.match(file, project); - }, "Match with " + AbstractDocumentMatcher.this.getClass().getName(), - project, null, AbstractDocumentMatcher.class, file.getUrl()); - init(); - } - } - - @Override - public @NotNull CompletableFuture matchAsync(@NotNull VirtualFile file, @NotNull Project project) { - return new CompletableFutureWrapper(file, project); - } - - @Override - public boolean shouldBeMatchedAsynchronously(@NotNull Project project) { - if (ApplicationManager.getApplication().isUnitTestMode()) { - return false; - } - if (!ApplicationManager.getApplication().isReadAccessAllowed()) { - return true; - } - return DumbService.getInstance(project).isDumb(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractLSPInlayProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractLSPInlayProvider.java deleted file mode 100644 index fa82cbc44..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/AbstractLSPInlayProvider.java +++ /dev/null @@ -1,178 +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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.codeInsight.hints.*; -import com.intellij.codeInsight.hints.presentation.PresentationFactory; -import com.intellij.ide.DataManager; -import com.intellij.lang.Language; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.actionSystem.impl.SimpleDataContext; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.ui.layout.LCFlags; -import com.intellij.ui.layout.LayoutKt; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import org.eclipse.lsp4j.Command; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import java.awt.*; - -public abstract class AbstractLSPInlayProvider implements InlayHintsProvider { - - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLSPInlayProvider.class); - - private final Key cancellationSupportKey; - - protected AbstractLSPInlayProvider(Key cancellationSupportKey) { - this.cancellationSupportKey = cancellationSupportKey; - } - - private SettingsKey key = new SettingsKey<>("LSP.hints"); - - - @Nullable - @Override - public final InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile, - @NotNull Editor editor, - @NotNull NoSettings o, - @NotNull InlayHintsSink inlayHintsSink) { - CancellationSupport previousCancellationSupport = editor.getUserData(cancellationSupportKey); - if (previousCancellationSupport != null) { - previousCancellationSupport.cancel(); - } - CancellationSupport cancellationSupport = new CancellationSupport(); - editor.putUserData(cancellationSupportKey, cancellationSupport); - - return new FactoryInlayHintsCollector(editor) { - - private boolean processed; - - @Override - public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) { - if (processed) { - // Before IJ 2023-3, FactoryInlayHintsCollector#collect(PsiElement element.. is called once time with PsiFile as element. - // Since IJ 2023-3, FactoryInlayHintsCollector#collect(PsiElement element.. is called several times for each token of the PsiFile - // which causes the problem of codelens/inlay hint which are not displayed because there are too many call of LSP request codelens/inlayhint which are cancelled. - // With IJ 2023-3 we need to collect LSP CodeLens/InlayHint just for the first call. - return false; - } - processed = true; - VirtualFile file = getFile(psiFile); - if (file == null) { - // InlayHint must not be collected - return false; - } - try { - doCollect(file, psiFile.getProject(), editor, getFactory(), inlayHintsSink, cancellationSupport); - cancellationSupport.checkCanceled(); - } catch (ProcessCanceledException e) { - // Cancel all LSP requests - cancellationSupport.cancel(); - } catch (InterruptedException e) { - // Cancel all LSP requests - cancellationSupport.cancel(); - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - return false; - } - }; - } - - - @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(CommandExecutor.LSP_COMMAND, command, DataManager.getInstance().getDataContext(source)); - action.actionPerformed(new AnActionEvent(null, context, - ActionPlaces.UNKNOWN, new Presentation(), - ActionManager.getInstance(), 0)); - } - } - } - - protected abstract void doCollect(@NotNull VirtualFile file, @NotNull Project project, @NotNull Editor editor, @NotNull PresentationFactory factory, @NotNull InlayHintsSink inlayHintsSink, @NotNull CancellationSupport cancellationSupport) throws InterruptedException; - - /** - * Returns the virtual file where inlay hint must be added and null otherwise. - * - * @param psiFile the psi file. - * @return the virtual file where inlay hint must be added and null otherwise. - */ - private @Nullable VirtualFile getFile(@NotNull PsiFile psiFile) { - Project project = psiFile.getProject(); - if (project.isDisposed()) { - // The project has been closed, don't collect inlay hints. - return null; - } - return LSPIJUtils.getFile(psiFile); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/CompletableFutures.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/CompletableFutures.java deleted file mode 100644 index 623327ee1..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/CompletableFutures.java +++ /dev/null @@ -1,63 +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 com.redhat.devtools.intellij.lsp4ij; - -import org.eclipse.lsp4j.jsonrpc.CancelChecker; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures.FutureCancelChecker; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -/** - * {@link CompletableFuture} utility class. - * - * @author Angelo ZERR - */ -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(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java deleted file mode 100644 index 75aee7ace..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java +++ /dev/null @@ -1,74 +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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManagerListener; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey; -import com.redhat.devtools.intellij.lsp4ij.internal.PromiseToCompletableFuture; -import com.redhat.devtools.intellij.lsp4ij.lifecycle.LanguageServerLifecycleManager; -import org.jetbrains.annotations.NotNull; - -import java.text.MessageFormat; - -/** - * Track file opened / closed to start language servers / disconnect file from language servers. - */ -public class ConnectDocumentToLanguageServerSetupParticipant implements ProjectManagerListener, FileEditorManagerListener { - - private static class ConnectToLanguageServerCompletableFuture extends PromiseToCompletableFuture { - - private static final String MESSAGE_KEY = "Connect ''{0}'' file to language servers."; - - public ConnectToLanguageServerCompletableFuture(@NotNull VirtualFile file, @NotNull Project project) { - super(monitor -> { - connectToLanguageServer(file, project); - return null; - }, MessageFormat.format(MESSAGE_KEY, file.getUrl()), project, null, new CoalesceByKey(ConnectDocumentToLanguageServerSetupParticipant.class.getName(), file.getUrl())); - init(); - } - } - - @Override - public void projectOpened(@NotNull Project project) { - project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this); - } - - @Override - public void projectClosing(@NotNull Project project) { - LanguageServerLifecycleManager.getInstance(project).dispose(); - LanguageServiceAccessor.getInstance(project).projectClosing(project); - } - - @Override - public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - Project project = source.getProject(); - // As document matcher requires read action, and language server starts can take some times, we connect the file in a future - // to avoid starting language server in the EDT Thread which could freeze IJ. - // Wait for indexing is finished and read action is enabled - // --> force the start of all languages servers mapped with the given file when indexing is finished and read action is allowed - new ConnectToLanguageServerCompletableFuture(file, project); - } - - private static void connectToLanguageServer(@NotNull VirtualFile file, @NotNull Project project) { - // Force the start of all languages servers mapped with the given file - // Server capabilities filter is set to null to avoid waiting - // for the start of the server when server capabilities are checked - LanguageServiceAccessor.getInstance(project) - .getLanguageServers(file, null); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java deleted file mode 100644 index af8af3d97..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ContentTypeToLanguageServerDefinition.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; - -import java.util.AbstractMap; -import java.util.concurrent.CompletableFuture; - -public class ContentTypeToLanguageServerDefinition extends AbstractMap.SimpleEntry { - - private final DocumentMatcher documentMatcher; - - public ContentTypeToLanguageServerDefinition(@NotNull Language language, - @NotNull LanguageServersRegistry.LanguageServerDefinition provider, - @NotNull DocumentMatcher documentMatcher) { - super(language, provider); - this.documentMatcher = documentMatcher; - } - - public boolean match(VirtualFile file, Project project) { - return getValue().supportsCurrentEditMode(project) && documentMatcher.match(file, project); - } - - public boolean shouldBeMatchedAsynchronously(Project project) { - return documentMatcher.shouldBeMatchedAsynchronously(project); - } - - public boolean isEnabled() { - return getValue().isEnabled(); - } - - public @NotNull CompletableFuture matchAsync(VirtualFile file, Project project) { - if (!getValue().supportsCurrentEditMode(project)) { - return CompletableFuture.completedFuture(false); - } - return documentMatcher.matchAsync(file, project); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java deleted file mode 100644 index c76bc8c28..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java +++ /dev/null @@ -1,212 +0,0 @@ -package com.redhat.devtools.intellij.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.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.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class DocumentContentSynchronizer implements DocumentListener { - private final static Logger LOGGER = LoggerFactory.getLogger(DocumentContentSynchronizer.class); - - private final @NotNull LanguageServerWrapper languageServerWrapper; - private final @NotNull Document document; - private final @NotNull String fileUri; - private final TextDocumentSyncKind syncKind; - - private int version = 0; - private final List changeEvents; - private long modificationStamp; - final @NotNull - CompletableFuture didOpenFuture; - - public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServerWrapper, - @NotNull URI fileUri, - @NotNull Document document, - TextDocumentSyncKind syncKind) { - this.languageServerWrapper = languageServerWrapper; - this.fileUri = fileUri.toASCIIString(); - this.modificationStamp = -1; - this.syncKind = syncKind != null ? syncKind : TextDocumentSyncKind.Full; - - this.document = document; - // add a document buffer - TextDocumentItem textDocument = new TextDocumentItem(); - textDocument.setUri(this.fileUri); - textDocument.setText(document.getText()); - - Language contentTypes = LSPIJUtils.getDocumentLanguage(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; - } - if (syncKind == TextDocumentSyncKind.Full) { - synchronized (changeEvents) { - changeEvents.clear(); - changeEvents.add(createChangeEvent(event)); - } - } - - if (ApplicationManager.getApplication().isUnitTestMode()) { - sendDidChangeEvents(); - } else { - PsiDocumentManager.getInstance(languageServerWrapper.getProject()).performForCommittedDocument(event.getDocument(), this::sendDidChangeEvents); - } - } - - private void sendDidChangeEvents() { - List events = null; - synchronized (changeEvents) { - events = new ArrayList<>(changeEvents); - changeEvents.clear(); - } - - DidChangeTextDocumentParams changeParamsToSend = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(), events); - changeParamsToSend.getTextDocument().setUri(fileUri.toString()); - changeParamsToSend.getTextDocument().setVersion(++version); - languageServerWrapper.sendNotification(ls -> ls.getTextDocumentService().didChange(changeParamsToSend)); - } - - @Override - public void beforeDocumentChange(DocumentEvent 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) { - Document document = event.getDocument(); - 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() { - 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.sendNotification(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); - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java deleted file mode 100644 index 798ad1c05..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentMatcher.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.concurrency.CancellablePromise; - -import java.util.concurrent.CompletableFuture; - -/** - * When a file is opened, the LSP support connect all available language server which matches the language of the file. - *

- * DocumentMatcher provides the capability to add advanced filter like check the file name, check that project have some Java classes in the classpath. - */ -public interface DocumentMatcher { - - /** - * Returns true if the given file matches a mapping with a language server and false otherwise. - * - * @param file teh file to check. - * @param project the file project. - * @return true if the given file matches a mapping with a language server and false otherwise. - */ - boolean match(@NotNull VirtualFile file, @NotNull Project project); - - /** - * Returns true if the given file matches a mapping with a language server and false otherwise. - *

- * In this case,the match is done in async mode. A typical usecase is when the matcher need to check that a given Java class belongs to the project or the file belongs to a source folder. - * To evaluate this match, the read action mode is required and it can create a non blocking read action to evaluate the match. - * - * @param file - * @param project - * @return true if the given file matches a mapping with a language server and false otherwise. - * @see AbstractDocumentMatcher - */ - default @NotNull CompletableFuture matchAsync(@NotNull VirtualFile file, @NotNull Project project) { - return CompletableFuture.completedFuture(match(file, project)); - } - - /** - * Returns true if the match must be done asynchronously and false otherwise. - *

- * A typical usecase is when IJ is indexing or read action is not allowed,this method should return true, to execute match in a non blocking read action. - * - * @param project the project. - * @return true if the match must be done asynchronously and false otherwise. - * @see AbstractDocumentMatcher - */ - default boolean shouldBeMatchedAsynchronously(@NotNull Project project) { - return false; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java deleted file mode 100644 index de1788c25..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java +++ /dev/null @@ -1,531 +0,0 @@ -package com.redhat.devtools.intellij.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.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.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.*; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.light.LightRecordField; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; -import org.apache.commons.io.FileUtils; -import org.eclipse.lsp4j.*; -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.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); - - private static final String JAR_PROTOCOL = "jar"; - - private static final String JRT_PROTOCOL = "jrt"; - - private static final String JAR_SCHEME = JAR_PROTOCOL + ":"; - - private static final String JRT_SCHEME = JRT_PROTOCOL + ":"; - - public static void openInEditor(Location location, Project project) { - if (location == null) { - return; - } - openInEditor(location.getUri(), location.getRange().getStart(), project); - } - - public static void openInEditor(String fileUri, Position position, Project project) { - VirtualFile file = findResourceFor(fileUri); - openInEditor(file, position, project); - } - - public static void openInEditor(VirtualFile file, Position position, Project project) { - if (file != null) { - if (position == null) { - FileEditorManager.getInstance(project).openFile(file, true); - } else { - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - OpenFileDescriptor desc = new OpenFileDescriptor(project, file, LSPIJUtils.toOffset(position, document)); - FileEditorManager.getInstance(project).openTextEditor(desc, true); - } - } - } - } - - @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) { - Position start = toPosition(offset, document); - param.setPosition(start); - TextDocumentIdentifier id = new TextDocumentIdentifier(); - URI uri = toUri(document); - if (uri != null) { - id.setUri(uri.toASCIIString()); - } - 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); - } - - - /** - * Returns the Uri of the virtual file corresponding to the specified document. - * - * @param document the document for which the virtual file is requested. - * @return the Uri of the file, or null if the document wasn't created from a virtual file. - */ - public static @Nullable URI toUri(@NotNull Document document) { - VirtualFile file = getFile(document); - return file != null ? toUri(file) : null; - } - - public static @NotNull URI toUri(@NotNull 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 @Nullable URI toUri(@NotNull PsiFile psiFile) { - VirtualFile file = getFile(psiFile); - return file != null ? toUri(file) : null; - } - - public static @NotNull URI toUri(@NotNull VirtualFile file) { - return toUri(VfsUtilCore.virtualToIoFile(file)); - } - - public static @Nullable String toUriAsString(@NotNull PsiFile psFile) { - VirtualFile file = psFile.getVirtualFile(); - return file != null ? toUriAsString(file) : null; - } - - public static @NotNull String toUriAsString(@NotNull VirtualFile file) { - String protocol = file.getFileSystem() != null ? file.getFileSystem().getProtocol() : null; - if (JAR_PROTOCOL.equals(protocol) || JRT_PROTOCOL.equals(protocol)) { - return VfsUtilCore.convertToURL(file.getUrl()).toExternalForm(); - } - return toUri(VfsUtilCore.virtualToIoFile(file)).toASCIIString(); - } - - /** - * Returns the virtual file corresponding to the specified document. - * - * @param document the document for which the virtual file is requested. - * @return the file, or null if the document wasn't created from a virtual file. - */ - public static @Nullable VirtualFile getFile(@NotNull Document document) { - if (ApplicationManager.getApplication().isReadAccessAllowed()) { - return FileDocumentManager.getInstance().getFile(document); - } - return ReadAction.compute(() -> FileDocumentManager.getInstance().getFile(document)); - } - - /** - * Returns the virtual file corresponding to the PSI file. - * - * @return the virtual file, or {@code null} if the file exists only in memory. - */ - public static @Nullable VirtualFile getFile(@NotNull PsiElement element) { - PsiFile psFile = element.getContainingFile(); - return psFile != null ? psFile.getVirtualFile() : null; - } - - public static @Nullable Document getDocument(@NotNull VirtualFile file) { - if (ApplicationManager.getApplication().isReadAccessAllowed()) { - return FileDocumentManager.getInstance().getDocument(file); - } - return ReadAction.compute(() -> FileDocumentManager.getInstance().getDocument(file)); - } - - /** - * Returns the @{@link Document} associated to the given @{@link URI}, or null if there's no match. - * - * @param documentUri the uri of the Document to return - * @return the @{@link Document} associated to documentUri, or null - */ - public static @Nullable Document getDocument(URI documentUri) { - if (documentUri == null) { - return null; - } - VirtualFile documentFile = findResourceFor(documentUri.toASCIIString()); - return getDocument(documentFile); - } - - @Nullable - public static Module getModule(@Nullable VirtualFile file, @NotNull Project project) { - if (file == null) { - return null; - } - if (ApplicationManager.getApplication().isReadAccessAllowed()) { - return ProjectFileIndex.getInstance(project).getModuleForFile(file, false); - } - return ReadAction.compute(() -> ProjectFileIndex.getInstance(project).getModuleForFile(file, false)); - } - - public static int toOffset(Position start, Document document) throws IndexOutOfBoundsException { - 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 Project project) { - WorkspaceFolder folder = new WorkspaceFolder(); - folder.setUri(toUri(project).toASCIIString()); - folder.setName(project.getName()); - return folder; - } - - public static URI toUri(Module module) { - VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots(); - if (roots.length > 0) { - return toUri(roots[0]); - } - File file = new File(module.getModuleFilePath()).getParentFile(); - return file.toURI(); - } - - public static URI toUri(Project project) { - VirtualFile[] roots = ProjectRootManager.getInstance(project).getContentRoots(); - if (roots.length > 0) { - return toUri(roots[0]); - } - File file = new File(project.getProjectFilePath()).getParentFile(); - return file.toURI(); - } - - public static Range toRange(TextRange range, Document document) { - return new Range(LSPIJUtils.toPosition(range.getStartOffset(), document), LSPIJUtils.toPosition(range.getEndOffset(), document)); - } - - /** - * Returns the IJ {@link TextRange} from the given LSP range and null otherwise. - * - * @param range the LSP range to conert. - * @param document the document. - * @return the IJ {@link TextRange} from the given LSP range and null otherwise. - */ - public static @Nullable TextRange toTextRange(Range range, Document document) { - try { - final int start = LSPIJUtils.toOffset(range.getStart(), document); - final int end = LSPIJUtils.toOffset(range.getEnd(), document); - if (start >= end || end > document.getTextLength()) { - // Language server reports invalid diagnostic, ignore it. - return null; - } - return new TextRange(start, end); - } catch (IndexOutOfBoundsException e) { - // Language server reports invalid diagnostic, ignore it. - LOGGER.warn("Invalid LSP text range", e); - return null; - } - } - - public static Location toLocation(PsiElement psiMember) { - PsiElement sourceElement = getNavigationElement(psiMember); - - if (sourceElement != null) { - PsiFile file = sourceElement.getContainingFile(); - Document document = PsiDocumentManager.getInstance(psiMember.getProject()).getDocument(file); - if (document != null) { - TextRange range = sourceElement.getTextRange(); - return toLocation(file, toRange(range, document)); - } - } - return null; - } - - private static @Nullable PsiElement getNavigationElement(PsiElement psiMember) { - if (psiMember instanceof LightRecordField) { - psiMember = ((LightRecordField) psiMember).getRecordComponent(); - } - return psiMember.getNavigationElement(); - } - - public static Location toLocation(PsiFile file, Range range) { - return toLocation(file.getVirtualFile(), range); - } - - public static Location toLocation(VirtualFile file, Range range) { - return new Location(toUriAsString(file), range); - } - - 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) { - CreateFile createOperation = (CreateFile) resourceOperation; - VirtualFile targetFile = findResourceFor(createOperation.getUri()); - 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 { - String fileUri = createOperation.getUri(); - createFile(fileUri); - } catch (IOException 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()); - } - } - - } - - } - } - - /** - * Create the file with the given file Uri. - * - * @param fileUri the file Uri. - * @return the created virtual file and null otherwise. - * @throws IOException - */ - public static @Nullable VirtualFile createFile(String fileUri) throws IOException { - URI targetURI = URI.create(fileUri); - return createFile(targetURI); - } - - /** - * Create the file with the given file Uri. - * - * @param fileUri the file Uri. - * @return the created virtual file and null otherwise. - * @throws IOException - */ - public static @Nullable VirtualFile createFile(URI fileUri) throws IOException { - File newFile = new File(fileUri); - FileUtils.createParentDirectories(newFile); - newFile.createNewFile(); - return VfsUtil.findFileByIoFile(newFile, true); - } - - private static void applyWorkspaceEdit(Document document, List edits) { - for (TextEdit edit : edits) { - if (edit.getRange() != null) { - String text = edit.getNewText(); - 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", ""); - 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 @Nullable VirtualFile findResourceFor(URI uri) { - return LocalFileSystem.getInstance().findFileByIoFile(Paths.get(uri).toFile()); - } - - public static @Nullable VirtualFile findResourceFor(String uri) { - if (uri.startsWith(JAR_SCHEME) || uri.startsWith(JRT_SCHEME)) { - // ex : jar:file:///C:/Users/azerr/.m2/repository/io/quarkus/quarkus-core/3.0.1.Final/quarkus-core-3.0.1.Final.jar!/io/quarkus/runtime/ApplicationConfig.class - try { - return VfsUtil.findFileByURL(new URL(uri)); - } catch (MalformedURLException e) { - return null; - } - } - return VirtualFileManager.getInstance().findFileByUrl(VfsUtilCore.fixURLforIDEA(uri)); - } - - public static @Nullable Editor editorForElement(@Nullable PsiElement element) { - if (element != null && element.getContainingFile() != null && element.getContainingFile().getVirtualFile() != null) { - return editorForFile(element.getContainingFile().getVirtualFile(), element.getProject()); - } - return null; - } - - private static @Nullable Editor editorForFile(@Nullable VirtualFile file, @NotNull Project project) { - Editor[] editors = editorsForDocument(getDocument(file), project); - return editors.length > 0 ? editors[0] : null; - } - - private static @NotNull Editor[] editorsForDocument(@Nullable Document document, @Nullable Project project) { - if (document == null) { - return new Editor[0]; - } - return EditorFactory.getInstance().getEditors(document, project); - } - - 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)); - marker.setGreedyToRight(true); - int startOffset = marker.getStartOffset(); - int endOffset = marker.getEndOffset(); - String text = textEdit.getNewText(); - if (text != null) { - text = text.replaceAll("\r", ""); - } - if (text == null || text.isEmpty()) { - document.deleteString(startOffset, endOffset); - } else if (endOffset - startOffset <= 0) { - document.insertString(startOffset, text); - } else { - document.replaceString(startOffset, endOffset, text); - } - if (text != null && !text.isEmpty()) { - editor.getCaretModel().moveToOffset(marker.getEndOffset()); - } - 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()); - } - - /** - * Returns the project URI of the given project. - * - * @param project the project - * @return the project URI of the given project. - */ - public static String getProjectUri(Module project) { - if (project == null) { - return null; - } - return project.getName(); - } - - /** - * Returns the project URI of the given project. - * - * @param project the project - * @return the project URI of the given project. - */ - public static String getProjectUri(Project project) { - if (project == null) { - return null; - } - return project.getName(); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileData.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileData.java deleted file mode 100644 index 4add3006b..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileData.java +++ /dev/null @@ -1,62 +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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.LSPDiagnosticsForServer; -import com.redhat.devtools.intellij.lsp4ij.operations.documentLink.LSPDocumentLinkForServer; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DocumentLink; - -import java.util.List; - -/** - * LSP data stored in {@link VirtualFile} which are used by some LSP operations. - * - * @author Angelo ZERR - */ -public class LSPVirtualFileData { - - private final LSPDiagnosticsForServer diagnosticsForServer; - - private final LSPDocumentLinkForServer documentLinkForServer; - - private final DocumentContentSynchronizer synchronizer; - - public LSPVirtualFileData(LanguageServerWrapper languageServerWrapper, VirtualFile file, DocumentContentSynchronizer synchronizer) { - this.synchronizer = synchronizer; - this.diagnosticsForServer = new LSPDiagnosticsForServer(languageServerWrapper,file); - this.documentLinkForServer = new LSPDocumentLinkForServer(languageServerWrapper, file); - } - - public DocumentContentSynchronizer getSynchronizer() { - return synchronizer; - } - - public LSPDiagnosticsForServer getDiagnosticsForServer() { - return diagnosticsForServer; - } - - public LSPDocumentLinkForServer getDocumentLinkForServer() { - return documentLinkForServer; - } - - public void updateDiagnostics(List diagnostics) { - diagnosticsForServer.update(diagnostics); - } - - public void updateDocumentLink(List documentLinks) { - documentLinkForServer.update(documentLinks); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java deleted file mode 100644 index ba1d74778..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageMappingExtensionPointBean.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.extensions.AbstractExtensionPointBean; -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.serviceContainer.BaseKeyedLazyInstance; -import com.intellij.util.xmlb.annotations.Attribute; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class LanguageMappingExtensionPointBean extends BaseKeyedLazyInstance { - - private static final DocumentMatcher DEFAULT_DOCUMENT_MATCHER = (file,project) -> true; - - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.languageMapping"); - - @Attribute("id") - public String id; - - @Attribute("language") - public String language; - - @Attribute("serverId") - public String serverId; - - @Attribute("documentMatcher") - public String documentMatcher; - - public @NotNull DocumentMatcher getDocumentMatcher() { - try { - return super.getInstance(); - } - catch(Exception e) { - return DEFAULT_DOCUMENT_MATCHER; - } - } - - @Override - protected @Nullable String getImplementationClassName() { - return documentMatcher; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerBundle.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerBundle.java deleted file mode 100644 index 66bd6b1d0..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerBundle.java +++ /dev/null @@ -1,45 +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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.DynamicBundle; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.PropertyKey; - -import java.util.function.Supplier; - -/** - * Language server messages bundle. - */ -public final class LanguageServerBundle extends DynamicBundle { - - @NonNls public static final String BUNDLE = "messages.LanguageServerBundle"; - private static final LanguageServerBundle INSTANCE = new LanguageServerBundle(); - - private LanguageServerBundle() { - super(BUNDLE); - } - - @NotNull - public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) { - return INSTANCE.getMessage(key, params); - } - - @NotNull - public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) { - return INSTANCE.getLazyMessage(key, params); - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerIconProviderDefinition.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerIconProviderDefinition.java deleted file mode 100644 index b62b63075..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerIconProviderDefinition.java +++ /dev/null @@ -1,32 +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 com.redhat.devtools.intellij.lsp4ij; - -import javax.swing.*; - -/** - * Definition for server icon provider. - */ -public class LanguageServerIconProviderDefinition { - - private final ServerIconProviderExtensionPointBean extension; - - public LanguageServerIconProviderDefinition(ServerIconProviderExtensionPointBean extension) { - this.extension = extension; - } - - public Icon getIcon() { - return extension.getInstance().getIcon(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerItem.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerItem.java deleted file mode 100644 index d179b0a39..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerItem.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij; - -import org.eclipse.lsp4j.services.LanguageServer; - -/** - * Item which stores the initialized LSP4j language server and the language server wrapper. - */ -public class LanguageServerItem { - - private final LanguageServerWrapper serverWrapper; - private final LanguageServer server; - - public LanguageServerItem(LanguageServer server, LanguageServerWrapper serverWrapper) { - this.server = server; - this.serverWrapper = serverWrapper; - } - - /** - * Returns the LSP4j language server. - * - * @return the LSP4j language server. - */ - public LanguageServer getServer() { - return server; - } - - /** - * Returns the language server wrapper. - * - * @return the language server wrapper. - */ - public LanguageServerWrapper getServerWrapper() { - return serverWrapper; - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java deleted file mode 100644 index 250313422..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java +++ /dev/null @@ -1,1080 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.intellij.lang.Language; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.*; -import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; -import com.intellij.util.messages.MessageBusConnection; -import com.redhat.devtools.intellij.lsp4ij.client.LanguageClientImpl; -import com.redhat.devtools.intellij.lsp4ij.internal.SupportedFeatures; -import com.redhat.devtools.intellij.lsp4ij.lifecycle.LanguageServerLifecycleManager; -import com.redhat.devtools.intellij.lsp4ij.lifecycle.NullLanguageServerLifecycleManager; -import com.redhat.devtools.intellij.lsp4ij.server.*; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -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.File; -import java.io.IOException; -import java.net.URI; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import java.util.function.UnaryOperator; - -/** - * Language server wrapper. - */ -public class LanguageServerWrapper implements Disposable { - - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServerWrapper.class);//$NON-NLS-1$ - private static final String CLIENT_NAME = "IntelliJ"; - private static final int MAX_NUMBER_OF_RESTART_ATTEMPTS = 20; // TODO move this max value in settings - - class Listener implements FileEditorManagerListener, VirtualFileListener { - - @Override - public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - if (initialProject != null && !Objects.equals(source.getProject(), initialProject)) { - // The file has been closed from another project,don't send textDocument/didClose - return; - } - // Manage textDocument/didClose - URI uri = LSPIJUtils.toUri(file); - if (uri != null) { - try { - // Disconnect the given file from the current language servers - disconnect(uri, !isDisposed()); - } catch (Exception e) { - LOGGER.warn("Error while disconnecting the file '" + uri + "' from all language servers", e); - } - } - } - - @Override - public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { - if (event.getPropertyName().equals(VirtualFile.PROP_NAME) && event.getOldValue() instanceof String) { - // A file (Test1.java) has been renamed (to Test2.java) by using Refactor / Rename from IJ - - // 1. Send a textDocument/didClose for the renamed file (Test1.java) - URI oldFileUri = didClose(event.getFile().getParent(), (String) event.getOldValue()); - URI newFileUri = LSPIJUtils.toUri(event.getFile()); - // 2. Send a workspace/didChangeWatchedFiles - didChangeWatchedFiles(fe(oldFileUri, FileChangeType.Deleted), - fe(newFileUri, FileChangeType.Created)); - } - } - - @Override - public void contentsChanged(@NotNull VirtualFileEvent event) { - URI uri = LSPIJUtils.toUri(event.getFile()); - if (uri != null) { - LSPVirtualFileData documentListener = connectedDocuments.get(uri); - if (documentListener != null) { - // 1. Send a textDocument/didSave for the saved file - documentListener.getSynchronizer().documentSaved(); - } - // 2. Send a workspace/didChangeWatchedFiles - didChangeWatchedFiles(fe(uri, FileChangeType.Changed)); - } - } - - @Override - public void fileCreated(@NotNull VirtualFileEvent event) { - URI uri = LSPIJUtils.toUri(event.getFile()); - if (uri != null) { - // 2. Send a workspace/didChangeWatchedFiles - didChangeWatchedFiles(fe(uri, FileChangeType.Created)); - } - } - - @Override - public void fileDeleted(@NotNull VirtualFileEvent event) { - URI uri = LSPIJUtils.toUri(event.getFile()); - if (uri != null) { - // 2. Send a workspace/didChangeWatchedFiles - didChangeWatchedFiles(fe(uri, FileChangeType.Deleted)); - } - } - - @Override - public void fileMoved(@NotNull VirtualFileMoveEvent event) { - // A file (foo.Test1.java) has been moved (to bar1.Test1.java) - - // 1. Send a textDocument/didClose for the moved file (foo.Test1.java) - URI oldFileUri = didClose(event.getOldParent(), event.getFileName()); - URI newFileUri = LSPIJUtils.toUri(event.getFile()); - // 2. Send a workspace/didChangeWatchedFiles - didChangeWatchedFiles(fe(oldFileUri, FileChangeType.Deleted), - fe(newFileUri, FileChangeType.Created)); - } - - private FileEvent fe(URI uri, FileChangeType type) { - return new FileEvent(uri.toASCIIString(), type); - } - - private @NotNull URI didClose(VirtualFile virtualParentFile, String fileName) { - File parent = VfsUtilCore.virtualToIoFile(virtualParentFile); - URI uri = LSPIJUtils.toUri(new File(parent, fileName)); - if (isConnectedTo(uri)) { - disconnect(uri, false); - } - return uri; - } - - private void didChangeWatchedFiles(FileEvent... changes) { - LanguageServerWrapper.this.sendNotification(ls -> { - DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(changes)); - ls.getWorkspaceService() - .didChangeWatchedFiles(params); - }); - } - } - - private Listener fileBufferListener = new Listener(); - private MessageBusConnection messageBusConnection; - - @NotNull - public final LanguageServersRegistry.LanguageServerDefinition serverDefinition; - @Nullable - protected final Project initialProject; - @NotNull - protected Map connectedDocuments; - @Nullable - protected final URI initialPath; - protected final InitializeParams initParams = new InitializeParams(); - - protected StreamConnectionProvider lspStreamProvider; - private Future launcherFuture; - - private int numberOfRestartAttempts; - private CompletableFuture initializeFuture; - private LanguageServer languageServer; - private LanguageClientImpl languageClient; - private ServerCapabilities serverCapabilities; - private Timer timer; - private final AtomicBoolean stopping = new AtomicBoolean(false); - - private ServerStatus serverStatus; - - private boolean disposed; - - private LanguageServerException serverError; - - private Long currentProcessId; - - private List currentProcessCommandLines; - - private final ExecutorService dispatcher; - - private final ExecutorService listener; - - /** - * Map containing unregistration handlers for dynamic capability registrations. - */ - private @NotNull - Map dynamicRegistrations = new HashMap<>(); - private boolean initiallySupportsWorkspaceFolders = false; - - /* Backwards compatible constructor */ - public LanguageServerWrapper(@NotNull Project project, @NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition) { - this(project, serverDefinition, null); - } - - public LanguageServerWrapper(@NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @Nullable URI initialPath) { - this(null, serverDefinition, initialPath); - } - - /** - * Unified private constructor to set sensible defaults in all cases - */ - private LanguageServerWrapper(@Nullable Project project, @NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, - @Nullable URI initialPath) { - this.initialProject = project; - this.initialPath = initialPath; - this.serverDefinition = serverDefinition; - this.connectedDocuments = new HashMap<>(); - String projectName = sanitize((project != null && project.getName() != null && !serverDefinition.isSingleton) ? ("@" + project.getName()) : ""); //$NON-NLS-1$//$NON-NLS-2$ - String dispatcherThreadNameFormat ="LS-" + serverDefinition.id + projectName + "#dispatcher"; //$NON-NLS-1$ //$NON-NLS-2$ - this.dispatcher = Executors - .newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(dispatcherThreadNameFormat).build()); - - // Executor service passed through to the LSP4j layer when we attempt to start the LS. It will be used - // to create a listener that sits on the input stream and processes inbound messages (responses, or server-initiated - // requests). - String listenerThreadNameFormat ="LS-" + serverDefinition.id + projectName + "#listener-%d"; //$NON-NLS-1$ //$NON-NLS-2$ - this.listener = Executors - .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat(listenerThreadNameFormat).build()); - udateStatus(ServerStatus.none); - if (project != null) { - // When project is disposed, we dispose the language server - // But the language server should be disposed before because when project is closing - // We do that to be sure that language server is disposed. - Disposer.register(project, this); - } - } - - /** - * Removes '%' from the given name - */ - private static String sanitize(String name) { - return name.replace("%",""); - } - - public Project getProject() { - return initialProject; - } - - void stopDispatcher() { - this.dispatcher.shutdownNow(); - - // Only really needed for testing - the listener (an instance of ConcurrentMessageProcessor) should exit - // as soon as the input stream from the LS is closed, and a cached thread pool will recycle idle - // threads after a 60 second timeout - or immediately in response to JVM shutdown. - // If we don't do this then a full test run will generate a lot of threads because we create new - // instances of this class for each test - this.listener.shutdownNow(); - } - - public synchronized void stopAndDisable() { - setEnabled(false); - stop(); - } - - public synchronized void restart() { - numberOfRestartAttempts = 0; - setEnabled(true); - stop(); - start(); - } - - private void setEnabled(boolean enabled) { - this.serverDefinition.setEnabled(enabled); - } - - public boolean isEnabled() { - return serverDefinition.isEnabled(); - } - - /** - * Starts a language server and triggers initialization. If language server is - * started and active, does nothing. If language server is inactive, restart it. - * - * @throws LanguageServerException thrown when the language server cannot be started - */ - public synchronized void start() throws LanguageServerException { - if (serverError != null) { - // Here the language server has been not possible - // we stop it and attempts a new restart if needed - stop(); - if (numberOfRestartAttempts > MAX_NUMBER_OF_RESTART_ATTEMPTS - 1) { - // Disable the language server - setEnabled(false); - return; - } else { - numberOfRestartAttempts++; - } - } - final var filesToReconnect = new ArrayList(); - if (this.languageServer != null) { - if (isActive()) { - return; - } else { - for (Map.Entry entry : this.connectedDocuments.entrySet()) { - filesToReconnect.add(entry.getKey()); - } - stop(); - } - } - - if (this.initializeFuture == null) { - final URI rootURI = getRootURI(); - this.launcherFuture = new CompletableFuture<>(); - this.initializeFuture = CompletableFuture.supplyAsync(() -> { - this.lspStreamProvider = serverDefinition.createConnectionProvider(initialProject); - initParams.setInitializationOptions(this.lspStreamProvider.getInitializationOptions(rootURI)); - - // Starting process... - udateStatus(ServerStatus.starting); - getLanguageServerLifecycleManager().onStatusChanged(this); - this.currentProcessId = null; - this.currentProcessCommandLines = null; - lspStreamProvider.start(); - - // As process can be stopped, we loose pid and command lines information - // when server is stopped, we store them here. - // to display them in the Language server explorer even if process is killed. - if (lspStreamProvider instanceof ProcessStreamConnectionProvider) { - ProcessStreamConnectionProvider provider = (ProcessStreamConnectionProvider) lspStreamProvider; - this.currentProcessId = provider.getPid(); - this.currentProcessCommandLines = provider.getCommands(); - } - - // Throws the CannotStartProcessException exception if process is not alive. - // This usecase comes for instance when the start process command fails (not a valid start command) - lspStreamProvider.ensureIsAlive(); - return null; - }).thenRun(() -> { - languageClient = serverDefinition.createLanguageClient(initialProject); - initParams.setProcessId(getParentProcessId()); - - if (rootURI != null) { - initParams.setRootUri(rootURI.toString()); - initParams.setRootPath(rootURI.getPath()); - } - - UnaryOperator wrapper = consumer -> (message -> { - logMessage(message, consumer); - try { - // To avoid having some lock problem when message is written in the stream output - // (when there are a lot of messages to write it) - // we consume the message in async mode - CompletableFuture.runAsync(() -> consumer.consume(message)); - } catch (Throwable e) { - // Log in the LSP console the error - getLanguageServerLifecycleManager().onError(this, e); - throw e; - } - final StreamConnectionProvider currentConnectionProvider = this.lspStreamProvider; - if (currentConnectionProvider != null && isActive()) { - currentConnectionProvider.handleMessage(message, this.languageServer, rootURI); - } - }); - Launcher launcher = serverDefinition.createLauncherBuilder() // - .setLocalService(languageClient)// - .setRemoteInterface(serverDefinition.getServerInterface())// - .setInput(lspStreamProvider.getInputStream())// - .setOutput(lspStreamProvider.getOutputStream())// - .setExecutorService(listener)// - .wrapMessages(wrapper)// - .create(); - this.languageServer = launcher.getRemoteProxy(); - languageClient.connect(languageServer, this); - this.launcherFuture = launcher.startListening(); - }) - .thenCompose(unused -> initServer(rootURI)) - .thenAccept(res -> { - serverError = null; - serverCapabilities = res.getCapabilities(); - this.initiallySupportsWorkspaceFolders = supportsWorkspaceFolders(serverCapabilities); - }).thenRun(() -> { - this.languageServer.initialized(new InitializedParams()); - }).thenRun(() -> { - final List toReconnect = filesToReconnect; - initializeFuture.thenRunAsync(() -> { - for (URI fileToReconnect : toReconnect) { - try { - connect(fileToReconnect); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - - messageBusConnection = ApplicationManager.getApplication().getMessageBus().connect(); - messageBusConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, fileBufferListener); - messageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkVirtualFileListenerAdapter(fileBufferListener)); - - udateStatus(ServerStatus.started); - getLanguageServerLifecycleManager().onStatusChanged(this); - }).exceptionally(e -> { - if (e instanceof CompletionException) { - e = e.getCause(); - } - if (e instanceof CannotStartProcessException) { - serverError = (CannotStartProcessException) e; - } else { - serverError = new CannotStartServerException("Error while starting language server '" + serverDefinition.id + "' (pid=" + getCurrentProcessId() + ")", e); - } - initializeFuture.completeExceptionally(serverError); - getLanguageServerLifecycleManager().onError(this, e); - stop(false); - return null; - }); - } - } - - private CompletableFuture initServer(final URI rootURI) { - - final var workspaceClientCapabilities = SupportedFeatures.getWorkspaceClientCapabilities(); - final var textDocumentClientCapabilities = SupportedFeatures.getTextDocumentClientCapabilities(); - - WindowClientCapabilities windowClientCapabilities = SupportedFeatures.getWindowClientCapabilities(); - initParams.setCapabilities(new ClientCapabilities( - workspaceClientCapabilities, - textDocumentClientCapabilities, - windowClientCapabilities, - lspStreamProvider.getExperimentalFeaturesPOJO())); - initParams.setClientInfo(getClientInfo()); - initParams.setTrace(this.lspStreamProvider.getTrace(rootURI)); - - if (initialProject != null) { - var folders = Arrays.asList(LSPIJUtils.toWorkspaceFolder(initialProject)); - initParams.setWorkspaceFolders(folders); - } - - // no then...Async future here as we want this chain of operation to be sequential and "atomic"-ish - return languageServer.initialize(initParams); - } - - private ClientInfo getClientInfo() { - ApplicationInfo applicationInfo = ApplicationInfo.getInstance(); - String versionName = applicationInfo.getVersionName(); - String buildNumber = applicationInfo.getBuild().asString(); - - String intellijVersion = versionName + " (build " + buildNumber + ")"; - return new ClientInfo(CLIENT_NAME, intellijVersion); - } - - @Nullable - private URI getRootURI() { - final Project project = this.initialProject; - if (project != null && !project.isDisposed()) { - return LSPIJUtils.toUri(project); - } - - final URI path = this.initialPath; - if (path != null) { - File projectDirectory = new File(initialPath); - if (projectDirectory.isFile()) { - projectDirectory = projectDirectory.getParentFile(); - } - return LSPIJUtils.toUri(projectDirectory); - } - 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 void logMessage(Message message, MessageConsumer consumer) { - getLanguageServerLifecycleManager().logLSPMessage(message, consumer, this); - } - - private void removeStopTimer(boolean stopping) { - if (timer != null) { - timer.cancel(); - timer = null; - if (!stopping) { - udateStatus(ServerStatus.started); - getLanguageServerLifecycleManager().onStatusChanged(this); - } - } - } - - private void udateStatus(ServerStatus serverStatus) { - this.serverStatus = serverStatus; - } - - private void startStopTimer() { - timer = new Timer("Stop Language Server Timer"); //$NON-NLS-1$ - udateStatus(ServerStatus.stopping); - getLanguageServerLifecycleManager().onStatusChanged(this); - timer.schedule(new TimerTask() { - @Override - public void run() { - try { - stop(); - } catch (Throwable t) { - //Need to catch time task exceptions, or it will cancel the timer - LOGGER.error("Failed to stop language server "+LanguageServerWrapper.this.serverDefinition.id, t); - } - } - }, TimeUnit.SECONDS.toMillis(this.serverDefinition.lastDocumentDisconnectedTimeout)); - } - - /** - * @return whether the underlying connection to language server is still active - */ - public boolean isActive() { - return this.launcherFuture != null && !this.launcherFuture.isDone() && !this.launcherFuture.isCancelled(); - } - - @Override - public void dispose() { - this.disposed = true; - stop(); - stopDispatcher(); - } - - public boolean isDisposed() { - return disposed; - } - - /** - * Returns true if the language server is stopping and false otherwise. - * - * @return true if the language server is stopping and false otherwise. - */ - public boolean isStopping() { - return this.stopping.get(); - } - - public synchronized void stop() { - final boolean alreadyStopping = this.stopping.getAndSet(true); - stop(alreadyStopping); - } - - public synchronized void stop(boolean alreadyStopping) { - try { - if (alreadyStopping) { - return; - } - udateStatus(ServerStatus.stopping); - getLanguageServerLifecycleManager().onStatusChanged(this); - - removeStopTimer(true); - if (this.languageClient != null) { - this.languageClient.dispose(); - } - - if (this.initializeFuture != null) { - this.initializeFuture.cancel(true); - this.initializeFuture = null; - } - - this.serverCapabilities = null; - this.dynamicRegistrations.clear(); - - if (isDisposed()) { - // When project is closing we shutdown everything in synch mode - shutdownAll(languageServer, lspStreamProvider, launcherFuture); - } else { - // We need to shutdown, kill and stop the process in a thread to avoid for instance - // stopping the new process created with a new start. - final Future serverFuture = this.launcherFuture; - final StreamConnectionProvider provider = this.lspStreamProvider; - final LanguageServer languageServerInstance = this.languageServer; - - Runnable shutdownKillAndStopFutureAndProvider = () -> { - shutdownAll(languageServerInstance, provider, serverFuture); - this.stopping.set(false); - udateStatus(ServerStatus.stopped); - getLanguageServerLifecycleManager().onStatusChanged(this); - }; - CompletableFuture.runAsync(shutdownKillAndStopFutureAndProvider); - } - } finally { - this.launcherFuture = null; - this.lspStreamProvider = null; - - while (!this.connectedDocuments.isEmpty()) { - disconnect(this.connectedDocuments.keySet().iterator().next(), false); - } - this.languageServer = null; - this.languageClient = null; - - if (messageBusConnection != null) { - messageBusConnection.disconnect(); - } - } - } - - private void shutdownAll(LanguageServer languageServerInstance, StreamConnectionProvider provider, Future serverFuture) { - if (languageServerInstance != null && provider != null && provider.isAlive()) { - // The LSP language server instance and the process which starts the language server is alive. Process - // - shutdown - // - exit - - // shutdown the language server - try { - shutdownLanguageServerInstance(languageServerInstance); - } catch (Exception ex) { - getLanguageServerLifecycleManager().onError(this, ex); - } - - // exit the language server - // Consume language server exit() before cancelling launcher future (serverFuture.cancel()) - // to avoid having error like "The pipe is being closed". - try { - exitLanguageServerInstance(languageServerInstance); - } catch (Exception ex) { - getLanguageServerLifecycleManager().onError(this, ex); - } - } - - if (serverFuture != null) { - serverFuture.cancel(true); - } - - if (provider != null) { - provider.stop(); - } - } - - private void shutdownLanguageServerInstance(LanguageServer languageServerInstance) throws Exception { - CompletableFuture shutdown = languageServerInstance.shutdown(); - try { - shutdown.get(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } catch (TimeoutException ex) { - String message = "Timeout error while shutdown the language server '" + serverDefinition.id + "'"; - LOGGER.warn(message, ex); - throw new Exception(message, ex); - } catch (Exception ex) { - String message = "Error while shutdown the language server '" + serverDefinition.id + "'"; - LOGGER.warn(message, ex); - throw new Exception(message, ex); - } - } - - private void exitLanguageServerInstance(LanguageServer languageServerInstance) throws Exception { - try { - languageServerInstance.exit(); - } catch (Exception ex) { - String message = "Error while exit the language server '" + serverDefinition.id + "'"; - LOGGER.error(message, ex); - throw new Exception(message, ex); - } - } - - /** - * Connect the given file to the language server. - * - * @param file the file to connect to the language server - * @return null if not connection has happened, a future tracking the connection state otherwise - * @throws IOException - */ - public CompletableFuture<@Nullable LanguageServer> connect(VirtualFile file) throws IOException { - if (file != null && file.exists()) { - return connect(LSPIJUtils.toUri(file)); - } - return CompletableFuture.completedFuture(null); - } - - /** - * 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(Project project) { - if (project != null && project.equals(this.initialProject)) { - return true; - } - - return serverDefinition.isSingleton; - } - - /** - * To make public when we support non IFiles - * - * @return null if not connection has happened, a future that completes when file is initialized otherwise - * @noreference internal so far - */ - private CompletableFuture connect(@NotNull URI fileUri) throws IOException { - removeStopTimer(false); - - if (this.connectedDocuments.containsKey(fileUri)) { - return CompletableFuture.completedFuture(languageServer); - } - start(); - if (this.initializeFuture == null) { - return CompletableFuture.completedFuture(null); - } - VirtualFile file = LSPIJUtils.findResourceFor(fileUri); - if (file == null) { - return CompletableFuture.completedFuture(null); - } - return initializeFuture.thenComposeAsync(theVoid -> { - synchronized (connectedDocuments) { - if (this.connectedDocuments.containsKey(fileUri)) { - 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(); - } - } - - Document document = LSPIJUtils.getDocument(file); - DocumentContentSynchronizer synchronizer = new DocumentContentSynchronizer(this, fileUri, document, syncKind); - document.addDocumentListener(synchronizer); - - LSPVirtualFileData data = new LSPVirtualFileData(this, file, synchronizer); - LanguageServerWrapper.this.connectedDocuments.put(fileUri, data); - - return synchronizer.didOpenFuture; - } - }).thenApply(theVoid -> languageServer); - } - - private void disconnect(URI path) { - disconnect(path, true); - } - - private void disconnect(URI path, boolean stopIfNoOpenedFiles) { - LSPVirtualFileData data = this.connectedDocuments.remove(path); - if (data != null) { - // Remove the listener from the old document stored in synchronizer - DocumentContentSynchronizer synchronizer = data.getSynchronizer(); - synchronizer.getDocument().removeDocumentListener(synchronizer); - synchronizer.documentClosed(); - } - if (stopIfNoOpenedFiles && this.connectedDocuments.isEmpty()) { - if (this.serverDefinition.lastDocumentDisconnectedTimeout != 0 && !ApplicationManager.getApplication().isUnitTestMode()) { - removeStopTimer(true); - startStopTimer(); - } else { - stop(); - } - } - } - - /** - * 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); - } - - public @Nullable LSPVirtualFileData getLSPVirtualFileData(URI fileUri) { - return connectedDocuments.get(fileUri); - } - - /** - * 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 languagServerFuture = getInitializedServer(); - if (ApplicationManager.getApplication().isDispatchThread()) { // UI Thread - return this.languageServer; - } else { - return languagServerFuture.join(); - } - } - - /** - * Starts the language server and returns a CompletableFuture waiting for the - * server to be initialized. If done in the UI stream, a job will be created - * displaying that the server is being initialized - */ - @NotNull - public CompletableFuture getInitializedServer() { - try { - start(); - } catch (LanguageServerException ex) { - // The language server cannot be started, return a null language server - return CompletableFuture.completedFuture(null); - } - if (initializeFuture != null && !this.initializeFuture.isDone()) { - /*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); - } - - /** - * Sends a notification to the wrapped language server - * - * @param fn LS notification to send - */ - public void sendNotification(@NotNull Consumer fn) { - // Enqueues a notification on the dispatch thread associated with the wrapped language server. This - // ensures the interleaving of document updates and other requests in the UI is mirrored in the - // order in which they get dispatched to the server - getInitializedServer().thenAcceptAsync(fn, this.dispatcher); - } - - /** - * 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; - } - - public void registerCapability(RegistrationParams params) { - 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(@NotNull Registration reg, @NotNull 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$ - } - } - - public 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) { - URI uri = LSPIJUtils.toUri(file); - if (uri != null) { - LSPVirtualFileData data = connectedDocuments.get(LSPIJUtils.toUri(file)); - if (data != null) { - var synchronizer = data.getSynchronizer(); - if (synchronizer != null) { - return synchronizer.getVersion(); - } - } - } - } - return -1; - } - - public boolean canOperate(@NotNull VirtualFile file) { - if (this.isConnectedTo(LSPIJUtils.toUri(file))) { - return true; - } - if (this.initialProject == null && this.connectedDocuments.isEmpty()) { - return true; - } - if (file != null && file.exists()) { - return true; - } - return serverDefinition.isSingleton; - } - - private LanguageServerLifecycleManager getLanguageServerLifecycleManager() { - Project project = initialProject; - if (project == null || project.isDisposed()) { - return NullLanguageServerLifecycleManager.INSTANCE; - } - return LanguageServerLifecycleManager.getInstance(project); - } - - /** - * Returns the parent process id (process id of Intellij). - * - * @return the parent process id (process id of Intellij). - */ - private static int getParentProcessId() { - return (int) ProcessHandle.current().pid(); - } - - // ------------------ Current Process information. - - /** - * Returns the current process id and null otherwise. - * - * @return the current process id and null otherwise. - */ - public Long getCurrentProcessId() { - return currentProcessId; - } - - public List getCurrentProcessCommandLine() { - return currentProcessCommandLines; - } - - // ------------------ Server status information . - - /** - * Returns the server status. - * - * @return the server status. - */ - public ServerStatus getServerStatus() { - return serverStatus; - } - - public LanguageServerException getServerError() { - return serverError; - } - - public int getNumberOfRestartAttempts() { - return numberOfRestartAttempts; - } - - public int getMaxNumberOfRestartAttempts() { - return MAX_NUMBER_OF_RESTART_ATTEMPTS; - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java deleted file mode 100644 index 7879b2b4e..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServersRegistry.java +++ /dev/null @@ -1,294 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.lightEdit.LightEdit; -import com.intellij.lang.Language; -import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.client.LanguageClientImpl; -import com.redhat.devtools.intellij.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -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 javax.swing.*; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -/** - * Language server registry. - */ -public class LanguageServersRegistry { - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServersRegistry.class); - - public abstract static class LanguageServerDefinition { - - enum Scope { - project, application - } - private static final int DEFAULT_LAST_DOCUMENTED_DISCONNECTED_TIMEOUT = 5; - - public final @Nonnull - String id; - public final @Nonnull - String label; - public final boolean isSingleton; - public final @Nonnull - Map languageIdMappings; - public final String description; - public final int lastDocumentDisconnectedTimeout; - private boolean enabled; - - public final boolean supportsLightEdit; - - final @Nonnull Scope scope; - - public LanguageServerDefinition(@Nonnull String id, @Nonnull String label, String description, boolean isSingleton, Integer lastDocumentDisconnectedTimeout, String scope, boolean supportsLightEdit) { - this.id = id; - this.label = label; - this.description = description; - this.isSingleton = isSingleton; - this.lastDocumentDisconnectedTimeout = lastDocumentDisconnectedTimeout != null && lastDocumentDisconnectedTimeout > 0 ? lastDocumentDisconnectedTimeout : DEFAULT_LAST_DOCUMENTED_DISCONNECTED_TIMEOUT; - this.languageIdMappings = new ConcurrentHashMap<>(); - this.scope = scope == null || scope.isBlank()? Scope.application : Scope.valueOf(scope); - this.supportsLightEdit = supportsLightEdit; - setEnabled(true); - } - - - /** - * Returns true if the language server definition is enabled and false otherwise. - * - * @return true if the language server definition is enabled and false otherwise. - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Set enabled the language server definition. - * - * @param enabled enabled the language server definition. - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public void registerAssociation(@Nonnull Language language, @Nonnull String languageId) { - this.languageIdMappings.put(language, languageId); - } - - @Nonnull - public String getDisplayName() { - return label != null ? label : id; - } - - public abstract StreamConnectionProvider createConnectionProvider(Project project); - - public LanguageClientImpl createLanguageClient(Project project) { - return new LanguageClientImpl(project); - } - - public Class getServerInterface() { - return LanguageServer.class; - } - - public Launcher.Builder createLauncherBuilder() { - return new Launcher.Builder<>(); - } - - public boolean supportsCurrentEditMode(@NotNull Project project) { - return project != null && (supportsLightEdit || !LightEdit.owns(project)); - } - } - - static class ExtensionLanguageServerDefinition extends LanguageServerDefinition { - private final ServerExtensionPointBean extension; - - public ExtensionLanguageServerDefinition(ServerExtensionPointBean element) { - super(element.id, element.label, element.description, element.singleton, element.lastDocumentDisconnectedTimeout, element.scope, element.supportsLightEdit); - this.extension = element; - } - - @Override - public StreamConnectionProvider createConnectionProvider(Project project) { - String serverImpl = extension.getImplementationClassName(); - if (serverImpl == null || serverImpl.isEmpty()) { - throw new RuntimeException( - "Exception occurred while creating an instance of the stream connection provider, you have to define server/@class attribute in the extension point."); //$NON-NLS-1$ - } - try { - if (Scope.project == scope) { - return (StreamConnectionProvider) project.instantiateClassWithConstructorInjection(extension.getServerImpl(), project, - extension.getPluginDescriptor().getPluginId()); - } - return (StreamConnectionProvider) project.instantiateClass(extension.getServerImpl(), extension.getPluginDescriptor().getPluginId()); - } 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 final List connections = new ArrayList<>(); - - private Map serverIcons = new HashMap<>(); - - private LanguageServersRegistry() { - initialize(); - } - - private void initialize() { - 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.getDocumentMatcher())); - } - } - - for (ServerIconProviderExtensionPointBean extension : ServerIconProviderExtensionPointBean.EP_NAME.getExtensions()) { - serverIcons.put(extension.serverId, new LanguageServerIconProviderDefinition(extension)); - } - - for (LanguageMapping mapping : languageMappings) { - LanguageServerDefinition lsDefinition = servers.get(mapping.languageId); - if (lsDefinition != null) { - registerAssociation(lsDefinition, mapping); - } else { - LOGGER.warn("server '" + mapping.id + "' not available"); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - } - - public Icon getServerIcon(String serverId) { - LanguageServerIconProviderDefinition iconProvider = serverIcons.get(serverId); - Icon icon = iconProvider != null ? iconProvider.getIcon() : null; - return icon != null ? icon : AllIcons.Webreferences.Server; - } - - /** - * @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 @NotNull Language contentType) { - return connections.stream() - .filter(entry -> contentType.isKindOf(entry.getKey())) - .collect(Collectors.toList()); - } - - - public void registerAssociation(@NotNull LanguageServerDefinition serverDefinition, @Nullable LanguageMapping mapping) { - @NotNull Language language = mapping.language; - @Nullable String languageId = mapping.languageId; - if (languageId != null) { - serverDefinition.registerAssociation(language, languageId); - } - - connections.add(new ContentTypeToLanguageServerDefinition(language, serverDefinition, mapping.getDocumentMatcher())); - } - - 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 { - - @NotNull - public final String id; - @NotNull - public final Language language; - @Nullable - public final String languageId; - - private final DocumentMatcher documentMatcher; - - public LanguageMapping(@NotNull Language language, @Nullable String id, @Nullable String languageId, @NotNull DocumentMatcher documentMatcher) { - this.language = language; - this.id = id; - this.languageId = languageId; - this.documentMatcher = documentMatcher; - } - - public DocumentMatcher getDocumentMatcher() { - return documentMatcher; - } - } - - public Set getAllDefinitions() { - return connections - .stream() - .map(AbstractMap.SimpleEntry::getValue) - .collect(Collectors.toSet()); - } - -} - diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java deleted file mode 100644 index e5102782e..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java +++ /dev/null @@ -1,425 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is made available under the terms of the - * Eclipse Public License v2.0 which accompanies this distribution, - * and is available at https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.lang.Language; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.server.StreamConnectionProvider; -import org.eclipse.lsp4j.ServerCapabilities; -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.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * Language server accessor. - */ -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 final Set startedServers = new HashSet<>(); - private Map providersToLSDefinitions = new HashMap<>(); - - @NotNull - public CompletableFuture> getLanguageServers(@NotNull VirtualFile file, - Predicate filter) { - URI uri = LSPIJUtils.toUri(file); - if (uri == null) { - return CompletableFuture.completedFuture(Collections.emptyList()); - } - - // Collect started (or not) language servers which matches the given file. - CompletableFuture> matchedServers = getMatchedLanguageServersWrappers(file); - if (matchedServers.isDone() && matchedServers.getNow(Collections.emptyList()).isEmpty()) { - // None language servers matches the given file - return CompletableFuture.completedFuture(Collections.emptyList()); - } - - // Returns the language servers which match the given file, start them and connect the file to each matched language server - final List servers = Collections.synchronizedList(new ArrayList<>()); - try { - return matchedServers - .thenComposeAsync(result -> CompletableFuture.allOf(result - .stream() - .map(wrapper -> - wrapper.getInitializedServer() - .thenComposeAsync(server -> { - if (server != null && wrapper.isEnabled() && (filter == null || filter.test(wrapper.getServerCapabilities()))) { - try { - return wrapper.connect(file); - } catch (IOException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - } - return CompletableFuture.completedFuture(null); - }).thenAccept(server -> { - if (server != null) { - servers.add(new LanguageServerItem(server, wrapper)); - } - })).toArray(CompletableFuture[]::new))) - .thenApply(theVoid -> servers); - } catch (final ProcessCanceledException cancellation) { - throw cancellation; - } catch (final Exception e) { - LOGGER.warn(e.getLocalizedMessage(), e); - return CompletableFuture.completedFuture(Collections.emptyList()); - } - } - - /** - * Return the started servers. - * - * @return the started servers. - */ - public Set getStartedServers() { - return startedServers; - } - - public void projectClosing(Project project) { - // On project closing, we dispose all language servers - startedServers.forEach(ls -> { - if (project.equals(ls.getProject())) { - ls.dispose(); - } - }); - } - - /** - * Get the requested language server instance for the given file. Starts the - * language server if not already started. - * - * @param file the file for which the initialized LanguageServer shall be returned - * @param lsDefinition the language server definition - * @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. If - * {@code capabilitesPredicate} does not test positive for the server's - * capabilities, {@code null} is returned. - */ - public CompletableFuture getInitializedLanguageServer(VirtualFile file, - LanguageServersRegistry.LanguageServerDefinition lsDefinition, Predicate capabilitiesPredicate) - throws IOException { - URI initialPath = LSPIJUtils.toUri(file); - LanguageServerWrapper wrapper = getLSWrapperForConnection(file, lsDefinition, initialPath); - if (wrapper != null && capabilitiesComply(wrapper, capabilitiesPredicate)) { - wrapper.connect(file); - 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()); - } - - @NotNull - private CompletableFuture> getMatchedLanguageServersWrappers(@NotNull VirtualFile file) { - MatchedLanguageServerDefinitions mappings = getMatchedLanguageServerDefinitions(file, project); - if (mappings == MatchedLanguageServerDefinitions.NO_MATCH) { - // There are no mapping for the given file - return CompletableFuture.completedFuture(Collections.emptyList()); - } - - LinkedHashSet matchedServers = new LinkedHashSet<>(); - - // Collect sync server definitions - var serverDefinitions = mappings.getMatched(); - collectLanguageServersFromDefinition(file, project, serverDefinitions, matchedServers); - - CompletableFuture> async = mappings.getAsyncMatched(); - if (async != null) { - // Collect async server definitions - return async - .thenApply(asyncServerDefinitions -> { - collectLanguageServersFromDefinition(file, project, asyncServerDefinitions, matchedServers); - return matchedServers; - }); - } - return CompletableFuture.completedFuture(matchedServers); - } - - /** - * Get or create a language server wrapper for the given server definitions and add then to the given matched servers. - * - * @param file the file. - * @param fileProject the file project. - * @param serverDefinitions the server definitions. - * @param matchedServers the list to update with get/created language server. - */ - private void collectLanguageServersFromDefinition(@NotNull VirtualFile file, @NotNull Project fileProject, @NotNull Set serverDefinitions, @NotNull Set matchedServers) { - synchronized (startedServers) { - for (var serverDefinition : serverDefinitions) { - boolean useExistingServer = false; - // Loop for started language servers - for (var startedServer : startedServers) { - if (startedServer.serverDefinition.equals(serverDefinition) - && startedServer.canOperate(file)) { - // A started language server match the file, use it - matchedServers.add(startedServer); - useExistingServer = true; - break; - } - } - if (!useExistingServer) { - // There are none started servers which matches the file, create and add it. - LanguageServerWrapper wrapper = new LanguageServerWrapper(fileProject, serverDefinition); - startedServers.add(wrapper); - matchedServers.add(wrapper); - } - } - } - } - - /** - * Store the matched language server definitions for a given file. - */ - private static class MatchedLanguageServerDefinitions { - - public static final MatchedLanguageServerDefinitions NO_MATCH = new MatchedLanguageServerDefinitions(Collections.emptySet(), null); - - private final Set matched; - - private final CompletableFuture> asyncMatched; - - public MatchedLanguageServerDefinitions(@NotNull Set matchedLanguageServersDefinition, CompletableFuture> async) { - this.matched = matchedLanguageServersDefinition; - this.asyncMatched = async; - } - - /** - * Return the matched server definitions get synchronously. - * - * @return the matched server definitions get synchronously. - */ - public @NotNull Set getMatched() { - return matched; - } - - /** - * Return the matched server definitions get asynchronously or null otherwise. - * - * @return the matched server definitions get asynchronously or null otherwise. - */ - public CompletableFuture> getAsyncMatched() { - return asyncMatched; - } - } - - /** - * Returns the matched language server definitions for the given file. - * - * @param file the file. - * @param fileProject the file project. - * @return the matched language server definitions for the given file. - */ - private MatchedLanguageServerDefinitions getMatchedLanguageServerDefinitions(@NotNull VirtualFile file, @NotNull Project fileProject) { - - Set syncMatchedDefinitions = null; - Set asyncMatchedDefinitions = null; - - // look for running language servers via content-type - Queue contentTypes = new LinkedList<>(); - Set processedContentTypes = new HashSet<>(); - contentTypes.add(LSPIJUtils.getFileLanguage(file, project)); - - while (!contentTypes.isEmpty()) { - Language contentType = contentTypes.poll(); - if (contentType == null || processedContentTypes.contains(contentType)) { - continue; - } - // Loop for server/language mapping - for (ContentTypeToLanguageServerDefinition mapping : LanguageServersRegistry.getInstance() - .findProviderFor(contentType)) { - if (mapping == null || !mapping.isEnabled() || (syncMatchedDefinitions != null && syncMatchedDefinitions.contains(mapping.getValue()))) { - // the mapping is disabled - // or the server definition has been already added - continue; - } - if (mapping.shouldBeMatchedAsynchronously(fileProject)) { - // Async mapping - // Mapping must be done asynchronously because the match of DocumentMatcher of the mapping need to be done asynchronously - // This usecase comes from for instance when custom match need to collect classes from the Java project and requires read only action. - if (asyncMatchedDefinitions == null) { - asyncMatchedDefinitions = new HashSet<>(); - } - asyncMatchedDefinitions.add(mapping); - } else { - // Sync mapping - if (match(file, fileProject, mapping)) { - if (syncMatchedDefinitions == null) { - syncMatchedDefinitions = new HashSet<>(); - } - syncMatchedDefinitions.add(mapping.getValue()); - } - } - } - } - if (syncMatchedDefinitions != null || asyncMatchedDefinitions != null) { - // Some match... - CompletableFuture> async = null; - if (asyncMatchedDefinitions != null) { - // Async match, compute a future which process all matchAsync and return a list of server definitions - final Set serverDefinitions = Collections.synchronizedSet(new HashSet<>()); - async = CompletableFuture.allOf(asyncMatchedDefinitions - .stream() - .map(mapping -> { - return mapping - .matchAsync(file, fileProject) - .thenApply(result -> { - if (result) { - serverDefinitions.add(mapping.getValue()); - } - return null; - }); - } - ) - .toArray(CompletableFuture[]::new)) - .thenApply(theVoid -> serverDefinitions); - } - return new MatchedLanguageServerDefinitions(syncMatchedDefinitions != null ? syncMatchedDefinitions : Collections.emptySet(), async); - } - // No match... - return MatchedLanguageServerDefinitions.NO_MATCH; - } - - private static boolean match(VirtualFile file, Project fileProject, ContentTypeToLanguageServerDefinition mapping) { - if (!ApplicationManager.getApplication().isReadAccessAllowed()) { - return ReadAction.compute(() -> mapping.match(file, fileProject)); - } - return mapping.match(file, fileProject); - } - - private LanguageServerWrapper getLSWrapperForConnection(VirtualFile file, - LanguageServersRegistry.LanguageServerDefinition serverDefinition, URI initialPath) throws IOException { - if (!serverDefinition.isEnabled()) { - // don't return a language server wrapper for the given server definition - return null; - } - LanguageServerWrapper wrapper = null; - - synchronized (startedServers) { - for (LanguageServerWrapper startedWrapper : getStartedLSWrappers(file)) { - if (startedWrapper.serverDefinition.equals(serverDefinition)) { - wrapper = startedWrapper; - break; - } - } - if (wrapper == null) { - wrapper = new LanguageServerWrapper(serverDefinition, initialPath); - wrapper.start(); - } - - startedServers.add(wrapper); - } - return wrapper; - } - - private List getStartedLSWrappers( - VirtualFile file) { - return getStartedLSWrappers(wrapper -> wrapper.canOperate(file)); - } - - private List getStartedLSWrappers(Predicate predicate) { - return startedServers.stream().filter(predicate) - .collect(Collectors.toList()); - // TODO multi-root: also return servers which support multi-root? - } - - /** - * 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 - */ - @NotNull - public List getActiveLanguageServers(Predicate request) { - return getLanguageServers(null, request, true); - } - - /** - * 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 - */ - @NotNull - public List getLanguageServers(@Nullable Project 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; - } - - 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/com/redhat/devtools/intellij/lsp4ij/ServerExtensionPointBean.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerExtensionPointBean.java deleted file mode 100644 index dc6834ae6..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerExtensionPointBean.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.serviceContainer.BaseKeyedLazyInstance; -import com.intellij.util.xmlb.annotations.Attribute; -import com.intellij.util.xmlb.annotations.Tag; -import com.redhat.devtools.intellij.lsp4ij.server.StreamConnectionProvider; -import org.jetbrains.annotations.Nullable; - -public class ServerExtensionPointBean extends BaseKeyedLazyInstance { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.server"); - - @Attribute("id") - public String id; - - @Attribute("label") - public String label; - - @Tag("description") - public String description; - - @Attribute("class") - public String serverImpl; - private Class serverImplClass; - - @Attribute("clientImpl") - public String clientImpl; - private Class clientClass; - - @Attribute("serverInterface") - public String serverInterface; - private Class serverClass; - - /** - * Valid values are project and application
- * When project scope is selected, the implementation of {@link StreamConnectionProvider} requires a - * constructor with a single {@link com.intellij.openapi.project.Project} parameter - */ - @Attribute("scope") - public String scope; - - @Attribute("singleton") - public boolean singleton; - - @Attribute("supportsLightEdit") - public boolean supportsLightEdit; - - @Attribute("lastDocumentDisconnectedTimeout") - public Integer lastDocumentDisconnectedTimeout; - - public Class getClientImpl() throws ClassNotFoundException { - if (clientClass == null) { - clientClass = getPluginDescriptor().getPluginClassLoader().loadClass(clientImpl); - } - return clientClass; - } - - public Class getServerImpl() throws ClassNotFoundException { - if (serverImplClass == null) { - serverImplClass = getPluginDescriptor().getPluginClassLoader().loadClass(serverImpl); - } - return serverImplClass; - } - - public Class getServerInterface() throws ClassNotFoundException { - if (serverClass == null) { - serverClass = getPluginDescriptor().getPluginClassLoader().loadClass(serverInterface); - } - return serverClass; - } - - @Override - protected @Nullable String getImplementationClassName() { - return serverImpl; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProvider.java deleted file mode 100644 index e41b394e0..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij; - -import javax.swing.*; - -public interface ServerIconProvider { - - Icon getIcon(); -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProviderExtensionPointBean.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProviderExtensionPointBean.java deleted file mode 100644 index 46476e799..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerIconProviderExtensionPointBean.java +++ /dev/null @@ -1,39 +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 com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.serviceContainer.BaseKeyedLazyInstance; -import com.intellij.util.xmlb.annotations.Attribute; -import org.jetbrains.annotations.Nullable; - -/** - * Server icon provider extension. - */ -public class ServerIconProviderExtensionPointBean extends BaseKeyedLazyInstance { - - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.serverIconProvider"); - - @Attribute("serverId") - public String serverId; - - @Attribute("class") - public String clazz; - - @Override - protected @Nullable String getImplementationClassName() { - return clazz; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerMessageHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerMessageHandler.java deleted file mode 100644 index 2aa0fd81f..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerMessageHandler.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.redhat.devtools.intellij.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 = switch (type) { - case Error -> NotificationType.ERROR; - case Info, Log -> NotificationType.INFORMATION; - case Warning -> NotificationType.WARNING; - }; - return result; - } - - - public static void showMessage(String title, MessageParams params) { - Notification notification = new Notification("Language Server Protocol", title, params.getMessage(), messageTypeToNotificationType(params.getType())); - notification.setIcon(messageTypeToIcon(params.getType())); - 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/com/redhat/devtools/intellij/lsp4ij/ServerStatus.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerStatus.java deleted file mode 100644 index 6851f4f61..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ServerStatus.java +++ /dev/null @@ -1,27 +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 com.redhat.devtools.intellij.lsp4ij; - -/** - * Language server status. - */ -public enum ServerStatus { - - none, // initial status - starting, // The language server process is starting - started, // The language server is started without error - stopping, // The language server is stopping - stopped // The language server is stopped with or without error; - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/CoalesceByKey.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/CoalesceByKey.java deleted file mode 100644 index 3409331db..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/CoalesceByKey.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij.client; - -import java.util.Arrays; -import java.util.Objects; - -/** - * A CoalesceBy key which defines a type (like LSP request) and a value which should be identified the request. - */ -public class CoalesceByKey { - - private final String type; - - private final Object[] value; - - public CoalesceByKey(String type, Object... value) { - this.type = type; - this.value = value; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CoalesceByKey that = (CoalesceByKey) o; - return Objects.equals(type, that.type) && Arrays.equals(value, that.value); - } - - @Override - public int hashCode() { - int result = Objects.hash(type); - result = 31 * result + Arrays.hashCode(value); - return result; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/ExecutionAttemptLimitReachedException.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/ExecutionAttemptLimitReachedException.java deleted file mode 100644 index 2d506fb8a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/ExecutionAttemptLimitReachedException.java +++ /dev/null @@ -1,24 +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 com.redhat.devtools.intellij.lsp4ij.client; - - -/** - * Exception thrown when the restart of non blocking read action for a given service is reached. - */ -public class ExecutionAttemptLimitReachedException extends RuntimeException { - public ExecutionAttemptLimitReachedException(String executionName, int limit, Throwable ex) { - super("Execution attempt limit (" + limit + ") reached to execute '" + executionName + ".", ex); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/IndexAwareLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/IndexAwareLanguageClient.java deleted file mode 100644 index 2edfab37c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/IndexAwareLanguageClient.java +++ /dev/null @@ -1,77 +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 https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.client; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -public class IndexAwareLanguageClient extends LanguageClientImpl { - private static final Logger LOGGER = LoggerFactory.getLogger(IndexAwareLanguageClient.class); - - public IndexAwareLanguageClient(Project project) { - super(project); - } - - /** - * Run the given function as a background task, wrapped in a read action - * - * @param progressTitle the progress title of the action being run - * @param code the function code to execute in the background - * @param the return type - * @return the output of the function code - */ - protected CompletableFuture runAsBackground(String progressTitle, Function code) { - return runAsBackground(progressTitle, code, null); - } - - /** - * Run the given function as a background task, wrapped in a read action - * - * @param progressTitle the progress title of the action being run - * @param code the function code to execute in the background - * @param the return type - * @return the output of the function code - */ - protected CompletableFuture runAsBackground(String progressTitle, Function code, Object coalesceBy) { - return new LSPCompletableFuture<>(code, progressTitle, IndexAwareLanguageClient.this, coalesceBy); - } - - /** - * This method returns the file path to display in the progress bar. - * - * @param fileUri the file uri. - * @return the file path to display in the progress bar. - */ - protected String getFilePath(String fileUri) { - VirtualFile file = LSPIJUtils.findResourceFor(fileUri); - if (file != null) { - Module module = LSPIJUtils.getModule(file, getProject()); - if (module != null) { - ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - VirtualFile[] contentRoots = rootManager.getContentRoots(); - if (contentRoots.length > 0) { - return VfsUtil.findRelativePath(contentRoots[0], file, '/'); - } - } - } - return fileUri; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPCompletableFuture.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPCompletableFuture.java deleted file mode 100644 index fc27c091f..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPCompletableFuture.java +++ /dev/null @@ -1,38 +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 com.redhat.devtools.intellij.lsp4ij.client; - -import com.intellij.openapi.progress.ProgressIndicator; -import com.redhat.devtools.intellij.lsp4ij.internal.PromiseToCompletableFuture; - -import java.util.function.Function; - -/** - * LSP completable future which execute a given function code in a non blocking reading action promise. - */ -public class LSPCompletableFuture extends PromiseToCompletableFuture { - private final IndexAwareLanguageClient languageClient; - - public LSPCompletableFuture(Function code, String progressTitle, IndexAwareLanguageClient languageClient, Object coalesceBy) { - super(code, progressTitle, languageClient.getProject(), languageClient, coalesceBy); - this.languageClient = languageClient; - init(); - } - - @Override - protected ProgressIndicator createProgressIndicator() { - return new LSPProgressIndicator(languageClient); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPProgressIndicator.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPProgressIndicator.java deleted file mode 100644 index 6fbb19a8c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LSPProgressIndicator.java +++ /dev/null @@ -1,34 +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 com.redhat.devtools.intellij.lsp4ij.client; - -import com.intellij.openapi.progress.StandardProgressIndicator; -import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase; -import com.redhat.devtools.intellij.lsp4ij.client.LanguageClientImpl; - -/** - * LSP progress indicator which check that language client is not stopped. - */ -class LSPProgressIndicator extends AbstractProgressIndicatorBase implements StandardProgressIndicator { - - private final LanguageClientImpl languageClient; - - public LSPProgressIndicator(LanguageClientImpl languageClient) { - this.languageClient = languageClient; - } - @Override - public final boolean isCanceled() { - return super.isCanceled() || languageClient.isDisposed(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java deleted file mode 100644 index 327faff3c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/client/LanguageClientImpl.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij.client; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.ServerMessageHandler; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.LSPDiagnosticHandler; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4j.services.LanguageServer; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; - -public class LanguageClientImpl implements LanguageClient, Disposable { - private final Project project; - private Consumer diagnosticHandler; - - private LanguageServer server; - private LanguageServerWrapper wrapper; - - private boolean disposed; - - private Runnable didChangeConfigurationListener; - - public LanguageClientImpl(Project project) { - this.project = project; - Disposer.register(project, this); - } - - 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) { - 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() { - Project project = wrapper.getProject(); - if (project != null) { - List folders = Arrays.asList(LSPIJUtils.toWorkspaceFolder(project)); - return CompletableFuture.completedFuture(folders); - } - return CompletableFuture.completedFuture(Collections.emptyList()); - } - - @Override - public void dispose() { - this.disposed = true; - } - - public boolean isDisposed() { - return disposed || getProject().isDisposed(); - } - - protected Object createSettings() { - return null; - } - - protected synchronized Runnable getDidChangeConfigurationListener() { - if (didChangeConfigurationListener != null) { - return didChangeConfigurationListener; - } - didChangeConfigurationListener = this::triggerChangeConfiguration; - return didChangeConfigurationListener; - } - - protected void triggerChangeConfiguration() { - LanguageServer languageServer = getLanguageServer(); - if (languageServer == null) { - return; - } - Object settings = createSettings(); - if (settings == null) { - return; - } - DidChangeConfigurationParams params = new DidChangeConfigurationParams(settings); - languageServer.getWorkspaceService().didChangeConfiguration(params); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/commands/CommandExecutor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/commands/CommandExecutor.java deleted file mode 100644 index 2de873fa3..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/commands/CommandExecutor.java +++ /dev/null @@ -1,260 +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 com.redhat.devtools.intellij.lsp4ij.commands; - -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.*; -import com.intellij.openapi.actionSystem.ex.ActionUtil; -import com.intellij.openapi.actionSystem.impl.SimpleDataContext; -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 com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import com.redhat.devtools.intellij.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.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); - - public static final DataKey LSP_COMMAND = DataKey.create("com.redhat.devtools.intellij.quarkus.lsp4ij.command"); - - public static final DataKey LSP_COMMAND_DOCUMENT_URI = DataKey.create("com.redhat.devtools.intellij.quarkus.lsp4ij.command.documentUri"); - - /** - * Will execute the given {@code command} either on a language server, - * supporting the command, or on the client, if an {@link AnAction} is - * registered for the ID of the command. 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 documentUri - * the URI of 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, URI documentUri, - String languageServerId) { - if (command == null) { - return; - } - if (executeCommandServerSide(project, command, documentUri, languageServerId)) { - return; - } - if (executeCommandClientSide(project, command, documentUri)) { - return; - } - // tentative fallback - if (documentUri != null && command.getArguments() != null) { - Document document = LSPIJUtils.getDocument(documentUri); - if (document != null) { - WorkspaceEdit edit = createWorkspaceEdit(command.getArguments(), document); - LSPIJUtils.applyWorkspaceEdit(edit); - } - } - } - - private static boolean executeCommandServerSide(Project project, Command command, - URI documentUri, String languageServerId) { - if (languageServerId == null) { - return false; - } - LanguageServersRegistry.LanguageServerDefinition languageServerDefinition = LanguageServersRegistry.getInstance() - .getDefinition(languageServerId); - if (languageServerDefinition == null) { - return false; - } - - try { - CompletableFuture languageServerFuture = getLanguageServerForCommand(project, command, documentUri, - 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, - URI documentUri, LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) throws IOException { - VirtualFile file = LSPIJUtils.findResourceFor(documentUri); - if (file == null) { - return null; - } - return LanguageServiceAccessor.getInstance(project) - //TODO pass documentUri instead of document, but looks like that implies non-trivial refactoring - .getInitializedLanguageServer(file, languageServerDefinition, serverCapabilities -> { - ExecuteCommandOptions provider = serverCapabilities.getExecuteCommandProvider(); - return provider != null && provider.getCommands().contains(command.getCommand()); - }); - } - - private static boolean executeCommandClientSide(Project project, Command command, URI documentUri) { - Application workbench = ApplicationManager.getApplication(); - if (workbench == null) { - return false; - } - AnAction parameterizedCommand = createIDEACoreCommand(command); - if (parameterizedCommand == null) { - return false; - } - DataContext dataContext = createDataContext(project, command, documentUri); - ActionUtil.invokeAction(parameterizedCommand, dataContext, ActionPlaces.UNKNOWN, null, null); - return true; - } - - private static AnAction createIDEACoreCommand(Command command) { - // 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(Project project, Command command, URI documentUri) { - - SimpleDataContext.Builder contextBuilder = SimpleDataContext.builder(); - contextBuilder.add(CommonDataKeys.PROJECT, project) - .add(LSP_COMMAND, command) - .add(LSP_COMMAND_DOCUMENT_URI, documentUri); - return contextBuilder.build(); - } - - // 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, @NotNull 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/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowFactory.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowFactory.java deleted file mode 100644 index 214855e13..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowFactory.java +++ /dev/null @@ -1,41 +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 com.redhat.devtools.intellij.lsp4ij.console; - -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowFactory; -import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentManager; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import org.jetbrains.annotations.NotNull; - -/** - * Language server console factory. - * - * @author Angelo ZERR - */ -public class LSPConsoleToolWindowFactory implements ToolWindowFactory, DumbAware { - - @Override - public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { - LSPConsoleToolWindowPanel consoleWindow = new LSPConsoleToolWindowPanel(project); - ContentManager contentManager = toolWindow.getContentManager(); - Content content = contentManager.getFactory().createContent(consoleWindow, - LanguageServerBundle.message("lsp.console.title"), false); - content.setDisposer(consoleWindow); - contentManager.addContent(content); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowPanel.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowPanel.java deleted file mode 100644 index ee0ed51f3..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleToolWindowPanel.java +++ /dev/null @@ -1,328 +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 com.redhat.devtools.intellij.lsp4ij.console; - -import com.intellij.execution.ui.ConsoleView; -import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionToolbar; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.ComboBox; -import com.intellij.openapi.ui.OnePixelDivider; -import com.intellij.openapi.ui.SimpleToolWindowPanel; -import com.intellij.openapi.util.Disposer; -import com.intellij.ui.CardLayoutPanel; -import com.intellij.ui.OnePixelSplitter; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.util.ui.FormBuilder; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UI; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerExplorer; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerProcessTreeNode; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerTreeNode; -import com.redhat.devtools.intellij.lsp4ij.settings.ServerTrace; -import com.redhat.devtools.intellij.lsp4ij.settings.UserDefinedLanguageServerSettings; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import java.awt.*; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.HashSet; -import java.util.Set; - -/** - * LSP consoles - */ -public class LSPConsoleToolWindowPanel extends SimpleToolWindowPanel implements Disposable { - - private final Project project; - - private LanguageServerExplorer explorer; - - private ConsolesPanel consoles; - private boolean disposed; - - public LSPConsoleToolWindowPanel(Project project) { - super(false, true); - this.project = project; - createUI(); - } - - private void createUI() { - explorer = new LanguageServerExplorer(this); - var scrollPane = new JBScrollPane(explorer); - this.consoles = new ConsolesPanel(); - var splitPane = createSplitPanel(scrollPane, consoles); - super.setContent(splitPane); - super.revalidate(); - super.repaint(); - explorer.load(); - } - - public Project getProject() { - return project; - } - - private static JComponent createSplitPanel(JComponent left, JComponent right) { - OnePixelSplitter splitter = new OnePixelSplitter(false, 0.15f); - splitter.setShowDividerControls(true); - splitter.setHonorComponentsMinimumSize(true); - splitter.setFirstComponent(left); - splitter.setSecondComponent(right); - return splitter; - } - - public void selectDetail(LanguageServerTreeNode treeNode) { - if (consoles == null || isDisposed()) { - return; - } - consoles.select(treeNode, true); - } - - public void selectConsole(LanguageServerProcessTreeNode processTreeNode) { - if (consoles == null || isDisposed()) { - return; - } - consoles.select(processTreeNode, true); - } - - /** - * A card-panel that displays panels for each language server instances. - */ - private class ConsolesPanel extends CardLayoutPanel { - - @Override - protected DefaultMutableTreeNode prepare(DefaultMutableTreeNode key) { - return key; - } - - @Override - protected ConsoleContentPanel create(DefaultMutableTreeNode key) { - if (isDisposed() || LSPConsoleToolWindowPanel.this.isDisposed()) { - return null; - } - return new ConsoleContentPanel(key); - } - - @Override - public void dispose() { - removeAll(); - } - - @Override - protected void dispose(DefaultMutableTreeNode key, ConsoleContentPanel value) { - if (value != null) { - value.dispose(); - } - } - } - - private class ConsoleContentPanel extends SimpleCardLayoutPanel { - - private static final String NAME_VIEW_CONSOLE = "console"; - - private static final String NAME_VIEW_DETAIL = "detail"; - - private ConsoleView consoleView; - - private final Set settingsChangeListeners = new HashSet<>(); - - public ConsoleContentPanel(DefaultMutableTreeNode key) { - if (key instanceof LanguageServerTreeNode) { - add(createDetailPanel((LanguageServerTreeNode) key), NAME_VIEW_DETAIL); - showDetail(); - } else if (key instanceof LanguageServerProcessTreeNode) { - consoleView = createConsoleView(((LanguageServerProcessTreeNode) key).getLanguageServer().serverDefinition, project); - JComponent consoleComponent = consoleView.getComponent(); - Disposer.register(LSPConsoleToolWindowPanel.this, consoleView); - add(consoleComponent, NAME_VIEW_CONSOLE); - configureToolbar(consoleComponent); - showConsole(); - } - } - - /** - * Configure console toolbar on the right of the console to provide some action like "Scroll to End", "Clean", etc - * - * @param consoleComponent - */ - private void configureToolbar(JComponent consoleComponent) { - DefaultActionGroup myToolbarActions = new DefaultActionGroup(); - myToolbarActions.addAll(consoleView.createConsoleActions()); - - ActionToolbar tb = ActionManager.getInstance().createActionToolbar("LSP Console", myToolbarActions, false); - tb.setTargetComponent(consoleComponent); - tb.getComponent().setBorder(JBUI.Borders.merge(tb.getComponent().getBorder(), JBUI.Borders.customLine(OnePixelDivider.BACKGROUND, 0, 0, 0, 1), true)); - consoleComponent.add(tb.getComponent(), BorderLayout.EAST); - } - - private JComponent createDetailPanel(LanguageServerTreeNode key) { - LanguageServersRegistry.LanguageServerDefinition serverDefinition = key.getServerDefinition(); - Project project = LSPConsoleToolWindowPanel.this.project; - ComboBox serverTraceComboBox = new ComboBox<>(new DefaultComboBoxModel<>(ServerTrace.values())); - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings initialSettings = UserDefinedLanguageServerSettings.getInstance(project).getLanguageServerSettings(serverDefinition.id); - if (initialSettings != null && initialSettings.getServerTrace() != null) { - serverTraceComboBox.setSelectedItem(initialSettings.getServerTrace()); - } - serverTraceComboBox.addItemListener(event -> { - ServerTrace serverTrace = (ServerTrace) event.getItem(); - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project).getLanguageServerSettings(serverDefinition.id); - if (settings == null) { - settings = new UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings(); - } - settings.setServerTrace(serverTrace); - UserDefinedLanguageServerSettings.getInstance(project).setLanguageServerSettings(serverDefinition.id, settings); - }); - - // Add a settings listener to keep serverTraceComboBox in sync with changes coming from the LSP settings page - // See https://github.com/redhat-developer/intellij-quarkus/issues/1062 - Runnable settingsChangeListener = createSettingsChangeListener(serverDefinition.id, serverTraceComboBox); - UserDefinedLanguageServerSettings.getInstance(getProject()).addChangeHandler(settingsChangeListener); - settingsChangeListeners.add(settingsChangeListener); - - return FormBuilder.createFormBuilder() - .setFormLeftIndent(10) - .addComponent(createTitleComponent(serverDefinition), 1) - .addLabeledComponent(LanguageServerBundle.message("language.server.trace"), serverTraceComboBox, 1) - .addComponentFillVertically(new JPanel(), 0) - .getPanel(); - } - - - private Runnable createSettingsChangeListener(String id, ComboBox serverTraceComboBox) { - return new Runnable() { - @Override - public void run() { - if (isDisposed()) { - return; - } - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project).getLanguageServerSettings(id); - if (settings == null) { //No settings have been set yet for this particular LS - return; - } - ServerTrace newServerTrace = settings.getServerTrace(); - if (newServerTrace != null && !newServerTrace.equals(serverTraceComboBox.getSelectedItem())) { - serverTraceComboBox.setSelectedItem(newServerTrace); - } - } - }; - } - - private JComponent createTitleComponent(LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) { - JLabel title = new JLabel(languageServerDefinition.getDisplayName()); - String description = languageServerDefinition.description; - if (description != null && description.length() > 0) { - // @See com.intellij.internal.ui.ComponentPanelTestAction for more details on how to create comment panels - return UI.PanelFactory.panel(title) - .withComment(description) - .createPanel(); - } - return title; - } - - private void showConsole() { - show(NAME_VIEW_CONSOLE); - } - - private void showDetail() { - show(NAME_VIEW_DETAIL); - } - - public void showMessage(String message) { - if (consoleView == null) { - return; - } - consoleView.print(message, ConsoleViewContentType.SYSTEM_OUTPUT); - } - - public void showError(Throwable exception) { - if (consoleView == null) { - return; - } - String stacktrace = getStackTrace(exception); - consoleView.print(stacktrace, ConsoleViewContentType.ERROR_OUTPUT); - } - - @Override - public void dispose() { - for (Runnable settingsChangeListener : settingsChangeListeners) { - UserDefinedLanguageServerSettings.getInstance(getProject()).removeChangeHandler(settingsChangeListener); - } - settingsChangeListeners.clear(); - super.dispose(); - if (consoleView != null) { - consoleView.dispose(); - } - } - } - - private ConsoleView createConsoleView(@NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @NotNull Project project) { - var builder = new LSPTextConsoleBuilderImpl(serverDefinition, project); - builder.setViewer(true); - return builder.getConsole(); - } - - public void showMessage(LanguageServerProcessTreeNode processTreeNode, String message) { - if (isDisposed()) { - return; - } - var consoleOrErrorPanel = consoles.getValue(processTreeNode, true); - if (consoleOrErrorPanel != null) { - consoleOrErrorPanel.showMessage(message); - } - } - - public void showError(LanguageServerProcessTreeNode processTreeNode, Throwable exception) { - if (isDisposed()) { - return; - } - var consoleOrErrorPanel = consoles.getValue(processTreeNode, true); - if (consoleOrErrorPanel != null) { - consoleOrErrorPanel.showError(exception); - } - } - - @Override - public void dispose() { - disposed = true; - if (consoles != null) { - consoles.dispose(); - } - explorer.dispose(); - } - - private boolean isDisposed() { - return disposed || project.isDisposed(); - } - - /** - * Code copied from https://github.com/apache/commons-lang/blob/24744a40b2c094945e542b71cc1fbf59caa0d70b/src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java#L400C5-L407C6 - * @param throwable - * @return - */ - private static String getStackTrace(final Throwable throwable) { - if (throwable == null) { - return ""; - } - final StringWriter sw = new StringWriter(); - throwable.printStackTrace(new PrintWriter(sw, true)); - return sw.toString(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleView.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleView.java deleted file mode 100644 index c70194714..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPConsoleView.java +++ /dev/null @@ -1,120 +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 com.redhat.devtools.intellij.lsp4ij.console; - -import com.intellij.execution.impl.ConsoleViewImpl; -import com.intellij.execution.impl.EditorHyperlinkSupport; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.editor.actions.ScrollToTheEndToolbarAction; -import com.intellij.openapi.project.Project; -import com.intellij.psi.search.GlobalSearchScope; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import com.redhat.devtools.intellij.lsp4ij.console.actions.AutoFoldingAction; -import com.redhat.devtools.intellij.lsp4ij.console.actions.ClearThisConsoleAction; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerProcessTreeNode; -import com.redhat.devtools.intellij.lsp4ij.settings.ServerTrace; -import com.redhat.devtools.intellij.lsp4ij.settings.UserDefinedLanguageServerSettings; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; - -import static com.redhat.devtools.intellij.lsp4ij.console.actions.AutoFoldingAction.shouldLSPTracesBeExpanded; - -/** - * Extends {@link ConsoleViewImpl} to support custom LSP folding by using [Trace - */ -public class LSPConsoleView extends ConsoleViewImpl { - - private final LanguageServersRegistry.LanguageServerDefinition serverDefinition; - - public LSPConsoleView(@NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @NotNull Project project, - @NotNull GlobalSearchScope searchScope, - boolean viewer, - boolean usePredefinedMessageFilter) { - super(project, searchScope, viewer, usePredefinedMessageFilter); - this.serverDefinition = serverDefinition; - } - - @Override - public AnAction @NotNull [] createConsoleActions() { - // Don't call super.createConsoleActions() to avoid having some action action like previous occurrence that we don't need. - List consoleActions = new ArrayList<>(); - consoleActions.add(new AutoFoldingAction(getEditor())); - consoleActions.add(new ScrollToTheEndToolbarAction(getEditor())); - consoleActions.add(ActionManager.getInstance().getAction("Print")); - consoleActions.add(new ClearThisConsoleAction(this)); - return consoleActions.toArray(AnAction.EMPTY_ARRAY); - } - - @Override - protected void updateFoldings(int startLine, int endLine) { - super.updateFoldings(startLine, endLine); - if (!canApplyFolding()) { - return; - } - - var editor = getEditor(); - editor.getFoldingModel().runBatchFoldingOperation(() -> { - var document = editor.getDocument(); - int foldingStartOffset = -1; - String foldingPaceholder = null; - int lineNumber = 0; - boolean expanded = shouldLSPTracesBeExpanded(editor); - for (int line = startLine; line <= endLine; line++) { - var lineText = EditorHyperlinkSupport.getLineText(document, line, false); - if (lineText.startsWith("[Trace")) { - var foldingEndOffset = document.getLineStartOffset(line) - 1; - if (foldingStartOffset != -1 && lineNumber > 0) { - // Fold the previous Trace - var region = editor.getFoldingModel().addFoldRegion(foldingStartOffset, foldingEndOffset, foldingPaceholder); - if (region != null) { - region.setExpanded(expanded); - } - } - foldingStartOffset = foldingEndOffset + 1; - foldingPaceholder = lineText; - lineNumber = 0; - } else { - if (foldingStartOffset != -1) { - lineNumber++; - } - } - } - if (foldingStartOffset != -1 && lineNumber > 0) { - // Fold the previous end Trace - var foldingEndOffset = document.getLineStartOffset(endLine) - 1; - var region = editor.getFoldingModel().addFoldRegion(foldingStartOffset, foldingEndOffset, foldingPaceholder); - if (region != null) { - region.setExpanded(expanded); - } - } - }); - } - - /** - * Returns true if language server settings is configured with "verbose" level trace for the language server and false otherwise. - * - * @return true if language server settings is configured with "verbose" level trace for the language server and false otherwise. - */ - private boolean canApplyFolding() { - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(getProject()).getLanguageServerSettings(serverDefinition.id); - if (settings == null) { - return false; - } - return settings.getServerTrace() == ServerTrace.verbose; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPTextConsoleBuilderImpl.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPTextConsoleBuilderImpl.java deleted file mode 100644 index b5460ffa7..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/LSPTextConsoleBuilderImpl.java +++ /dev/null @@ -1,41 +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 com.redhat.devtools.intellij.lsp4ij.console; - -import com.intellij.execution.filters.TextConsoleBuilderImpl; -import com.intellij.execution.impl.ConsoleViewImpl; -import com.intellij.execution.ui.ConsoleView; -import com.intellij.openapi.project.Project; -import com.intellij.psi.search.GlobalSearchScope; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerProcessTreeNode; -import org.jetbrains.annotations.NotNull; - -/** - * Extends {@link TextConsoleBuilderImpl} to create the custom {@link LSPConsoleView}. - */ -public class LSPTextConsoleBuilderImpl extends TextConsoleBuilderImpl { - - private final LanguageServersRegistry.LanguageServerDefinition serverDefinition; - - public LSPTextConsoleBuilderImpl(@NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @NotNull Project project) { - super(project); - this.serverDefinition = serverDefinition; - } - - @Override - protected @NotNull ConsoleView createConsole() { - return new LSPConsoleView(serverDefinition, getProject(), getScope(), isViewer(), isUsePredefinedMessageFilter()); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/SimpleCardLayoutPanel.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/SimpleCardLayoutPanel.java deleted file mode 100644 index 883bc258d..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/SimpleCardLayoutPanel.java +++ /dev/null @@ -1,89 +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 com.redhat.devtools.intellij.lsp4ij.console; - -import com.intellij.openapi.Disposable; -import com.intellij.util.ui.JBInsets; - -import java.awt.*; -import javax.swing.JComponent; -import javax.swing.JPanel; - -abstract class SimpleCardLayoutPanel extends JPanel implements Disposable { - - protected volatile boolean isDisposed = false; - - private CardLayout cardLayout; - - public SimpleCardLayoutPanel() { - this(new CardLayout()); - } - - public SimpleCardLayoutPanel(CardLayout cardLayout) { - super(cardLayout); - this.cardLayout = cardLayout; - } - - private Component visibleComponent() { - for (var component : getComponents()) { - if (component.isVisible()) return component; - } - return null; - } - - public void show(String name) { - cardLayout.show(this, name); - } - - @Override - public void dispose() { - if (!isDisposed) { - isDisposed = true; - removeAll(); - } - } - - @Override - public void doLayout() { - var bounds = new Rectangle(getWidth(), getHeight()); - JBInsets.removeFrom(bounds, getInsets()); - for (var component : getComponents()) { - component.setBounds(bounds); - } - } - - @Override - public Dimension getPreferredSize() { - var component = isPreferredSizeSet() ? null : visibleComponent(); - if (component == null) { - return super.getPreferredSize(); - } - // preferred size of a visible component plus border insets of this panel - var size = component.getPreferredSize(); - JBInsets.addTo(size, getInsets()); // add border of this panel - return size; - } - - @Override - public Dimension getMinimumSize() { - var component = isMinimumSizeSet() ? null : visibleComponent(); - if (component == null) { - return super.getMinimumSize(); - } - // minimum size of a visible component plus border insets of this panel - var size = component.getMinimumSize(); - JBInsets.addTo(size, getInsets()); - return size; - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/AutoFoldingAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/AutoFoldingAction.java deleted file mode 100644 index 4267fc1e8..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/AutoFoldingAction.java +++ /dev/null @@ -1,72 +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 com.redhat.devtools.intellij.lsp4ij.console.actions; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.ToggleAction; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.FoldRegion; -import com.intellij.openapi.project.DumbAware; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import org.jetbrains.annotations.NotNull; - -/** - * Collapse / Expand all LSP Traces action. - */ -public class AutoFoldingAction extends ToggleAction implements DumbAware { - - private boolean initialExpanded; - - private final Editor myEditor; - - public AutoFoldingAction(@NotNull final Editor editor) { - super(); - myEditor = editor; - final String message = LanguageServerBundle.message("action.lsp.console.folding.text"); - getTemplatePresentation().setDescription(message); - getTemplatePresentation().setText(message); - getTemplatePresentation().setIcon(AllIcons.Actions.Expandall); - } - - @Override - public boolean isSelected(@NotNull AnActionEvent e) { - return shouldLSPTracesBeExpanded(myEditor); - } - - /** - * Returns true if LSP traces from the console editor should be expanded and false otherwise. - * - * @param editor the console editor. - * @return true if LSP traces from the console editor should be expanded and false otherwise. - */ - public static boolean shouldLSPTracesBeExpanded(Editor editor) { - // Takes the last fold region and returns the expanded state - FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions(); - FoldRegion lastRegion = allRegions.length > 0 ? allRegions[allRegions.length - 1] : null; - return lastRegion != null ? lastRegion.isExpanded() : false; - } - - @Override - public void setSelected(@NotNull AnActionEvent e, boolean state) { - boolean expanded = state; - initialExpanded = expanded; - myEditor.getFoldingModel().runBatchFoldingOperation(() -> { - FoldRegion[] allRegions = myEditor.getFoldingModel().getAllFoldRegions(); - for (FoldRegion region : allRegions) { - region.setExpanded(expanded); - } - }); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/ClearThisConsoleAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/ClearThisConsoleAction.java deleted file mode 100644 index 7740c90e2..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/actions/ClearThisConsoleAction.java +++ /dev/null @@ -1,43 +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 com.redhat.devtools.intellij.lsp4ij.console.actions; - -import com.intellij.execution.actions.ClearConsoleAction; -import com.intellij.execution.ui.ConsoleView; -import com.intellij.openapi.actionSystem.AnActionEvent; -import org.jetbrains.annotations.NotNull; - -/** - * Clear a given console view. - * - * @author Angelo ZERR - */ -public class ClearThisConsoleAction extends ClearConsoleAction { - private final ConsoleView myConsoleView; - - public ClearThisConsoleAction(@NotNull ConsoleView consoleView) { - myConsoleView = consoleView; - } - - @Override - public void update(@NotNull AnActionEvent e) { - boolean enabled = myConsoleView.getContentSize() > 0; - e.getPresentation().setEnabled(enabled); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - myConsoleView.clear(); - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorer.java deleted file mode 100644 index ee540648a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorer.java +++ /dev/null @@ -1,219 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.SimpleToolWindowPanel; -import com.intellij.ui.AnimatedIcon; -import com.intellij.ui.PopupHandler; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.console.LSPConsoleToolWindowPanel; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.actions.CopyStartServerCommandAction; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.actions.RestartServerAction; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.actions.PauseServerAction; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.actions.StopServerAction; -import com.redhat.devtools.intellij.lsp4ij.lifecycle.LanguageServerLifecycleManager; - -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; -import java.awt.*; -import java.util.Comparator; - -/** - * Language server explorer which shows language servers and their process. - * - * @author Angelo ZERR - */ -public class LanguageServerExplorer extends SimpleToolWindowPanel implements Disposable { - - private final LSPConsoleToolWindowPanel panel; - - private final Tree tree; - private final LanguageServerExplorerLifecycleListener listener; - private boolean disposed; - - private TreeSelectionListener treeSelectionListener = event -> { - if (isDisposed()) { - return; - } - TreePath selectionPath = event.getPath(); - Object selectedItem = selectionPath != null ? selectionPath.getLastPathComponent() : null; - if (selectedItem instanceof LanguageServerTreeNode) { - LanguageServerTreeNode node = (LanguageServerTreeNode) selectedItem; - onLanguageServerSelected(node); - } else if (selectedItem instanceof LanguageServerProcessTreeNode) { - LanguageServerProcessTreeNode node = (LanguageServerProcessTreeNode) selectedItem; - onLanguageServerProcessSelected(node); - } - }; - - public LanguageServerExplorer(LSPConsoleToolWindowPanel panel) { - super(true, false); - this.panel = panel; - tree = buildTree(); - this.setContent(tree); - listener = new LanguageServerExplorerLifecycleListener(this); - LanguageServerLifecycleManager.getInstance(panel.getProject()) - .addLanguageServerLifecycleListener(listener); - } - - private void onLanguageServerSelected(LanguageServerTreeNode treeNode) { - if (isDisposed()) { - return; - } - panel.selectDetail(treeNode); - } - - private void onLanguageServerProcessSelected(LanguageServerProcessTreeNode processTreeNode) { - if (isDisposed()) { - return; - } - panel.selectConsole(processTreeNode); - } - - /** - * Builds the Language server tree - * - * @return Tree object of all language servers - */ - private Tree buildTree() { - - DefaultMutableTreeNode top = new DefaultMutableTreeNode("Language servers"); - - Tree tree = new Tree(top); - tree.setRootVisible(false); - - // Fill tree will all language server definitions, ordered alphabetically - LanguageServersRegistry.getInstance().getAllDefinitions().stream() - .sorted(Comparator.comparing(LanguageServersRegistry.LanguageServerDefinition::getDisplayName)) - .map(LanguageServerTreeNode::new) - .forEach(top::add); - - tree.setCellRenderer(new LanguageServerTreeRenderer()); - - tree.addTreeSelectionListener(treeSelectionListener); - - tree.addMouseListener(new PopupHandler() { - @Override - public void invokePopup(Component comp, int x, int y) { - if (isDisposed()) { - return; - } - final TreePath path = tree.getSelectionPath(); - if (path != null) { - DefaultActionGroup group = null; - Object node = path.getLastPathComponent(); - if (node instanceof LanguageServerProcessTreeNode) { - LanguageServerProcessTreeNode processTreeNode = (LanguageServerProcessTreeNode) node; - switch (processTreeNode.getServerStatus()) { - case starting: - case started: - // Stop and disable the language server action - group = new DefaultActionGroup(); - AnAction stopServerAction = ActionManager.getInstance().getAction(StopServerAction.ACTION_ID); - group.add(stopServerAction); - if (Boolean.getBoolean("idea.is.internal")) { - // In dev mode, enable the "Pause" action - AnAction pauseServerAction = ActionManager.getInstance().getAction(PauseServerAction.ACTION_ID); - group.add(pauseServerAction); - } - break; - case stopping: - case stopped: - // Restart language server action - group = new DefaultActionGroup(); - AnAction restartServerAction = ActionManager.getInstance().getAction(RestartServerAction.ACTION_ID); - group.add(restartServerAction); - break; - } - if (group == null) { - group = new DefaultActionGroup(); - } - AnAction testStartServerAction = ActionManager.getInstance().getAction(CopyStartServerCommandAction.ACTION_ID); - group.add(testStartServerAction); - } - - if (group != null) { - ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.TOOLWINDOW_POPUP, group); - menu.getComponent().show(comp, x, y); - } - } - } - }); - tree.putClientProperty(AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED, true); - - ((DefaultTreeModel) tree.getModel()).reload(top); - return tree; - } - - public Tree getTree() { - return tree; - } - - @Override - public void dispose() { - this.disposed = true; - tree.removeTreeSelectionListener(treeSelectionListener); - LanguageServerLifecycleManager.getInstance(panel.getProject()) - .removeLanguageServerLifecycleListener(listener); - } - - public boolean isDisposed() { - return disposed || getProject().isDisposed() || listener.isDisposed(); - } - - public void showMessage(LanguageServerProcessTreeNode processTreeNode, String message) { - panel.showMessage(processTreeNode, message); - } - - public void showError(LanguageServerProcessTreeNode processTreeNode, Throwable exception) { - panel.showError(processTreeNode, exception); - } - - public DefaultTreeModel getTreeModel() { - return (DefaultTreeModel) tree.getModel(); - } - - public void selectAndExpand(DefaultMutableTreeNode treeNode) { - var treePath = new TreePath(treeNode.getPath()); - tree.setSelectionPath(treePath); - if (!tree.isExpanded(treePath)) { - tree.expandPath(treePath); - } - } - - public Project getProject() { - return panel.getProject(); - } - - /** - * Initialize language server process with the started language servers. - */ - public void load() { - LanguageServiceAccessor.getInstance(getProject()).getStartedServers() - .forEach(ls -> { - Throwable serverError = ls.getServerError(); - listener.handleStatusChanged(ls); - if (serverError != null) { - listener.handleError(ls, serverError); - } - }); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorerLifecycleListener.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorerLifecycleListener.java deleted file mode 100644 index c507a57bc..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerExplorerLifecycleListener.java +++ /dev/null @@ -1,188 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.ServerStatus; -import com.redhat.devtools.intellij.lsp4ij.settings.ServerTrace; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.lifecycle.LanguageServerLifecycleListener; -import com.redhat.devtools.intellij.lsp4ij.settings.UserDefinedLanguageServerSettings; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.messages.Message; - -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.HashMap; -import java.util.Map; - -/** - * Language server listener to refresh the language server explorer according to the server state and fill the LSP console. - * - * @author Angelo ZERR - */ -public class LanguageServerExplorerLifecycleListener implements LanguageServerLifecycleListener { - - private final Map tracingPerServer = new HashMap<>(10); - - private boolean disposed; - - private final LanguageServerExplorer explorer; - - public LanguageServerExplorerLifecycleListener(LanguageServerExplorer explorer) { - this.explorer = explorer; - } - - @Override - public void handleStatusChanged(LanguageServerWrapper languageServer) { - ServerStatus serverStatus = languageServer.getServerStatus(); - boolean selectProcess = serverStatus == ServerStatus.starting; - updateServerStatus(languageServer, serverStatus, selectProcess); - } - - @Override - public void handleLSPMessage(Message message, MessageConsumer messageConsumer, LanguageServerWrapper languageServer) { - if (explorer.isDisposed()) { - return; - } - LanguageServerProcessTreeNode processTreeNode = updateServerStatus(languageServer, null, false); - ServerTrace serverTrace = getServerTrace(explorer.getProject(), languageServer.serverDefinition.id); - if (serverTrace == ServerTrace.off) { - return; - } - - TracingMessageConsumer tracing = getLSPRequestCacheFor(languageServer); - String log = tracing.log(message, messageConsumer, serverTrace); - invokeLater(() -> showMessage(processTreeNode, log)); - } - - @Override - public void handleError(LanguageServerWrapper languageServer, Throwable exception) { - LanguageServerProcessTreeNode processTreeNode = updateServerStatus(languageServer, null, false); - if (exception == null) { - return; - } - - invokeLater(() -> showError(processTreeNode, exception)); - } - - private TracingMessageConsumer getLSPRequestCacheFor(LanguageServerWrapper languageServer) { - TracingMessageConsumer cache = tracingPerServer.get(languageServer); - if (cache != null) { - return cache; - } - synchronized (tracingPerServer) { - cache = tracingPerServer.get(languageServer); - if (cache != null) { - return cache; - } - cache = new TracingMessageConsumer(); - tracingPerServer.put(languageServer, cache); - return cache; - } - } - - - private static ServerTrace getServerTrace(Project project, String languageServerId) { - ServerTrace serverTrace = null; - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project).getLanguageServerSettings(languageServerId); - if (settings != null) { - serverTrace = settings.getServerTrace(); - } - return serverTrace != null ? serverTrace : ServerTrace.off; - } - - private LanguageServerTreeNode findLanguageServerTreeNode(LanguageServerWrapper languageServer) { - var tree = explorer.getTree(); - DefaultMutableTreeNode top = (DefaultMutableTreeNode) tree.getModel().getRoot(); - for (int i = 0; i < top.getChildCount(); i++) { - LanguageServerTreeNode node = (LanguageServerTreeNode) top.getChildAt(i); - if (node.getServerDefinition().equals(languageServer.serverDefinition)) { - return node; - } - } - return null; - } - - private LanguageServerProcessTreeNode updateServerStatus(LanguageServerWrapper languageServer, ServerStatus serverStatus, boolean selectProcess) { - LanguageServerTreeNode serverNode = findLanguageServerTreeNode(languageServer); - if (serverNode == null) { - // Should never occur. - return null; - } - var processTreeNode = serverNode.getActiveProcessTreeNode(); - if (processTreeNode == null) { - var treeModel = explorer.getTreeModel(); - processTreeNode = new LanguageServerProcessTreeNode(languageServer, treeModel); - if (serverStatus == null) { - // compute the server status - serverStatus = languageServer.getServerStatus(); - } - selectProcess = true; - serverNode.add(processTreeNode); - } - boolean serverStatusChanged = serverStatus != null && serverStatus != processTreeNode.getServerStatus(); - boolean updateUI = serverStatusChanged || selectProcess; - if (updateUI) { - final var node = processTreeNode; - final var status = serverStatus; - final var select = selectProcess; - invokeLater(() -> { - if (explorer.isDisposed()) { - return; - } - if (serverStatusChanged) { - node.setServerStatus(status); - } - if (select) { - explorer.selectAndExpand(node); - } - }); - } - return processTreeNode; - } - - private void showMessage(LanguageServerProcessTreeNode processTreeNode, String message) { - if (explorer.isDisposed()) { - return; - } - explorer.showMessage(processTreeNode, message); - } - - private void showError(LanguageServerProcessTreeNode processTreeNode, Throwable exception) { - if (explorer.isDisposed()) { - return; - } - explorer.showError(processTreeNode, exception); - } - - public boolean isDisposed() { - return disposed; - } - - @Override - public void dispose() { - disposed = true; - tracingPerServer.clear(); - } - - private static void invokeLater(Runnable runnable) { - if (ApplicationManager.getApplication().isDispatchThread()) { - runnable.run(); - } else { - ApplicationManager.getApplication().invokeLater(runnable); - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerProcessTreeNode.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerProcessTreeNode.java deleted file mode 100644 index fc9950460..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerProcessTreeNode.java +++ /dev/null @@ -1,129 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.util.text.Formats; -import com.intellij.ui.AnimatedIcon; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.ServerStatus; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; - -/** - * Language server process node. - */ -public class LanguageServerProcessTreeNode extends DefaultMutableTreeNode { - - private static final Icon RUNNING_ICON = new AnimatedIcon.Default(); - - private final LanguageServerWrapper languageServer; - - private final DefaultTreeModel treeModel; - - private ServerStatus serverStatus; - - private long startTime = -1; - - private String displayName; - - public LanguageServerProcessTreeNode(LanguageServerWrapper languageServer, DefaultTreeModel treeModel) { - this.languageServer = languageServer; - this.treeModel = treeModel; - } - - public void setServerStatus(ServerStatus serverStatus) { - this.serverStatus = serverStatus; - displayName = getDisplayName(serverStatus); - switch (serverStatus) { - case starting: - case stopping: - startTime = System.currentTimeMillis(); - break; - case stopped: - case started: - startTime = -1; - break; - } - this.setUserObject(displayName); - treeModel.nodeChanged(this); - } - - private String getDisplayName(ServerStatus serverStatus) { - if (!languageServer.isEnabled()) { - return "disabled"; - } - Throwable serverError = languageServer.getServerError(); - StringBuilder name = new StringBuilder(); - if (serverError == null) { - name.append(serverStatus.name()); - } else { - name.append(serverStatus == ServerStatus.stopped ? "crashed" : serverStatus.name()); - int nbTryRestart = languageServer.getNumberOfRestartAttempts(); - int nbTryRestartMax = languageServer.getMaxNumberOfRestartAttempts(); - name.append(" ["); - name.append(nbTryRestart); - name.append("/"); - name.append(nbTryRestartMax); - name.append("]"); - } - Long pid = languageServer.getCurrentProcessId(); - if (pid != null) { - name.append(" pid:"); - name.append(pid); - } - return name.toString(); - } - - public LanguageServerWrapper getLanguageServer() { - return languageServer; - } - - public ServerStatus getServerStatus() { - return serverStatus; - } - - public Icon getIcon() { - if (!languageServer.isEnabled()) { - return AllIcons.Actions.Cancel; - } - boolean hasError = languageServer.getServerError() != null; - switch (serverStatus) { - case started: - if (hasError) { - return AllIcons.RunConfigurations.TestFailed; - } - return AllIcons.Actions.Commit; - case stopped: - if (hasError) { - return AllIcons.RunConfigurations.TestError; - } - return AllIcons.Actions.Suspend; - default: - return RUNNING_ICON; - } - } - - public String getDisplayName() { - return displayName; - } - - public String getElapsedTime() { - long endTime = System.currentTimeMillis(); - long duration = endTime - startTime; - return Formats.formatDuration(duration, "\u2009"); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeNode.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeNode.java deleted file mode 100644 index ff49fff37..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeNode.java +++ /dev/null @@ -1,51 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; - -/** - * Language server node. - */ -public class LanguageServerTreeNode extends DefaultMutableTreeNode { - - private final LanguageServersRegistry.LanguageServerDefinition serverDefinition; - - public LanguageServerTreeNode(LanguageServersRegistry.LanguageServerDefinition serverDefinition) { - this.serverDefinition = serverDefinition; - } - - public LanguageServersRegistry.LanguageServerDefinition getServerDefinition() { - return serverDefinition; - } - - public LanguageServerProcessTreeNode getActiveProcessTreeNode() { - for (int i = 0; i < super.getChildCount(); i++) { - return (LanguageServerProcessTreeNode) super.getChildAt(i); - } - return null; - } - - public Icon getIcon() { - String serverId = getServerDefinition().id; - return LanguageServersRegistry.getInstance().getServerIcon(serverId); - } - - public String getDisplayName() { - return serverDefinition.getDisplayName(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeRenderer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeRenderer.java deleted file mode 100644 index 4b3c31a06..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/LanguageServerTreeRenderer.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 com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.intellij.ide.ui.UISettings; -import com.intellij.ui.ColoredTreeCellRenderer; -import com.intellij.ui.RelativeFont; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.util.ui.UIUtil; -import com.redhat.devtools.intellij.lsp4ij.ServerStatus; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; - -/** - * Language Server tree nodes renderer. - *

- * Some piece of code has been copied/pasted from https://github.com/JetBrains/intellij-community/blob/master/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/TestTreeRenderer.java and adapted for Language Server case. - */ -public class LanguageServerTreeRenderer extends ColoredTreeCellRenderer { - - @NonNls - private static final String SPACE_STRING = " "; - private String myDurationText; - private Color myDurationColor; - private int myDurationWidth; - private int myDurationOffset; - - @Override - public void customizeCellRenderer(@NotNull final JTree tree, - final Object value, - final boolean selected, - final boolean expanded, - final boolean leaf, - final int row, - final boolean hasFocus) { - myDurationText = null; - myDurationColor = null; - myDurationWidth = 0; - myDurationOffset = 0; - - if (value instanceof LanguageServerTreeNode) { - // Render of language server - LanguageServerTreeNode languageServerTreeNode = (LanguageServerTreeNode) value; - setIcon(languageServerTreeNode.getIcon()); - append(languageServerTreeNode.getDisplayName()); - return; - } - - if (value instanceof LanguageServerProcessTreeNode) { - // Render of language server process - LanguageServerProcessTreeNode languageProcessTreeNode = (LanguageServerProcessTreeNode) value; - setIcon(languageProcessTreeNode.getIcon()); - append(languageProcessTreeNode.getDisplayName()); - - if (languageProcessTreeNode.getServerStatus() == ServerStatus.starting - || languageProcessTreeNode.getServerStatus() == ServerStatus.stopping) { - // Display elapsed time when language server is starting/stopping - myDurationText = languageProcessTreeNode.getElapsedTime(); - final var durationText = myDurationText; - if (durationText != null) { - FontMetrics metrics = getFontMetrics(RelativeFont.SMALL.derive(getFont())); - myDurationWidth = metrics.stringWidth(durationText); - myDurationOffset = metrics.getHeight() / 2; // an empty area before and after the text - myDurationColor = selected ? UIUtil.getTreeSelectionForeground(hasFocus) : SimpleTextAttributes.GRAYED_ATTRIBUTES.getFgColor(); - } - } - return; - } - //strange node - final String text = value.toString(); - //no icon - append(text != null ? text : SPACE_STRING, SimpleTextAttributes.GRAYED_ATTRIBUTES); - } - - @NotNull - @Override - public Dimension getPreferredSize() { - final Dimension preferredSize = super.getPreferredSize(); - if (myDurationWidth > 0) preferredSize.width += myDurationWidth + myDurationOffset; - return preferredSize; - } - - @Override - protected void paintComponent(Graphics g) { - UISettings.setupAntialiasing(g); - Shape clip = null; - int width = getWidth(); - int height = getHeight(); - if (isOpaque()) { - // paint background for expanded row - g.setColor(getBackground()); - g.fillRect(0, 0, width, height); - } - if (myDurationWidth > 0) { - width -= myDurationWidth + myDurationOffset; - if (width > 0 && height > 0) { - g.setColor(myDurationColor); - g.setFont(RelativeFont.SMALL.derive(getFont())); - g.drawString(myDurationText, width + myDurationOffset / 2, getTextBaseLine(g.getFontMetrics(), height)); - clip = g.getClip(); - g.clipRect(0, 0, width, height); - } - } - super.paintComponent(g); - // restore clip area if needed - if (clip != null) g.setClip(clip); - } -} - diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/TracingMessageConsumer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/TracingMessageConsumer.java deleted file mode 100644 index e1bb121da..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/TracingMessageConsumer.java +++ /dev/null @@ -1,214 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2016-2019 TypeFox 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 Eclipse Distribution License v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.console.explorer; - -import com.redhat.devtools.intellij.lsp4ij.settings.ServerTrace; -import org.eclipse.lsp4j.jsonrpc.JsonRpcException; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.MessageIssueException; -import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint; -import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler; -import org.eclipse.lsp4j.jsonrpc.json.StreamMessageConsumer; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage; -import org.eclipse.lsp4j.jsonrpc.messages.RequestMessage; -import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage; - -import java.time.Clock; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Outputs logs in a format that can be parsed by the LSP Inspector. - * https://microsoft.github.io/language-server-protocol/inspector/ - *

- * This class is a copy/paste of https://github.com/eclipse-lsp4j/lsp4j/blob/main/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/TracingMessageConsumer.java - * adapted for IJ. - */ -public class TracingMessageConsumer { - private final Map sentRequests; - private final Map receivedRequests; - private final Clock clock; - private final DateTimeFormatter dateTimeFormatter; - - public TracingMessageConsumer() { - this.sentRequests = new ConcurrentHashMap<>(); - this.receivedRequests = new ConcurrentHashMap<>(); - this.clock = Clock.systemDefaultZone(); - this.dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(clock.getZone()); - } - - /** - * Constructs a log string for a given {@link Message}. The type of the {@link MessageConsumer} - * determines if we're sending or receiving a message. The type of the @{link Message} determines - * if it is a request, response, or notification. - */ - public String log(Message message, MessageConsumer messageConsumer, ServerTrace serverTrace) throws MessageIssueException, JsonRpcException { - final Instant now = clock.instant(); - final String date = dateTimeFormatter.format(now); - - if (messageConsumer instanceof StreamMessageConsumer) { - return consumeMessageSending(message, now, date, serverTrace); - } else if (messageConsumer instanceof RemoteEndpoint) { - return consumeMessageReceiving(message, now, date, serverTrace); - } else { - return String.format("Unknown MessageConsumer type: %s", messageConsumer); - } - } - - private String consumeMessageSending(Message message, Instant now, String date, ServerTrace serverTrace) { - if (message instanceof RequestMessage) { - RequestMessage requestMessage = (RequestMessage) message; - String id = requestMessage.getId(); - String method = requestMessage.getMethod(); - RequestMetadata requestMetadata = new RequestMetadata(method, now); - sentRequests.put(id, requestMetadata); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Sending request '%s - (%s)'.\n"; - return String.format(format, date, method, id); - } - Object params = requestMessage.getParams(); - String paramsJson = MessageJsonHandler.toString(params); - String format = "[Trace - %s] Sending request '%s - (%s)'.\nParams: %s\n\n\n"; - return String.format(format, date, method, id, paramsJson); - } else if (message instanceof ResponseMessage) { - ResponseMessage responseMessage = (ResponseMessage) message; - String id = responseMessage.getId(); - RequestMetadata requestMetadata = receivedRequests.remove(id); - String method = getMethod(requestMetadata); - String latencyMillis = getLatencyMillis(requestMetadata, now); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Sending response '%s - (%s)'. Processing request took %sms\n"; - return String.format(format, date, method, id, latencyMillis); - } - Object result = responseMessage.getResult(); - String resultJson = MessageJsonHandler.toString(result); - String resultTrace = getResultTrace(resultJson, null); - String format = - "[Trace - %s] Sending response '%s - (%s)'. Processing request took %sms\n%s\n\n\n"; - return String.format(format, date, method, id, latencyMillis, resultTrace); - } else if (message instanceof NotificationMessage) { - NotificationMessage notificationMessage = (NotificationMessage) message; - String method = notificationMessage.getMethod(); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Sending notification '%s'\n"; - return String.format(format, date, method); - } - Object params = notificationMessage.getParams(); - String paramsJson = MessageJsonHandler.toString(params); - String format = "[Trace - %s] Sending notification '%s'\nParams: %s\n\n\n"; - return String.format(format, date, method, paramsJson); - } else { - return String.format("Unknown message type: %s", message); - } - } - - private String consumeMessageReceiving(Message message, Instant now, String date, ServerTrace serverTrace) { - if (message instanceof RequestMessage) { - RequestMessage requestMessage = (RequestMessage) message; - String method = requestMessage.getMethod(); - String id = requestMessage.getId(); - RequestMetadata requestMetadata = new RequestMetadata(method, now); - receivedRequests.put(id, requestMetadata); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Received request '%s - (%s)'.\n"; - return String.format(format, date, method, id); - } - Object params = requestMessage.getParams(); - String paramsJson = MessageJsonHandler.toString(params); - String format = "[Trace - %s] Received request '%s - (%s)'\nParams: %s\n\n\n"; - return String.format(format, date, method, id, paramsJson); - } else if (message instanceof ResponseMessage) { - ResponseMessage responseMessage = (ResponseMessage) message; - String id = responseMessage.getId(); - RequestMetadata requestMetadata = sentRequests.remove(id); - String method = getMethod(requestMetadata); - String latencyMillis = getLatencyMillis(requestMetadata, now); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Received response '%s - (%s)' in %sms.\n"; - return String.format(format, date, method, id, latencyMillis); - } - Object result = responseMessage.getResult(); - String resultJson = MessageJsonHandler.toString(result); - Object error = responseMessage.getError(); - String errorJson = MessageJsonHandler.toString(error); - String resultTrace = getResultTrace(resultJson, errorJson); - String format = "[Trace - %s] Received response '%s - (%s)' in %sms.\n%s\n\n\n"; - return String.format(format, date, method, id, latencyMillis, resultTrace); - } else if (message instanceof NotificationMessage) { - NotificationMessage notificationMessage = (NotificationMessage) message; - String method = notificationMessage.getMethod(); - if (serverTrace == ServerTrace.messages) { - String format = "[Trace - %s] Received notification '%s'\n"; - return String.format(format, date, method); - } - Object params = notificationMessage.getParams(); - String paramsJson = MessageJsonHandler.toString(params); - String format = "[Trace - %s] Received notification '%s'\nParams: %s\n\n\n"; - return String.format(format, date, method, paramsJson); - } else { - return String.format("Unknown message type: %s", message); - } - } - - private static String getResultTrace(String resultJson, String errorJson) { - StringBuilder result = new StringBuilder(); - if (resultJson != null && !"null".equals(resultJson)) { - result.append("Result: "); - result.append(resultJson); - } else { - result.append("No result returned."); - } - if (errorJson != null && !"null".equals(errorJson)) { - result.append("\nError: "); - result.append(errorJson); - } - return result.toString(); - } - - private static String getMethod(RequestMetadata requestMetadata) { - return requestMetadata != null ? requestMetadata.method : ""; - } - - private static String getLatencyMillis(RequestMetadata requestMetadata, Instant now) { - return requestMetadata != null ? String.valueOf(now.toEpochMilli() - requestMetadata.start.toEpochMilli()) : "?"; - } - - /** - * Data class for holding pending request metadata. - */ - public static class RequestMetadata { - final String method; - final Instant start; - - public RequestMetadata(String method, Instant start) { - this.method = method; - this.start = start; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - RequestMetadata that = (RequestMetadata) o; - return Objects.equals(method, that.method) && Objects.equals(start, that.start); - } - - @Override - public int hashCode() { - return Objects.hash(method, start); - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/CopyStartServerCommandAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/CopyStartServerCommandAction.java deleted file mode 100644 index 95479dd4a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/CopyStartServerCommandAction.java +++ /dev/null @@ -1,65 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer.actions; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.DumbAware; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Copy in the clipboard the command which starts the selected language server process from the language explorer. - *

- * This action can be helpful to understand some problem with start of the language server - */ -public class CopyStartServerCommandAction extends TreeAction implements DumbAware { - - public static final String ACTION_ID = "lsp.console.explorer.copy.command"; - - @Override - protected void actionPerformed(@NotNull Tree tree, @NotNull AnActionEvent e) { - LanguageServerWrapper languageServer = getSelectedLanguageServer(tree); - if (languageServer != null) { - - List commands = languageServer.getCurrentProcessCommandLine(); - if (commands == null) { - return; - } - - String text = commands - .stream() - .map(param -> { - if (param.indexOf(' ') != -1) { - return "\"" + param + "\""; - } - return param; - }) - .collect(Collectors.joining(" ")); - - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - StringSelection selection = new StringSelection(text); - clipboard.setContents(selection, null); - - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/PauseServerAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/PauseServerAction.java deleted file mode 100644 index d4688c60c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/PauseServerAction.java +++ /dev/null @@ -1,37 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer.actions; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.DumbAware; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.jetbrains.annotations.NotNull; - -/** - * Action to stop the selected language server process from the language explorer. - */ -public class PauseServerAction extends TreeAction implements DumbAware { - - public static final String ACTION_ID = "lsp.console.explorer.pause"; - @Override - protected void actionPerformed(@NotNull Tree tree, @NotNull AnActionEvent e) { - LanguageServerWrapper languageServer = getSelectedLanguageServer(tree); - if (languageServer != null) { - languageServer.stop(); - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/RestartServerAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/RestartServerAction.java deleted file mode 100644 index db198e007..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/RestartServerAction.java +++ /dev/null @@ -1,41 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer.actions; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.DumbAware; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -/** - * Action to restart the selected language server process from the language explorer. - */ -public class RestartServerAction extends TreeAction implements DumbAware { - - public static final String ACTION_ID = "lsp.console.explorer.restart"; - - @Override - protected void actionPerformed(@NotNull Tree tree, @NotNull AnActionEvent e) { - LanguageServerWrapper languageServer = getSelectedLanguageServer(tree); - if (languageServer != null) { - languageServer.restart(); - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/StopServerAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/StopServerAction.java deleted file mode 100644 index 114aca98f..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/StopServerAction.java +++ /dev/null @@ -1,38 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer.actions; - -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.DumbAware; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.jetbrains.annotations.NotNull; - -/** - * Action to stop the selected language server process and disable it from the language explorer. - */ -public class StopServerAction extends TreeAction implements DumbAware { - - public static final String ACTION_ID = "lsp.console.explorer.stop"; - - @Override - protected void actionPerformed(@NotNull Tree tree, @NotNull AnActionEvent e) { - LanguageServerWrapper languageServer = getSelectedLanguageServer(tree); - if (languageServer != null) { - languageServer.stopAndDisable(); - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/TreeAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/TreeAction.java deleted file mode 100644 index dfdfe9b82..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/console/explorer/actions/TreeAction.java +++ /dev/null @@ -1,72 +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 com.redhat.devtools.intellij.lsp4ij.console.explorer.actions; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.util.NlsActions; -import com.intellij.ui.treeStructure.Tree; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.console.explorer.LanguageServerProcessTreeNode; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.tree.TreePath; -import java.awt.*; - -/** - * Base class for Actions processed from the Language Server tree. - */ -public abstract class TreeAction extends AnAction { - public final void actionPerformed(@NotNull AnActionEvent e) { - Tree tree = getTree(e); - if (tree == null) { - return; - } - actionPerformed(tree, e); - } - - /** - * Returns the language server tree and null otherwise. - * - * @param e the action event. - * @return the language server tree and null otherwise. - */ - private Tree getTree(AnActionEvent e) { - Component component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT); - if (component instanceof Tree) { - return (Tree) component; - } - return null; - } - - /** - * Returns the selected language server tree node and null otherwise. - * - * @param tree the tree. - * @return the selected language server tree node and null otherwise. - */ - protected LanguageServerWrapper getSelectedLanguageServer(Tree tree) { - TreePath path = tree.getSelectionPath(); - Object node = path != null ? path.getLastPathComponent() : null; - if (node instanceof LanguageServerProcessTreeNode) { - return ((LanguageServerProcessTreeNode) node).getLanguageServer(); - } - return null; - } - - protected abstract void actionPerformed(@NotNull Tree tree, @NotNull AnActionEvent e); - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspection.java deleted file mode 100644 index daca8791a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspection.java +++ /dev/null @@ -1,22 +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 com.redhat.devtools.intellij.lsp4ij.inspections; - -import com.intellij.codeInspection.LocalInspectionTool; - -/** - * No-op {@link LocalInspectionTool} used as a basis for mapping inspection severities to matching LSP severities. - */ -public abstract class AbstractDelegateInspection extends LocalInspectionTool { -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspectionWithExclusions.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspectionWithExclusions.java deleted file mode 100644 index a2bc0357d..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/inspections/AbstractDelegateInspectionWithExclusions.java +++ /dev/null @@ -1,52 +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 com.redhat.devtools.intellij.lsp4ij.inspections; - -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.ui.InspectionOptionsPanel; -import com.intellij.codeInspection.ui.ListEditForm; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.List; - -/** - * Base {@link LocalInspectionTool} providing the possibility to define exclusions. - */ -public abstract class AbstractDelegateInspectionWithExclusions extends LocalInspectionTool { - - private final String exclusionsLabel; - - /** - * Inspection constructor - * @param exclusionsLabel the label to use for the exclusion component in the options panel - */ - public AbstractDelegateInspectionWithExclusions(@NotNull String exclusionsLabel) { - this.exclusionsLabel = exclusionsLabel; - } - - //Field is public, so it can be serialized as XML - public final @NotNull List excludeList = new ArrayList<>(); - - public JComponent createOptionsPanel() { - InspectionOptionsPanel panel = new InspectionOptionsPanel(); - - var injectionListTable = new ListEditForm("", exclusionsLabel, excludeList); - - panel.addGrowing(injectionListTable.getContentPanel()); - - return panel; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationSupport.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationSupport.java deleted file mode 100644 index e97f81ce2..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationSupport.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij.internal; - -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CopyOnWriteArrayList; - -import com.intellij.openapi.progress.ProcessCanceledException; -import org.eclipse.lsp4j.jsonrpc.CancelChecker; - -/** - * LSP cancellation support hosts the list of LSP requests to cancel when a - * process is canceled (ex: when completion is re-triggered, when hover is give - * up, etc) - * - * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#cancelRequest - */ -public class CancellationSupport implements CancelChecker { - - private final List> futuresToCancel; - - private boolean cancelled; - - public CancellationSupport() { - this.futuresToCancel = new CopyOnWriteArrayList<>(); - this.cancelled = false; - } - - public CompletableFuture execute(CompletableFuture future) { - if (cancelled) { - future.cancel(true); - throw new ProcessCanceledException(); - } else { - this.futuresToCancel.add(future); - } - return future; - } - - /** - * Cancel all LSP requests. - */ - public void cancel() { - if (cancelled) { - return; - } - this.cancelled = true; - for (CompletableFuture futureToCancel : futuresToCancel) { - if (!futureToCancel.isDone()) { - futureToCancel.cancel(true); - } - } - futuresToCancel.clear(); - } - - @Override - public void checkCanceled() { - // When LSP requests are called (ex : 'textDocument/completion') the LSP - // response - // items are used to compose some UI item (ex : LSP CompletionItem are translate - // to IJ LookupElement fo). - // If the cancel occurs after the call of those LSP requests, the component - // which uses the LSP responses - // can call checkCanceled to stop the UI creation. - if (cancelled) { - throw new CancellationException(); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationUtil.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationUtil.java deleted file mode 100644 index 7caa62ca3..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/CancellationUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Avaloq Group AG. - * 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: - * Rubén Porras Campo (Avaloq Group AG) - Initial Implementation - *******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.internal; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; - -import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; -import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; -import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode; - -/** - * Cancellation utilities. - * - * This class is a copy/paste from https://github.com/eclipse/lsp4e/blob/master/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/CancellationUtil.java - */ -public final class CancellationUtil { - - private CancellationUtil() { - // this class shouldn't be instantiated - } - - public static boolean isRequestCancelledException(Throwable throwable) { - if (throwable instanceof CompletionException || throwable instanceof ExecutionException) { - throwable = throwable.getCause(); - } - if (throwable instanceof ResponseErrorException) { - return isRequestCancelled((ResponseErrorException)throwable); - } - return throwable instanceof CancellationException; - } - - private static boolean isRequestCancelled(ResponseErrorException responseErrorException) { - ResponseError responseError = responseErrorException.getResponseError(); - return responseError != null - && responseError.getCode() == ResponseErrorCode.RequestCancelled.getValue(); - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/PromiseToCompletableFuture.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/PromiseToCompletableFuture.java deleted file mode 100644 index 4173bc391..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/PromiseToCompletableFuture.java +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij.internal; - -import com.intellij.ide.lightEdit.LightEdit; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.IndexNotReadyException; -import com.intellij.openapi.project.Project; -import com.intellij.util.concurrency.AppExecutorUtil; -import com.redhat.devtools.intellij.lsp4ij.client.ExecutionAttemptLimitReachedException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.CancellablePromise; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; - -/** - * LSP completable future which execute a given function code in a non blocking reading action promise. - */ -public class PromiseToCompletableFuture extends CompletableFuture { - - private static final Logger LOGGER = LoggerFactory.getLogger(PromiseToCompletableFuture.class); - - private class ResultOrError { - - public final R result; - - public final Exception error; - - public ResultOrError(R result, Exception error) { - this.result = result; - this.error = error; - } - } - - private static final int MAX_ATTEMPT = 5; - - private final Function code; - - private final String progressTitle; - - private final Project project; - private final Disposable parentDisposable; - - private final AtomicInteger nbAttempt; - - private final Object[] coalesceBy; - private CancellablePromise> nonBlockingReadActionPromise; - - public PromiseToCompletableFuture(@NotNull Function code, @NotNull String progressTitle, @NotNull Project project, @Nullable Disposable parentDisposable, Object... coalesceBy) { - this.code = code; - this.progressTitle = progressTitle; - this.project = project; - this.parentDisposable = parentDisposable; - this.coalesceBy = coalesceBy; - this.nbAttempt = new AtomicInteger(0); - } - - protected void init() { - // if indexation is processing and not in Light Edit mode, we need to execute the promise in smart mode - var executeInSmartMode = !LightEdit.owns(project) && DumbService.getInstance(project).isDumb(); - var promise = nonBlockingReadActionPromise(executeInSmartMode); - bind(promise); - } - - /** - * Bind the given promise with the completable future. - * - * @param promise the promise which will execute the function code in a non blocking read action context - */ - private void bind(CancellablePromise> promise) { - this.nonBlockingReadActionPromise = promise; - // On error... - promise.onError(ex -> { - if (ex instanceof ProcessCanceledException || ex instanceof CancellationException) { - // Case 2: cancel the completable future - super.cancel(true); - } else { - // Other case..., mark the completable future as error - this.completeExceptionally(ex); - } - }); - // On success... - promise.onSuccess(value -> { - if (value.error != null) { - Exception ex = value.error; - // There were an error with IndexNotReadyException or ReadAction.CannotReadException - // Case 1: Attempt to retry the start of the promise - if (nbAttempt.incrementAndGet() >= MAX_ATTEMPT) { - // 1.1 Maximum number reached, mark the completable future as error - LOGGER.warn("Maximum number (" + MAX_ATTEMPT + ")" + " of attempts to start non blocking read action for '" + progressTitle + "' has been reached", ex); - this.completeExceptionally(new ExecutionAttemptLimitReachedException(progressTitle, MAX_ATTEMPT, ex)); - } else { - // Retry ... - // 1.2 Index are not ready or the read action cannot be done, retry in smart mode... - LOGGER.warn("Restart non blocking read action for '" + progressTitle + "' with attempt " + nbAttempt.get() + "/" + MAX_ATTEMPT + ".", ex); - var newPromise = nonBlockingReadActionPromise(true); - bind(newPromise); - } - } else { - this.complete(value.result); - } - }); - } - - /** - * Create a non blocking read action promise. - * - * @param executeInSmartMode true if the promise must be executed in smart mode and false otherwise. - * @return a non blocking read action promise - */ - @NotNull - private CancellablePromise> nonBlockingReadActionPromise(boolean executeInSmartMode) { - var indicator = createProgressIndicator(); - indicator.setText(progressTitle); - var action = ReadAction.nonBlocking(() -> - { - try { - R result = code.apply(indicator); - return new ResultOrError(result, null); - } catch (IndexNotReadyException | ReadAction.CannotReadException e) { - // When there is any exception, AsyncPromise report a log error. - // As we retry to execute the function code 5 times, we don't want to log this error - // To do that we catch the error and recreate a new promise on the promise.onSuccess - return new ResultOrError(null, e); - } - }) - .wrapProgress(indicator); - if (parentDisposable != null) { - action = action.expireWith(parentDisposable); // ex: promise is canceled when language client is stopped - } - if (executeInSmartMode) { - action = action.inSmartMode(project); - } - if (coalesceBy != null) { - action = action.coalesceBy(coalesceBy); - } - return action - .submit(AppExecutorUtil.getAppExecutorService()); - } - - protected ProgressIndicator createProgressIndicator() { - return new EmptyProgressIndicator(); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - if (nonBlockingReadActionPromise != null) { - // cancel the current promise - if (!nonBlockingReadActionPromise.isDone()) { - nonBlockingReadActionPromise.cancel(mayInterruptIfRunning); - } - } - return super.cancel(mayInterruptIfRunning); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/StringUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/StringUtils.java deleted file mode 100644 index d6084baae..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/StringUtils.java +++ /dev/null @@ -1,63 +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 com.redhat.devtools.intellij.lsp4ij.internal; - -public class StringUtils { - - /** - * Copied from https://github.com/apache/commons-lang/blob/24744a40b2c094945e542b71cc1fbf59caa0d70b/src/main/java/org/apache/commons/lang3/StringUtils.java#L3596C5-L3598C6 - * - * @param cs - * @return - */ - public static boolean isEmpty(final CharSequence cs) { - return cs == null || cs.length() == 0; - } - - /** - * Copied code from https://github.com/apache/commons-lang/blob/24744a40b2c094945e542b71cc1fbf59caa0d70b/src/main/java/org/apache/commons/lang3/StringUtils.java#L3714C5-L3716C6 - * @param cs - * @return - */ - public static boolean isNotBlank(final CharSequence cs) { - return !isBlank(cs); - } - - /** - * Copied code from https://github.com/apache/commons-lang/blob/24744a40b2c094945e542b71cc1fbf59caa0d70b/src/main/java/org/apache/commons/lang3/StringUtils.java#L3564 - * @param cs - * @return - */ - public static boolean isBlank(final CharSequence cs) { - final int strLen = length(cs); - if (strLen == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(cs.charAt(i))) { - return false; - } - } - return true; - } - - /** - * Copied code from https://github.com/apache/commons-lang/blob/24744a40b2c094945e542b71cc1fbf59caa0d70b/src/main/java/org/apache/commons/lang3/StringUtils.java#L5281 - * @param cs - * @return - */ - public static int length(final CharSequence cs) { - return cs == null ? 0 : cs.length(); - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java deleted file mode 100644 index b6815a315..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/internal/SupportedFeatures.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022-3 Cocotec Ltd 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: - * Ahmed Hussain (Cocotec Ltd) - initial implementation - * - *******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.internal; - -import java.util.Arrays; -import java.util.List; - -import org.eclipse.lsp4j.*; - -import javax.annotation.Nonnull; - -/** - * - */ -public class SupportedFeatures { - - public static @Nonnull TextDocumentClientCapabilities getTextDocumentClientCapabilities() { - final var textDocumentClientCapabilities = new TextDocumentClientCapabilities(); - - // Code Action support - final var 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"))); - textDocumentClientCapabilities.setCodeAction(codeAction); - - // Code Lens support - textDocumentClientCapabilities.setCodeLens(new CodeLensCapabilities()); - - // Inlay Hint support - textDocumentClientCapabilities.setInlayHint(new InlayHintCapabilities()); - - // TODO : support textDocument/colorPresentation - // textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities()); - - // Completion support - final var completionItemCapabilities = new CompletionItemCapabilities(Boolean.TRUE); - completionItemCapabilities - .setDocumentationFormat(Arrays.asList(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT)); - completionItemCapabilities.setInsertTextModeSupport(new CompletionItemInsertTextModeSupportCapabilities(List.of(InsertTextMode.AsIs, InsertTextMode.AdjustIndentation))); - - completionItemCapabilities.setResolveSupport(new CompletionItemResolveSupportCapabilities(List.of("documentation" /*, "detail", "additionalTextEdits" */))); - CompletionCapabilities completionCapabilities = new CompletionCapabilities(completionItemCapabilities); - completionCapabilities.setCompletionList(new CompletionListCapabilities(List.of("editRange"))); - textDocumentClientCapabilities.setCompletion(completionCapabilities); - - // Definition support - final var definitionCapabilities = new DefinitionCapabilities(); - definitionCapabilities.setLinkSupport(Boolean.TRUE); - textDocumentClientCapabilities.setDefinition(definitionCapabilities); - - // TODO support type definition - //final var typeDefinitionCapabilities = new TypeDefinitionCapabilities(); - //typeDefinitionCapabilities.setLinkSupport(Boolean.TRUE); - //textDocumentClientCapabilities.setTypeDefinition(typeDefinitionCapabilities); - - // DocumentHighlight support - textDocumentClientCapabilities.setDocumentHighlight(new DocumentHighlightCapabilities()); - - // DocumentLink support - textDocumentClientCapabilities.setDocumentLink(new DocumentLinkCapabilities()); - - // TODO : support textDocument/documentSymbol - /** final var 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); - **/ - // TODO : support textDocument/foldingRange - // textDocumentClientCapabilities.setFoldingRange(new FoldingRangeCapabilities()); - // TODO : support textDocument/formatting - // textDocumentClientCapabilities.setFormatting(new FormattingCapabilities(Boolean.TRUE)); - - // Hover support - final var hoverCapabilities = new HoverCapabilities(); - hoverCapabilities.setContentFormat(Arrays.asList(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT)); - textDocumentClientCapabilities.setHover(hoverCapabilities); - - // TODO: support onTypeFormatting - // textDocumentClientCapabilities.setOnTypeFormatting(null); // TODO - // TODO : support textDocument/rangeFormatting - // textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities()); - // TODO : support textDocument/references - // textDocumentClientCapabilities.setReferences(new ReferencesCapabilities()); - // TODO : support textDocument/rename - //final var renameCapabilities = new RenameCapabilities(); - //renameCapabilities.setPrepareSupport(true); - //textDocumentClientCapabilities.setRename(renameCapabilities); - // TODO : support textDocument/signatureHelp - // textDocumentClientCapabilities.setSignatureHelp(new SignatureHelpCapabilities()); - - // Synchronization support - textDocumentClientCapabilities - .setSynchronization(new SynchronizationCapabilities(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)); - - // TODO - // SelectionRangeCapabilities selectionRange = new SelectionRangeCapabilities(); - // textDocumentClientCapabilities.setSelectionRange(selectionRange); - return textDocumentClientCapabilities; - } - - public static @Nonnull WorkspaceClientCapabilities getWorkspaceClientCapabilities() { - final var workspaceClientCapabilities = new WorkspaceClientCapabilities(); - workspaceClientCapabilities.setApplyEdit(Boolean.TRUE); - // TODO - // workspaceClientCapabilities.setConfiguration(Boolean.TRUE); - workspaceClientCapabilities.setExecuteCommand(new ExecuteCommandCapabilities(Boolean.TRUE)); - // TODO - // workspaceClientCapabilities.setSymbol(new SymbolCapabilities(Boolean.TRUE)); - workspaceClientCapabilities.setWorkspaceFolders(Boolean.TRUE); - WorkspaceEditCapabilities editCapabilities = new WorkspaceEditCapabilities(); - editCapabilities.setDocumentChanges(Boolean.TRUE); - // TODO - // editCapabilities.setResourceOperations(Arrays.asList(ResourceOperationKind.Create, - // ResourceOperationKind.Delete, ResourceOperationKind.Rename)); - // TODO - // editCapabilities.setFailureHandling(FailureHandlingKind.Undo); - workspaceClientCapabilities.setWorkspaceEdit(editCapabilities); - workspaceClientCapabilities.setDidChangeWatchedFiles(new DidChangeWatchedFilesCapabilities(Boolean.TRUE)); - return workspaceClientCapabilities; - } - - public static WindowClientCapabilities getWindowClientCapabilities() { - final var windowClientCapabilities = new WindowClientCapabilities(); - //windowClientCapabilities.setShowDocument(new ShowDocumentCapabilities(true)); - //windowClientCapabilities.setWorkDoneProgress(true); - //windowClientCapabilities.setShowMessage(new WindowShowMessageRequestCapabilities()); - return windowClientCapabilities; - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleListener.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleListener.java deleted file mode 100644 index 391b545f4..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleListener.java +++ /dev/null @@ -1,48 +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 - *******************************************************************************/ -/******************************************************************************* - * Copyright (c) 2023 Red Hat Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 - * which is available at https://www.apache.org/licenses/LICENSE-2.0. - * - * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 - * - * Contributors: - * Red Hat Inc. - initial API and implementation - *******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.lifecycle; - -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.messages.Message; - -/** - * Language server lifecycle listener - * - * @author Angelo ZERR - */ -public interface LanguageServerLifecycleListener { - - void handleStatusChanged(LanguageServerWrapper languageServer); - - void handleLSPMessage(Message message, MessageConsumer consumer, LanguageServerWrapper languageServer); - - void handleError(LanguageServerWrapper languageServer, Throwable exception); - - void dispose(); - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java deleted file mode 100644 index f70871167..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java +++ /dev/null @@ -1,107 +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 com.redhat.devtools.intellij.lsp4ij.lifecycle; - -import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.eclipse.lsp4j.jsonrpc.MessageConsumer; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * Language server lifecycle manager - */ -public class LanguageServerLifecycleManager { - - public static LanguageServerLifecycleManager getInstance(@NotNull Project project) { - return project.getService(LanguageServerLifecycleManager.class); - } - - private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServerLifecycleManager.class);//$NON-NLS-1$ - - private final Collection listeners; - - private boolean disposed; - - public LanguageServerLifecycleManager() { - this(new ConcurrentLinkedQueue<>()); - } - - public LanguageServerLifecycleManager(Collection listeners) { - this.listeners = listeners; - } - - public void addLanguageServerLifecycleListener(LanguageServerLifecycleListener listener) { - this.listeners.add(listener); - } - - public void removeLanguageServerLifecycleListener(LanguageServerLifecycleListener listener) { - this.listeners.remove(listener); - } - - public void onStatusChanged(LanguageServerWrapper languageServer) { - if (isDisposed()) { - return; - } - for (LanguageServerLifecycleListener listener : this.listeners) { - try { - listener.handleStatusChanged(languageServer); - } catch (Exception e) { - LOGGER.error("Error while status changed of the language server '" + languageServer.serverDefinition.id + "'", e); - } - } - } - - public void logLSPMessage(Message message, MessageConsumer consumer, LanguageServerWrapper languageServer) { - if (isDisposed()) { - return; - } - for (LanguageServerLifecycleListener listener : this.listeners) { - try { - listener.handleLSPMessage(message, consumer, languageServer); - } catch (Exception e) { - LOGGER.error("Error while handling LSP message of the language server '" + languageServer.serverDefinition.id + "'", e); - } - } - } - - public void onError(LanguageServerWrapper languageServer, Throwable exception) { - if (isDisposed()) { - return; - } - for (LanguageServerLifecycleListener listener : this.listeners) { - try { - listener.handleError(languageServer, exception); - } catch (Exception e) { - LOGGER.error("Error while handling error of the language server '" + languageServer.serverDefinition.id + "'", e); - } - } - } - public boolean isDisposed() { - return disposed; - } - - public void dispose() { - disposed = true; - listeners.stream().forEach(LanguageServerLifecycleListener::dispose); - listeners.clear(); - } - - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/NullLanguageServerLifecycleManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/NullLanguageServerLifecycleManager.java deleted file mode 100644 index ffac57ad2..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/NullLanguageServerLifecycleManager.java +++ /dev/null @@ -1,28 +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 com.redhat.devtools.intellij.lsp4ij.lifecycle; - -import java.util.Collections; - -/** - * Language server lifecycle manager which does nothing. - */ -public class NullLanguageServerLifecycleManager extends LanguageServerLifecycleManager { - - public static final LanguageServerLifecycleManager INSTANCE = new NullLanguageServerLifecycleManager(); - - private NullLanguageServerLifecycleManager() { - super(Collections.emptyList()); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActionIntentionAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActionIntentionAction.java deleted file mode 100644 index 2a5ea6c5a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActionIntentionAction.java +++ /dev/null @@ -1,166 +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 com.redhat.devtools.intellij.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 com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.CodeActionOptions; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.ServerCapabilities; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.NotNull; - -import static com.redhat.devtools.intellij.lsp4ij.operations.codeactions.LSPLazyCodeActions.NO_CODEACTION_AT_INDEX; - -/** - * The lazy IJ Quick fix. - */ -public class LSPLazyCodeActionIntentionAction implements IntentionAction { - - private final LSPLazyCodeActions lazyCodeActions; - - private final int index; - private Either action; - private CodeAction codeAction; - - private String title; - private Command command; - private String familyName; - - public LSPLazyCodeActionIntentionAction(LSPLazyCodeActions lazyCodeActions, int index) { - this.lazyCodeActions = lazyCodeActions; - this.index = index; - } - - @Override - public @IntentionName @NotNull String getText() { - loadCodeActionIfNeeded(); - return title; - } - - @Override - public @NotNull @IntentionFamilyName String getFamilyName() { - loadCodeActionIfNeeded(); - return familyName; - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - loadCodeActionIfNeeded(); - return isValidCodeAction(); - } - - @Override - public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - String serverId = getLanguageServerWrapper().serverDefinition.id; - if (codeAction != null) { - if (codeAction.getEdit() == null && codeAction.getCommand() == null && isCodeActionResolveSupported()) { - // Unresolved code action "edit" property. Resolve it. - getLanguageServerWrapper().getInitializedServer() - .thenApply(ls -> - ls.getTextDocumentService().resolveCodeAction(codeAction) - .thenAccept(resolved -> { - ApplicationManager.getApplication().invokeLater(() -> { - DocumentUtil.writeInRunUndoTransparentAction(() -> { - apply(resolved != null ? resolved : codeAction, project, file, serverId); - }); - }); - }) - ); - } else { - apply(codeAction, project, file, serverId); - } - } else if (command != null) { - executeCommand(command, project, file, serverId); - } else { - // Should never get here - } - } - - private void apply(CodeAction codeaction, @NotNull Project project, PsiFile file, String serverId) { - if (codeaction != null) { - if (codeaction.getEdit() != null) { - LSPIJUtils.applyWorkspaceEdit(codeaction.getEdit(), codeaction.getTitle()); - } - if (codeaction.getCommand() != null) { - executeCommand(codeaction.getCommand(), project, file, serverId); - } - } - } - - private void executeCommand(Command command, @NotNull Project project, PsiFile file, String serverId) { - CommandExecutor.executeCommand(project, command, LSPIJUtils.toUri(file), serverId); - } - - private LanguageServerWrapper getLanguageServerWrapper() { - return lazyCodeActions.getLanguageServerWrapper(); - } - - private boolean isCodeActionResolveSupported() { - ServerCapabilities capabilities = getLanguageServerWrapper().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 boolean startInWriteAction() { - return true; - } - - private void loadCodeActionIfNeeded() { - if (action != null) { - // The LSP code action has been already loaded. - return; - } - // Try to get the LSP code action from the given indes - this.action = lazyCodeActions.getCodeActionAt(index); - if (isValidCodeAction()) { - if (action.isRight()) { - codeAction = action.getRight(); - title = action.getRight().getTitle(); - familyName = StringUtils.isNotBlank(codeAction.getKind()) ? codeAction.getKind() : "LSP QuickFix"; - } else { - command = action.getLeft(); - title = action.getRight().getTitle(); - familyName = "LSP Command"; - } - } - } - - private boolean isValidCodeAction() { - return action != null && !NO_CODEACTION_AT_INDEX.equals(action); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActions.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActions.java deleted file mode 100644 index 678440730..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codeactions/LSPLazyCodeActions.java +++ /dev/null @@ -1,213 +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 com.redhat.devtools.intellij.lsp4ij.operations.codeactions; - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.redhat.devtools.intellij.lsp4ij.CompletableFutures; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.Nullable; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * This class returns 10 IJ {@link LSPLazyCodeActionIntentionAction} which does nothing. It loads the LSP code actions - * for the given diagnostic only when user triggers the quick fixes for the diagnostic. - * - * @author Angelo ZERR - */ -public class LSPLazyCodeActions { - - public static final Either NO_CODEACTION_AT_INDEX = Either.forLeft(new Command()); - - private static final int NB_LAZY_CODE_ACTIONS = 10; - - private static final long LSP_REQUEST_CODEACTION_TIMEOUT = 20L; // wait for 20ms to request the LSP textDocument/codeAction - - // The diagnostic - private final Diagnostic diagnostic; - - // The virtual file - private final VirtualFile file; - - // The language server which has reported the diagnostic - private final LanguageServerWrapper languageServerWrapper; - - // List of lazy code actions - private final List codeActions; - - // LSP code actions request used to load code action for the diagnostic. - private CompletableFuture>> lspCodeActionRequest = null; - private Boolean refreshValidation; - - public LSPLazyCodeActions(Diagnostic diagnostic, VirtualFile file, LanguageServerWrapper languageServerWrapper) { - this.diagnostic = diagnostic; - this.file = file; - this.languageServerWrapper = languageServerWrapper; - // Create 10 lazy IJ quick fixes which does nothing (IntentAction#isAvailable returns false) - codeActions = new ArrayList<>(NB_LAZY_CODE_ACTIONS); - for (int i = 0; i < NB_LAZY_CODE_ACTIONS; i++) { - codeActions.add(new LSPLazyCodeActionIntentionAction(this, i)); - } - } - - /** - * Returns the LSP CodeAction for the given index and null otherwise. - * - * @param index the code action index. - * @return the LSP CodeAction for the given index and null otherwise. - */ - public @Nullable Either getCodeActionAt(int index) { - List> codeActions = getOrLoadCodeActions(); - if (codeActions == null) { - if (refreshValidation != null && refreshValidation) { - // On the first TimeoutException, the IJ validator will be refreshed as soon as the code actions - // will be loaded in order to refresh quick fixes by adding a new CompletableFuture with thenApply. - lspCodeActionRequest.thenAccept(inn -> { - ReadAction.compute(() -> { - // Here The LSP request textDocument/codeAction takes some times - if (codeActions != null) { - // The code actions has been loaded, don't refresh the IJ validator. - return null; - } - Project project = languageServerWrapper.getProject(); - PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - DaemonCodeAnalyzer.getInstance(project).restart(psiFile); - return null; - }); - }); - // No need to add a new CompletableFuture which refreshes the IJ validator on the next TimeoutException - refreshValidation = false; - } - } - if (codeActions != null) { - if (codeActions.size() > index) { - // The LSP code actions are loaded and it matches the given index - return codeActions.get(index); - } - return NO_CODEACTION_AT_INDEX; - } - return null; - } - - @Nullable - private List> getOrLoadCodeActions() { - if (lspCodeActionRequest == null) { - // Create LSP textDocument/codeAction request - lspCodeActionRequest = loadCodeActionsFor(diagnostic); - } - // Get the response of the LSP textDocument/codeAction request with TimeOut. - List> codeActions = null; - try { - codeActions = lspCodeActionRequest.get(LSP_REQUEST_CODEACTION_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - refreshValidation = true; - } catch (Exception e) { - // Do nothing - } - return codeActions; - } - - /** - * 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) { - return Collections.emptyList(); - } - return codeActions; - }); - }); - }); - } - - /** - * Create the LSP code action parameters for the given diagnostic and file. - * - * @param diagnostic the diagnostic. - * @param file the file. - * @return the LSP code action parameters for the given diagnostic and file. - */ - 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; - } - - /** - * Returns the language server which has reported the diagnostic. - * - * @return the language server which has reported the diagnostic. - */ - public LanguageServerWrapper getLanguageServerWrapper() { - return languageServerWrapper; - } - - /** - * Returns the list of lazy code actions. - * - * @return the list of lazy code actions. - */ - public List getCodeActions() { - return codeActions; - } - - /** - * Cancel if needed the LSP request textDocument/codeAction - */ - public void cancel() { - if (lspCodeActionRequest != null && !lspCodeActionRequest.isDone()) { - lspCodeActionRequest.cancel(true); - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codelens/LSPCodelensInlayProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codelens/LSPCodelensInlayProvider.java deleted file mode 100644 index f503602e4..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/codelens/LSPCodelensInlayProvider.java +++ /dev/null @@ -1,170 +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 com.redhat.devtools.intellij.lsp4ij.operations.codelens; - -import com.intellij.codeInsight.hints.InlayHintsSink; -import com.intellij.codeInsight.hints.presentation.InlayPresentation; -import com.intellij.codeInsight.hints.presentation.PresentationFactory; -import com.intellij.codeInsight.hints.presentation.SequencePresentation; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.AbstractLSPInlayProvider; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -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 java.awt.*; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -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; - -/** - * LSP textDocument/codeLens support. - */ -public class LSPCodelensInlayProvider extends AbstractLSPInlayProvider { - - private static final Key CANCELLATION_SUPPORT_KEY = new Key<>(LSPCodelensInlayProvider.class.getName() + "-CancellationSupport"); - - - public LSPCodelensInlayProvider() { - super(CANCELLATION_SUPPORT_KEY); - } - - @Override - protected void doCollect(@NotNull VirtualFile file, @NotNull Project project, @NotNull Editor editor, @NotNull PresentationFactory factory, @NotNull InlayHintsSink inlayHintsSink, @NotNull CancellationSupport cancellationSupport) throws InterruptedException { - Document document = editor.getDocument(); - URI fileUri = LSPIJUtils.toUri(file); - CodeLensParams param = new CodeLensParams(new TextDocumentIdentifier(fileUri.toASCIIString())); - BlockingDeque> pairs = new LinkedBlockingDeque<>(); - - CompletableFuture future = collect(file, project, param, pairs, cancellationSupport); - List>> codeLenses = createCodeLenses(document, pairs, future, cancellationSupport); - codeLenses.stream() - .collect(Collectors.groupingBy(p -> p.first)) - .forEach((offset, list) -> - inlayHintsSink.addBlockElement( - offset, - true, - true, - 0, - toPresentation(editor, offset, list, factory, cancellationSupport)) - ); - } - - @NotNull - private List>> createCodeLenses(Document document, BlockingDeque> pairs, CompletableFuture future, CancellationSupport cancellationSupport) throws InterruptedException { - List>> codelenses = new ArrayList<>(); - 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(), document); - codelenses.add(Pair.create(offset, pair)); - } - } - return codelenses; - } - - private @NotNull CompletableFuture collect(@NotNull VirtualFile file, @NotNull Project project, @NotNull CodeLensParams param, @NotNull BlockingDeque> pairs, @NotNull CancellationSupport cancellationSupport) { - return LanguageServiceAccessor.getInstance(project) - .getLanguageServers(file, capabilities -> capabilities.getCodeLensProvider() != null) - .thenComposeAsync(languageServers -> - cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> - cancellationSupport.execute(languageServer.getServer().getTextDocumentService().codeLens(param)) - .thenAcceptAsync(codeLenses -> { - // textDocument/codeLens may return null - if (codeLenses != null) { - codeLenses.stream() - .filter(Objects::nonNull) - .forEach(codeLens -> { - if (getCodeLensContent(codeLens) != null) { - // The codelens content is filled, display it - pairs.add(new Pair(codeLens, languageServer.getServer())); - } - }); - } - })) - .toArray(CompletableFuture[]::new)))); - } - - private InlayPresentation toPresentation( - @NotNull Editor editor, - int offset, - @NotNull List>> elements, - @NotNull PresentationFactory factory, - @NotNull CancellationSupport cancellationSupport) { - 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 -> { - cancellationSupport.checkCanceled(); - CodeLens codeLens = p.second.first; - LanguageServer languageServer = p.second.second; - InlayPresentation text = factory.smallText(getCodeLensContent(codeLens)); - if (!hasCommand(codeLens)) { - // No command, create a simple text inlay hint - presentations.add(text); - } else { - // Codelens defines a Command, create a clickable inlay hint - InlayPresentation clickableText = factory.referenceOnHover(text, (event, translated) -> - executeClientCommand(p.second.second, p.second.first, (Component) event.getSource(), editor.getProject()) - ); - presentations.add(clickableText); - } - 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, capabilities -> - Boolean.TRUE.equals(capabilities.getCodeLensProvider().getResolveProvider())) - ) { - languageServer.getTextDocumentService().resolveCodeLens(codeLens).thenAcceptAsync(resolvedCodeLens -> - executeClientCommand(source, resolvedCodeLens.getCommand()) - ); - } else { - executeClientCommand(source, codeLens.getCommand()); - } - } - - private static boolean hasCommand(CodeLens codeLens) { - Command command = codeLens.getCommand(); - return (command != null && command.getCommand() != null && !command.getCommand().isEmpty()); - } - - private static String getCodeLensContent(CodeLens codeLens) { - Command command = codeLens.getCommand(); - if (command == null || command.getTitle().isEmpty()) { - return null; - } - return command.getTitle(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionPrefix.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionPrefix.java deleted file mode 100644 index 50016260f..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionPrefix.java +++ /dev/null @@ -1,119 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion; - -import com.intellij.openapi.editor.Document; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.CompletionProposalTools.getCompletionPrefix; - -/** - * Completion prefix provides the capability to compute the prefix where the completion has been triggered and by using the LSP {@link org.eclipse.lsp4j.TextEdit} information. - *

- * It caches the prefix compute for a given TextEdit to avoid computing the prefix for all completion items which have the same {@link org.eclipse.lsp4j.TextEdit} - */ -public class CompletionPrefix { - - private final int completionOffset; - private final Position completionPos; - private final Document document; - - private final Map prefixCache; - - public CompletionPrefix(int completionOffset, Document document) { - this.completionOffset = completionOffset; - this.document = document; - this.completionPos = LSPIJUtils.toPosition(completionOffset, document); - this.prefixCache = new HashMap<>(); - } - - public int getCompletionOffset() { - return completionOffset; - } - - public Document getDocument() { - return document; - } - - /** - * Returns the proper prefix from the given text range and label/filterText defined in the given completion item and null otherwise. - * - * @param textEditRange the completion item edit range. - * @param item the completion item. - * @return the proper prefix from the given text range and label/filterText defined in the given completion item and null otherwise. - */ - public @Nullable String getPrefixFor(@NotNull Range textEditRange, CompletionItem item) { - // Try to get the computed prefix from the cache - String prefix = prefixCache.get(textEditRange); - if (prefix == null && !prefixCache.containsKey(textEditRange)) { - // Compute the prefix which can be null - // ex : {#ea|ch will return {#ea - prefix = getCompletionPrefix(completionPos, textEditRange, document); - prefixCache.put(textEditRange, prefix); - } - if (prefix == null) { - // - null prefix - return prefix; - } - String filterText = getAccurateFilterText(item); - if (filterText == null) { - // - no filter text defined - return prefix; - } - // Filter text is defined here (ex : {#each - // In case of Qute, completion item has the following data: - // - label = each - // - filterText = {#each - // - text edit = [{#each] - // if completion is triggered in {#ea|ch - // prefix will be {#ea - // Here we need to remove the '{#' defined by the filter text to return the proper prefix 'ea' - int index = 0; - for (int i = 0; i < Math.min(prefix.length(), filterText.length()); i++) { - if (prefix.charAt(i) == filterText.charAt(i)) { - index++; - } else { - break; - } - } - if (index > 0) { - // Remove '{#' from '{#ea' to get the proper prefix 'ea' - prefix = prefix.substring(index); - } - return prefix; - } - - private static String getAccurateFilterText(CompletionItem item) { - String filterText = item.getFilterText(); - if (StringUtils.isBlank(filterText)) { - return null; - } - String label = item.getLabel(); - if (label.startsWith(filterText)) { - // The label starts with filterText, ignore the filter to avoid computing a bad prefix - return null; - } - return filterText; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionProposalTools.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionProposalTools.java deleted file mode 100644 index c3a4ba458..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/CompletionProposalTools.java +++ /dev/null @@ -1,103 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion; - -import com.intellij.lang.Language; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.project.Project; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.impl.PsiManagerEx; -import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetIndentOptions; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.util.Ranges; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Utilities for LSP completion. - */ -public final class CompletionProposalTools { - - private CompletionProposalTools() { - // to avoid instances, requested by sonar - } - - // ----------------- Prefix utilities - - public static @Nullable String getCompletionPrefix(@NotNull Position completionPos, @NotNull Range textEditRange, @NotNull Document document) { - if (Ranges.containsPosition(textEditRange, completionPos)) { - // ex : {#ea|ch - // here the prefix to return should be {#ea - int lineStartOffset = document.getLineStartOffset(completionPos.getLine()); - int startOffset = lineStartOffset + textEditRange.getStart().getCharacter(); - int endOffset = lineStartOffset + completionPos.getCharacter(); - return document.getCharsSequence().subSequence(startOffset, endOffset).toString(); - } else { - return null; - } - } - - // ----------------- Snippet utilities - - /** - * Returns the indent options to use to format the given snippet text block and null otherwise. - * - * @param snippetTextBlock the snippet text block (ex : foo\t\nbar). - * @param file the file where thesnippet must be inserted. - * @return the indent options to use to format the given snippet text block and null otherwise. - */ - public static @Nullable LspSnippetIndentOptions createLspIndentOptions(String snippetTextBlock, PsiFile file) { - if (LspSnippetIndentOptions.shouldBeFormatted(snippetTextBlock)) { - // Get global line separator settings - CodeStyleSettings settings = CodeStyleSettings.getDefaults(); - String lineSeparator = settings.getLineSeparator(); - CommonCodeStyleSettings.@NotNull IndentOptions indentOptions = getIndentOptions(file, settings); - boolean insertSpaces = !indentOptions.USE_TAB_CHARACTER; - int tabSize = indentOptions.TAB_SIZE; - return new LspSnippetIndentOptions(tabSize, insertSpaces, lineSeparator); - } - return null; - } - - /** - * Returns the Intellij indent options for the given file and the global indent options otherwise. - * - * @param file the file. - * @param settings the global code style settings. - * @return indent options for the given file and the global indent options otherwise. - */ - private @NotNull - static CommonCodeStyleSettings.@NotNull IndentOptions getIndentOptions(PsiFile file, CodeStyleSettings settings) { - Project project = file.getProject(); - FileViewProvider provider = PsiManagerEx.getInstanceEx(project).findViewProvider(file.getVirtualFile()); - if (provider instanceof TemplateLanguageFileViewProvider) { - // get indent options of the language - Language language = ((TemplateLanguageFileViewProvider) provider).getTemplateDataLanguage(); - CommonCodeStyleSettings.IndentOptions indentOptions = settings.getLanguageIndentOptions(language); - if (indentOptions != null) { - return indentOptions; - } - } - Language language = provider.getBaseLanguage(); - CommonCodeStyleSettings.IndentOptions indentOptions = settings.getLanguageIndentOptions(language); - if (indentOptions != null) { - return indentOptions; - } - return settings.getIndentOptions(); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionConfidence.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionConfidence.java deleted file mode 100644 index 01ce243b4..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionConfidence.java +++ /dev/null @@ -1,31 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion; - -import com.intellij.codeInsight.completion.CompletionConfidence; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.util.ThreeState; -import org.jetbrains.annotations.NotNull; - -/** - * Provides the capability to open completion anywhere. - */ -public class LSPCompletionConfidence extends CompletionConfidence { - - @Override - public @NotNull ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) { - return ThreeState.NO; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java deleted file mode 100644 index b462ad97a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java +++ /dev/null @@ -1,189 +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 com.redhat.devtools.intellij.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 com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerItem; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URI; -import java.util.List; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * LSP completion contributor. - */ -public class LSPCompletionContributor extends CompletionContributor { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPCompletionContributor.class); - - @Override - public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { - PsiFile psiFile = parameters.getOriginalFile(); - VirtualFile file = psiFile.getVirtualFile(); - if (file == null) { - return; - } - - Editor editor = parameters.getEditor(); - Document document = editor.getDocument(); - Project project = psiFile.getProject(); - int offset = parameters.getOffset(); - URI uri = LSPIJUtils.toUri(file); - - ProgressManager.checkCanceled(); - - final CancellationSupport cancellationSupport = new CancellationSupport(); - try { - CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(file, project); - cancellationSupport.execute(completionLanguageServersFuture); - ProgressManager.checkCanceled(); - - /* - 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. - */ - CompletionParams params = LSPIJUtils.toCompletionParams(uri, offset, document); - BlockingDeque, CompletionList>, LanguageServerItem>> proposals = new LinkedBlockingDeque<>(); - - CompletableFuture future = completionLanguageServersFuture - .thenComposeAsync(languageServers -> cancellationSupport.execute( - CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> - cancellationSupport.execute(languageServer.getServer().getTextDocumentService().completion(params)) - .thenAcceptAsync(completion -> proposals.add(new Pair<>(completion, languageServer)))) - .toArray(CompletableFuture[]::new)))); - - ProgressManager.checkCanceled(); - while (!future.isDone() || !proposals.isEmpty()) { - ProgressManager.checkCanceled(); - Pair, CompletionList>, LanguageServerItem> pair = proposals.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - Either, CompletionList> completion = pair.getFirst(); - if (completion != null) { - CompletionPrefix completionPrefix = new CompletionPrefix(offset, document); - addCompletionItems(psiFile, editor, completionPrefix, pair.getFirst(), pair.getSecond(), result, cancellationSupport); - } - } - } - } catch (ProcessCanceledException cancellation) { - cancellationSupport.cancel(); - throw cancellation; - } catch (RuntimeException | InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - result.addElement(createErrorProposal(offset, e)); - } - } - - private void addCompletionItems(PsiFile file, Editor editor, CompletionPrefix completionPrefix, Either, - CompletionList> completion, LanguageServerItem languageServer, @NotNull CompletionResultSet result, CancellationSupport cancellationSupport) { - CompletionItemDefaults itemDefaults = null; - List items = null; - if (completion.isLeft()) { - items = completion.getLeft(); - } else { - CompletionList completionList = completion.getRight(); - itemDefaults = completionList.getItemDefaults(); - items = completionList.getItems(); - } - for (var item : items) { - if (StringUtils.isBlank(item.getLabel())) { - // Invalid completion Item, ignore it - continue; - } - cancellationSupport.checkCanceled(); - // Create lookup item - var lookupItem = createLookupItem(file, editor, completionPrefix.getCompletionOffset(), item, itemDefaults, languageServer); - // Group it by using completion item kind - var groupedLookupItem = PrioritizedLookupElement.withGrouping(lookupItem, item.getKind().getValue()); - // Compute the prefix - String prefix = completionPrefix.getPrefixFor(lookupItem.getTextEditRange(), item); - if (prefix != null) { - // Add the IJ completion item (lookup item) by using the computed prefix - result.withPrefixMatcher(prefix) - .caseInsensitive() // set case-insensitive to search Java class which starts with upper case - .addElement(groupedLookupItem); - } else { - // Should happen rarely, only when text edit is for multi-lines or if completion is triggered outside the text edit range. - // Add the IJ completion item (lookup item) which will use the IJ prefix - result.addElement(groupedLookupItem); - } - } - } - - private static LSPCompletionProposal createLookupItem(PsiFile file, Editor editor, int offset, - CompletionItem item, - CompletionItemDefaults itemDefaults, LanguageServerItem languageServer) { - // Update text edit range with item defaults if needed - updateWithItemDefaults(item, itemDefaults); - return new LSPCompletionProposal(file, editor, offset, item, languageServer); - } - - private static void updateWithItemDefaults(CompletionItem item, CompletionItemDefaults itemDefaults) { - if (itemDefaults == null) { - return; - } - String itemText = item.getTextEditText(); - if (itemDefaults.getEditRange() != null && itemText != null) { - if (itemDefaults.getEditRange().isLeft()) { - Range defaultRange = itemDefaults.getEditRange().getLeft(); - if (defaultRange != null) { - item.setTextEdit(Either.forLeft(new TextEdit(defaultRange, itemText))); - } - } else { - InsertReplaceRange defaultInsertReplaceRange = itemDefaults.getEditRange().getRight(); - if (defaultInsertReplaceRange != null) { - item.setTextEdit(Either.forRight(new InsertReplaceEdit(itemText, defaultInsertReplaceRange.getInsert(), defaultInsertReplaceRange.getReplace()))); - } - } - } - } - - - private static LookupElement createErrorProposal(int offset, Exception ex) { - return LookupElementBuilder.create("Error while computing completion", ""); - } - - private static CompletableFuture> initiateLanguageServers(@NotNull VirtualFile file, @NotNull Project project) { - return LanguageServiceAccessor.getInstance(project).getLanguageServers(file, - capabilities -> { - CompletionOptions provider = capabilities.getCompletionProvider(); - if (provider != null) { - return true; - } - return false; - }); - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java deleted file mode 100644 index fc9ea5361..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionProposal.java +++ /dev/null @@ -1,394 +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 com.redhat.devtools.intellij.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.codeInsight.template.Template; -import com.intellij.codeInsight.template.TemplateManager; -import com.intellij.codeInsight.template.impl.TemplateImpl; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorModificationUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerItem; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetIndentOptions; -import org.eclipse.lsp4j.*; -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 java.net.URI; -import java.util.*; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.CompletionProposalTools.createLspIndentOptions; -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetVariableConstants.*; -import static com.redhat.devtools.intellij.lsp4ij.ui.IconMapper.getIcon; - -/** - * LSP completion lookup element. - */ -public class LSPCompletionProposal extends LookupElement { - private static final Logger LOGGER = LoggerFactory.getLogger(LSPCompletionProposal.class); - - private final CompletionItem item; - private final int initialOffset; - private final PsiFile file; - private final Boolean supportResolveCompletion; - private int currentOffset; - private int bestOffset; - private final Editor editor; - private final LanguageServerItem languageServer; - private String documentation; - - public LSPCompletionProposal(PsiFile file, Editor editor, int offset, CompletionItem item, LanguageServerItem languageServer) { - this.file = file; - this.item = item; - this.editor = editor; - this.languageServer = languageServer; - this.initialOffset = offset; - this.currentOffset = offset; - this.bestOffset = getPrefixCompletionStart(editor.getDocument(), offset); - ServerCapabilities serverCapabilities = languageServer.getServerWrapper().getServerCapabilities(); - this.supportResolveCompletion = serverCapabilities != null && serverCapabilities.getCompletionProvider() != null && serverCapabilities.getCompletionProvider().getResolveProvider(); - putUserData(CodeCompletionHandlerBase.DIRECT_INSERTION, true); - } - - @Override - public void handleInsert(@NotNull InsertionContext context) { - Template template = null; - if (item.getInsertTextFormat() == InsertTextFormat.Snippet) { - // Insert text has snippet syntax, ex : ${1:name} - String snippetContent = getInsertText(); - // Get the indentation settings - LspSnippetIndentOptions indentOptions = createLspIndentOptions(snippetContent, file); - // Load the insert text to build: - // - an IJ Template instance which will take care of replacement of placeholders - // - the insert text without placeholders - template = SnippetTemplateFactory.createTemplate(snippetContent, context.getProject(), name -> getVariableValue(name), indentOptions); - // Update the TextEdit with the content snippet content without placeholders - // ex : ${1:name} --> name - updateInsertText(template.getTemplateText()); - } - - // Apply all text edits - apply(context.getDocument(), context.getCompletionChar(), 0, context.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET)); - - if (template != null && ((TemplateImpl) template).getVariableCount() > 0) { - // LSP completion with snippet syntax, activate the inline template - context.setAddCompletionChar(false); - EditorModificationUtil.moveCaretRelatively(editor, -template.getTemplateText().length()); - TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), template); - } - } - - /** - * Returns the text content to insert coming from the LSP CompletionItem. - * - * @return the text content to insert coming from the LSP CompletionItem. - */ - 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; - } - - private void updateInsertText(String newText) { - Either eitherTextEdit = this.item.getTextEdit(); - if (eitherTextEdit != null) { - if (eitherTextEdit.isLeft()) { - eitherTextEdit.getLeft().setNewText(newText); - } else { - eitherTextEdit.getRight().setNewText(newText); - } - } - } - - 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 { - String subDoc = document.getText(new TextRange( - Math.max(0, completionOffset - insertText.length()), - Math.min(insertText.length(), completionOffset))); - 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; - } - - @Override - public Set getAllLookupStrings() { - if (StringUtils.isBlank(item.getFilterText())) { - return super.getAllLookupStrings(); - } - return new HashSet<>(Arrays.asList(item.getFilterText(), item.getLabel())); - } - - @NotNull - @Override - public String getLookupString() { - return item.getLabel(); - } - - private boolean isDeprecated() { - return (item.getTags() != null && item.getTags().contains(CompletionItemTag.Deprecated)) - || (item.getDeprecated() != null && item.getDeprecated().booleanValue()); - } - - @Override - public void renderElement(LookupElementPresentation presentation) { - presentation.setItemText(item.getLabel()); - presentation.setTypeText(item.getDetail()); - presentation.setIcon(getIcon(item.getKind())); - 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); - } - - 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)); - } - - // Execute custom command of the completion item if needed - Command command = item.getCommand(); - if (command != null) { - executeCustomCommand(command, LSPIJUtils.toUri(document)); - } - } catch (RuntimeException ex) { - LOGGER.warn(ex.getLocalizedMessage(), ex); - } - } - - - /** - * Execute custom command of the completion item. - * - * @param command - * @param documentUri - */ - private void executeCustomCommand(@NotNull Command command, URI documentUri) { - Project project = editor.getProject(); - // Execute custom command of the completion item. - LanguageServiceAccessor.getInstance(project) - .resolveServerDefinition(languageServer.getServer()).map(definition -> definition.id) - .ifPresent(id -> { - CommandExecutor.executeCommand(project, command, documentUri, id); - }); - - } - - public 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 CompletionItem getItem() { - return item; - } - - /** - * Return the result of the resolved LSP variable and null otherwise. - * - * @param variableName the variable name to resolve. - * @return the result of the resolved LSP variable and null otherwise. - */ - private @Nullable 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; - } - } - - public MarkupContent getDocumentation() { - if (item.getDocumentation() == null && supportResolveCompletion) { - try { - CompletionItem resolved = languageServer.getServer() - .getTextDocumentService() - .resolveCompletionItem(item) - .get(1000, TimeUnit.MILLISECONDS); - if (resolved != null) { - item.setDocumentation(resolved.getDocumentation()); - } - } catch (ExecutionException e) { - if (!(e.getCause() instanceof CancellationException)) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } catch (TimeoutException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - } - return getDocumentation(item.getDocumentation()); - } - - private static MarkupContent getDocumentation(Either documentation) { - if (documentation == null) { - return null; - } - if (documentation.isLeft()) { - String content = documentation.getLeft(); - return new MarkupContent(content, MarkupKind.PLAINTEXT); - } - return documentation.getRight(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/SnippetTemplateFactory.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/SnippetTemplateFactory.java deleted file mode 100644 index 55059f85c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/SnippetTemplateFactory.java +++ /dev/null @@ -1,48 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion; - -import com.intellij.codeInsight.template.Template; -import com.intellij.codeInsight.template.TemplateManager; -import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetIndentOptions; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetParser; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Function; - -/** - * Intellij {@link Template} factory to create the proper template structure (text segment, variables, etc) according the LSP snippet content. - * - * @author Angelo ZERR - * @see choices) { - String value = choices.isEmpty() ? "" : choices.get(0); - choice(value, choices); - } - - @Override - public void choice(String name, List choices) { - template.addVariable(new ConstantNode(name).withLookupStrings(choices), true); - } - - @Override - public void startPlaceholder(int index, String name, int level) { - variable(name); - } - - @Override - public void endPlaceholder(int level) { - - } - - @Override - public void variable(String name) { - String resolvedValue = super.resolveVariable(name); - if (resolvedValue != null) { - // ex : ${TM_SELECTED_TEXT} - // the TM_SELECTED_TEXT is resolved, we do a simple replacement - template.addVariable(new ConstantNode(resolvedValue), false); - } else { - if (existingVariables.contains(name)) { - // The variable (ex : ${name}) has already been declared, add a simple variable segment - // which will be updated by the previous add variable - template.addVariableSegment(name); - } else { - // The variable doesn't exists, add a variable which can be updated - // and which will replace other variables with the same name. - existingVariables.add(name); - template.addVariable(name, new ConstantNode(name), null, true, false); - } - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AbstractLspSnippetHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AbstractLspSnippetHandler.java deleted file mode 100644 index b1be57d5d..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AbstractLspSnippetHandler.java +++ /dev/null @@ -1,65 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import java.util.function.Function; - -/** - * Abstract class LSP snippet handler (aka SAXHandler). - * - * @author Angelo ZERR - * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax - */ -public abstract class AbstractLspSnippetHandler implements LspSnippetHandler { - - private final Function variableResolver; - - private final LspSnippetIndentOptions indentOptions; - - /** - * Abstract LSP snippet handler constructor. - * - * @param variableResolver the variable resolver. - * @param indentOptions the indent options to use to format text block which contains '\n' and '\t'. - */ - public AbstractLspSnippetHandler(Function variableResolver, LspSnippetIndentOptions indentOptions) { - this.variableResolver = variableResolver; - this.indentOptions = indentOptions; - } - - /** - * Replace '\n' and '\t' declared in the snippets according to the LSP client settings. - * - * @param text the text to format according to the LSP client settings. - * - * @return the result of '\n' and '\t' replacement declared in the snippets according to the LSP client settings. - */ - protected String formatText(String text) { - return indentOptions != null ? indentOptions.formatText(text) : text; - } - - /** - * Return the result of the resolved LSP variable and null otherwise. - * - * @param variableName the variable name to resolve. - * @return the result of the resolved LSP variable and null otherwise. - */ - protected String resolveVariable(String variableName) { - if (variableResolver == null || variableName == null || variableName.isEmpty()) { - return variableName; - } - return variableResolver.apply(variableName); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/DefaultLspSnippetHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/DefaultLspSnippetHandler.java deleted file mode 100644 index 0c23723da..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/DefaultLspSnippetHandler.java +++ /dev/null @@ -1,103 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import java.util.List; -import java.util.function.Function; - -/** - * Default LSP snippet handler which provides: - * - *

    - *
  • the snippet content without the placeholders.
  • - *
  • the start offset of each placeholders
  • - *
- * - * @author Angelo ZERR - */ -public class DefaultLspSnippetHandler extends AbstractLspSnippetHandler { - - private final StringBuilder templateContent; - - /** - * Default LSP snippet handler constructor. - * - * @param variableResolver the variable resolver. - * @param indentOptions the indent options to use to format text block which contains '\n' and '\t'. - */ - public DefaultLspSnippetHandler(Function variableResolver, LspSnippetIndentOptions indentOptions) { - super(variableResolver, indentOptions); - this.templateContent = new StringBuilder(); - } - - @Override - public void startSnippet() { - - } - - @Override - public void endSnippet() { - - } - - @Override - public void text(String text) { - appendContent(formatText(text)); - } - - @Override - public void tabstop(int index) { - - } - - @Override - public void choice(int index, List choices) { - String value = choices.isEmpty() ? "" : choices.get(0); - appendContent(value); - } - - @Override - public void choice(String name, List choices) { - appendContent(name); - } - - @Override - public void startPlaceholder(int index, String name, int level) { - appendContent(name); - } - - @Override - public void endPlaceholder(int level) { - - } - - @Override - public void variable(String name) { - appendContent(name); - } - - public String getTemplateContent() { - return templateContent.toString(); - } - - public int getCurrentOffset() { - return templateContent.length(); - } - - protected void appendContent(String content) { - if (content != null) { - templateContent.append(content); - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/Location.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/Location.java deleted file mode 100644 index 0d0707c2b..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/Location.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2016 EclipseSource. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - - -/** - * An immutable object that represents a location in the parsed text. - *

- * This code is a copy/paste from - * https://github.com/ralfstx/minimal-json/blob/master/com.eclipsesource.json/src/main/java/com/eclipsesource/json/Location.java - * adapted for LSP Snippet. - */ -public class Location { - - /** - * The absolute character index, starting at 0. - */ - public final int offset; - - /** - * The line number, starting at 1. - */ - public final int line; - - /** - * The column number, starting at 1. - */ - public final int column; - - Location(int offset, int line, int column) { - this.offset = offset; - this.column = column; - this.line = line; - } - - @Override - public String toString() { - return line + ":" + column; - } - - @Override - public int hashCode() { - return offset; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Location other = (Location) obj; - return offset == other.offset && column == other.column && line == other.line; - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetHandler.java deleted file mode 100644 index 6f07b8f88..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetHandler.java +++ /dev/null @@ -1,89 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import java.util.List; - -/** - * LSP snippet handler (aka SAXHandler). - * - * @author Angelo ZERR - * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax - */ -public interface LspSnippetHandler { - - /** - * On start snippet. - */ - void startSnippet(); - - /** - * On end snippet. - */ - void endSnippet(); - - /** - * On text block. - * - * @param text the text block. - */ - void text(String text); - - /** - * On tabstop (ex: $1}. - * - * @param index the tabstop index (ex:1) - */ - void tabstop(int index); - - /** - * On choice (ex : ${1|one,two,three|}). - * - * @param index the choice index (ex:1) - * @param choices the choices list (ex: [one,two,three]) - */ - void choice(int index, List choices); - - /** - * On choice (ex : ${two|one,two,three|}). - * - * @param name the choice name (ex:two) - * @param choices the choices list (ex: [one,two,three]) - */ - void choice(String name, List choices); - - /** - * On start placeholder (ex : {1:name}). - * - * @param index the placeholder index (ex:1) - * @param name the placeholder name (ex:name) - * @param level the placeholder level (1 for root and other for nested placeholder) - */ - void startPlaceholder(int index, String name, int level); - - /** - * On end place holder. - * - * @param level the placeholder level (1 for root and other for nested placeholder) - */ - void endPlaceholder(int level); - - /** - * On variable (ex : ${name} - * - * @param name the variable name (ex:name) - */ - void variable(String name); - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetIndentOptions.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetIndentOptions.java deleted file mode 100644 index 735950a3a..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetIndentOptions.java +++ /dev/null @@ -1,122 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -/** - * LSP snippet indent options used to replace LSP snippet content '\n' and '\t: - * - *

    - *
  • '\n' with the line separator settings of the LSP client.
  • - *
  • '\t' with the tab size settings of the LSP client if the LSP client settings has insert spaces.
  • - *
- */ -public class LspSnippetIndentOptions { - - private static final String CRLF = "\r\n"; - - private final int tabSize; - - private final boolean insertSpaces; - - private final String lineSeparator; - - private String replacementTab; - - public LspSnippetIndentOptions(int tabSize, boolean insertSpaces, String lineSeparator) { - this.tabSize = tabSize; - this.insertSpaces = insertSpaces; - this.lineSeparator = lineSeparator; - } - - public int getTabSize() { - return tabSize; - } - - public boolean isInsertSpaces() { - return insertSpaces; - } - - public String getLineSeparator() { - return lineSeparator; - } - - /** - * Replace '\n' and '\t' declared in the snippets according to the LSP client settings. - * - * @param text the text to format according to the LSP client settings. - * - * @return the result of '\n' and '\t' replacement declared in the snippets according to the LSP client settings. - */ - protected String formatText(String text) { - if (!shouldBeFormatted(text)) { - return text; - } - StringBuilder formattedText = new StringBuilder(); - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - switch (c) { - case '\t': - if (!insertSpaces) { - formattedText.append(c); - } else { - formattedText.append(getSpacesReplacement()); - } - break; - case '\n': - if (shouldIgnoreLineSeparatorSettings(text, i)) { - formattedText.append(c); - } else { - formattedText.append(lineSeparator); - } - break; - default: - formattedText.append(c); - } - } - return formattedText.toString(); - } - - private boolean shouldIgnoreLineSeparatorSettings(String text, int i) { - if (lineSeparator == null || lineSeparator.isEmpty()) { - // Line separator is not customized - return true; - } - if (lineSeparator.equals(CRLF)) { - // Line separator settings is '\r\n', check that the previous character of the text is not '\r' - return i >= 1 && text.charAt(i-1) == '\r'; - } - return false; - } - - public String getSpacesReplacement() { - if(replacementTab == null) { - StringBuilder spaces = new StringBuilder(); - for (int i = 0; i < tabSize; i++) { - spaces.append(' '); - } - replacementTab = spaces.toString(); - } - return replacementTab; - } - - /** - * Returns true if the given text contains '\n' or '\t' and false otherwise. - * @param text the text to format according the LSP client settings. - * @return true if the given text contains '\n' or '\t' and false otherwise. - */ - public static boolean shouldBeFormatted(String text) { - return text.indexOf('\t') != -1 || text.indexOf('\n') != -1; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetParser.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetParser.java deleted file mode 100644 index 41437d5fc..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetParser.java +++ /dev/null @@ -1,377 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2016 EclipseSource. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; - -/** - * LSP snippet parser. - *

- * This code is a copy/paste from - * https://github.com/ralfstx/minimal-json/blob/master/com.eclipsesource.json/src/main/java/com/eclipsesource/json/JsonParser.java - * adapted for LSP Snippet. - * - * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax - */ -public class LspSnippetParser { - private static final int MIN_BUFFER_SIZE = 10; - private static final int DEFAULT_BUFFER_SIZE = 1024; - - private final LspSnippetHandler handler; - private Reader reader; - private char[] buffer; - private int bufferOffset; - private int index; - private int fill; - private int line; - private int lineOffset; - private int current; - private StringBuilder captureBuffer; - private int captureStart; - private int nestingLevel; - - /* - * | bufferOffset v [a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t] < input - * [l|m|n|o|p|q|r|s|t|?|?] < buffer ^ ^ | index fill - */ - - public LspSnippetParser(LspSnippetHandler handler) { - this.handler = handler; - } - - /** - * Parses the given input string. The input must contain a valid JSON value, - * optionally padded with whitespace. - * - * @param string the input string, must be valid JSON - * @throws ParseException if the input is not valid JSON - */ - public void parse(String string) { - if (string == null) { - throw new NullPointerException("string is null"); - } - int bufferSize = Math.max(MIN_BUFFER_SIZE, Math.min(DEFAULT_BUFFER_SIZE, string.length())); - try { - parse(new StringReader(string), bufferSize); - } catch (IOException exception) { - // StringReader does not throw IOException - throw new RuntimeException(exception); - } - } - - /** - * Reads the entire input from the given reader and parses it as JSON. The input - * must contain a valid JSON value, optionally padded with whitespace. - *

- * Characters are read in chunks into a default-sized input buffer. Hence, - * wrapping a reader in an additional BufferedReader likely won't - * improve reading performance. - *

- * - * @param reader the reader to read the input from - * @throws IOException if an I/O error occurs in the reader - * @throws ParseException if the input is not valid JSON - */ - public void parse(Reader reader) throws IOException { - parse(reader, DEFAULT_BUFFER_SIZE); - } - - /** - * Reads the entire input from the given reader and parses it as JSON. The input - * must contain a valid JSON value, optionally padded with whitespace. - *

- * Characters are read in chunks into an input buffer of the given size. Hence, - * wrapping a reader in an additional BufferedReader likely won't - * improve reading performance. - *

- * - * @param reader the reader to read the input from - * @param buffersize the size of the input buffer in chars - * @throws IOException if an I/O error occurs in the reader - * @throws ParseException if the input is not valid JSON - */ - public void parse(Reader reader, int buffersize) throws IOException { - if (reader == null) { - throw new NullPointerException("reader is null"); - } - if (buffersize <= 0) { - throw new IllegalArgumentException("buffersize is zero or negative"); - } - this.reader = reader; - buffer = new char[buffersize]; - bufferOffset = 0; - index = 0; - fill = 0; - line = 1; - lineOffset = 0; - current = 0; - captureStart = -1; - handler.startSnippet(); - read(); - readAny(); - handler.endSnippet(); - if (!isEndOfText()) { - throw error("Unexpected character"); - } - } - - // Snippet syntax: - - /* - any ::= tabstop | placeholder | choice | variable | text - tabstop ::= '$' int | '${' int '}' - placeholder ::= '${' int ':' any '}' - choice ::= '${' int '|' text (',' text)* '|}' - variable ::= '$' var | '${' var }' - | '${' var ':' any '}' - | '${' var '/' regex '/' (format | text)+ '/' options '}' - format ::= '$' int | '${' int '}' - | '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}' - | '${' int ':+' if '}' - | '${' int ':?' if ':' else '}' - | '${' int ':-' else '}' | '${' int ':' else '}' - regex ::= Regular Expression value (ctor-string) - options ::= Regular Expression option (ctor-options) - var ::= [_a-zA-Z] [_a-zA-Z0-9]* - int ::= [0-9]+ - text ::= .* - if ::= text - else ::= text - */ - - /** - * @throws IOException - */ - private void readAny() throws IOException { - if (isEndOfText()) { - return; - } - switch (current) { - case '$': - // read next character - read(); - if (isDigit()) { - // ex : $0, $10 - int index = readInt(); - handleTabstop(index); - } else if (readChar('{')) { - if (isDigit()) { - // - ${1:name} <-- placeholder - // - ${1|one,two,three|} <-- choice - // - ${1} <-- tabstop - int index = readInt(); - if (readChar(':')) { - // - ${1:name} <-- placeholder - String name = readString('}', '$'); - nestingLevel++; - handler.startPlaceholder(index, name, nestingLevel); - // placeholder ::= '${' int ':' any '}' - if (current == '}') { - // read next character - read(); - } else { - readAny(); - } - handler.endPlaceholder(nestingLevel); - nestingLevel--; - } else if (readChar('|')) { - // - ${1|one,two,three|} <-- choice - handleChoice(null, index); - } else { - // - ${1} <-- tabstop - handleTabstop(index); - readRequiredChar('}'); - } - } else { - // - ${name} <-- variable - String name = readString('}'); - handleVariable(name); - readRequiredChar('}'); - } - - } else { - // - $name <-- variable - String name = readString('$', ' '); - handleVariable(name); - } - break; - default: - handleText(); - break; - } - readAny(); - } - - private void handleChoice(String name, Integer index) throws IOException { - List choices = new ArrayList<>(); - String choice = readString(',', '|'); - while (!choice.isEmpty()) { - choices.add(choice); - if (readChar(',')) { - choice = readString(',', '|'); - } else { - break; - } - } - if (name == null) { - handler.choice(index, choices); - } else { - handler.choice(name, choices); - } - readRequiredChar('|'); - readRequiredChar('}'); - } - - private void handleTabstop(int index) { - handler.tabstop(index); - } - - private void handleVariable(String name) { - handler.variable(name); - } - - private String readString(int... stopOn) throws IOException { - startCapture(); - do { - read(); - for (int i = 0; i < stopOn.length; i++) { - if (current == stopOn[i]) { - return endCapture(); - } - } - - } while (!isEndOfText()); - return endCapture(); - } - - private void handleText() throws IOException { - String text = readString('$'); - handler.text(text); - } - - private int readInt() throws IOException { - startCapture(); - int firstDigit = current; - if (!readDigit()) { - throw expected("digit"); - } - if (firstDigit != '0') { - while (readDigit()) { - } - } - return Integer.parseInt(endCapture()); - } - - private void readRequiredChar(char ch) throws IOException { - if (!readChar(ch)) { - throw expected("'" + ch + "'"); - } - } - - private boolean readChar(char ch) throws IOException { - if (current != ch) { - return false; - } - read(); - return true; - } - - private boolean readDigit() throws IOException { - if (!isDigit()) { - return false; - } - read(); - return true; - } - - private void read() throws IOException { - if (index == fill) { - if (captureStart != -1) { - captureBuffer.append(buffer, captureStart, fill - captureStart); - captureStart = 0; - } - bufferOffset += fill; - fill = reader.read(buffer, 0, buffer.length); - index = 0; - if (fill == -1) { - current = -1; - index++; - return; - } - } - if (current == '\n') { - line++; - lineOffset = bufferOffset + index; - } - current = buffer[index++]; - } - - private void startCapture() { - if (captureBuffer == null) { - captureBuffer = new StringBuilder(); - } - captureStart = index - 1; - } - - private String endCapture() { - int start = captureStart; - int end = index - 1; - captureStart = -1; - if (captureBuffer.length() > 0) { - captureBuffer.append(buffer, start, end - start); - String captured = captureBuffer.toString(); - captureBuffer.setLength(0); - return captured; - } - return new String(buffer, start, end - start); - } - - Location getLocation() { - int offset = bufferOffset + index - 1; - int column = offset - lineOffset + 1; - return new Location(offset, line, column); - } - - private ParseException expected(String expected) { - if (isEndOfText()) { - return error("Unexpected end of input"); - } - return error("Expected " + expected); - } - - private ParseException error(String message) { - return new ParseException(message, getLocation()); - } - - private boolean isDigit() { - return current >= '0' && current <= '9'; - } - - private boolean isEndOfText() { - return current == -1; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetVariableConstants.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetVariableConstants.java deleted file mode 100644 index 83240593b..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetVariableConstants.java +++ /dev/null @@ -1,70 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -/** - * LSP variables. - * - * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax - */ -public class LspSnippetVariableConstants { - - private LspSnippetVariableConstants() { - - } - - /** - * The currently selected text or the empty string - */ - public static final String TM_SELECTED_TEXT = "TM_SELECTED_TEXT"; //$NON-NLS-1$ - - /** - * The contents of the current line - */ - public static final String TM_CURRENT_LINE = "TM_CURRENT_LINE"; //$NON-NLS-1$ - - /** - * The contents of the word under cursor or the empty string - */ - public static final String TM_CURRENT_WORD = "TM_CURRENT_WORD"; //$NON-NLS-1$ - /** - * The zero-index based line number - */ - - public static final String TM_LINE_INDEX = "TM_LINE_INDEX"; //$NON-NLS-1$ - /** - * The one-index based line number - */ - public static final String TM_LINE_NUMBER = "TM_LINE_NUMBER"; //$NON-NLS-1$ - - /** - * The filename of the current document - */ - public static final String TM_FILENAME = "TM_FILENAME"; //$NON-NLS-1$ - - /** - * The filename of the current document without its extensions - */ - public static final String TM_FILENAME_BASE = "TM_FILENAME_BASE"; //$NON-NLS-1$ - - /** - * The directory of the current document - */ - public static final String TM_DIRECTORY = "TM_DIRECTORY"; //$NON-NLS-1$ - - /** - * The full file path of the current document - */ - public static final String TM_FILEPATH = "TM_FILEPATH"; //$NON-NLS-1$ -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ParseException.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ParseException.java deleted file mode 100644 index bfec14290..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ParseException.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2016 EclipseSource. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - ******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -/** - * An unchecked exception to indicate that an input does not qualify as valid JSON. - *

- * This code is a copy/paste from - * https://github.com/ralfstx/minimal-json/blob/master/com.eclipsesource.json/src/main/java/com/eclipsesource/json/ParserException.java - * adapted for LSP Snippet. - */ -@SuppressWarnings("serial") // use default serial UID -public class ParseException extends RuntimeException { - - private final Location location; - - ParseException(String message, Location location) { - super(message + " at " + location); - this.location = location; - } - - /** - * Returns the location at which the error occurred. - * - * @return the error location - */ - public Location getLocation() { - return location; - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java deleted file mode 100644 index 4692c18d7..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticAnnotator.java +++ /dev/null @@ -1,101 +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 com.redhat.devtools.intellij.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.progress.ProgressManager; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileData; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.operations.codeactions.LSPLazyCodeActionIntentionAction; -import org.eclipse.lsp4j.Diagnostic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.net.URI; -import java.util.List; - -import static com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping.toHighlightSeverity; - -/** - * 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 Boolean collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { - return Boolean.TRUE; - } - - @Override - public @Nullable Boolean doAnnotate(Boolean unused) { - return Boolean.TRUE; - } - - @Override - public void apply(@NotNull PsiFile file, Boolean unused, @NotNull AnnotationHolder holder) { - URI fileUri = LSPIJUtils.toUri(file); - Document document = LSPIJUtils.getDocument(file.getVirtualFile()); - - // Loop for language server which report diagnostics for the given file - var servers = LanguageServiceAccessor.getInstance(file.getProject()) - .getStartedServers(); - for (var ls : servers) { - LSPVirtualFileData data = ls.getLSPVirtualFileData(fileUri); - if (data != null) { - // The file is mapped with the current language server - var ds = data.getDiagnosticsForServer(); - // Loop for LSP diagnostics to transform it to Intellij annotation. - for (Diagnostic diagnostic : ds.getDiagnostics()) { - ProgressManager.checkCanceled(); - createAnnotation(diagnostic, document, ds, holder); - } - } - } - } - - private static void createAnnotation(Diagnostic diagnostic, Document document, LSPDiagnosticsForServer diagnosticsForServer, AnnotationHolder holder) { - TextRange range = LSPIJUtils.toTextRange(diagnostic.getRange(), document); - if (range == null) { - // Language server reports invalid diagnostic, ignore it. - return; - } - // Collect information required to create Intellij Annotations - HighlightSeverity severity = toHighlightSeverity(diagnostic.getSeverity()); - String message = diagnostic.getMessage(); - - // Create Intellij Annotation from the given LSP diagnostic - AnnotationBuilder builder = holder - .newAnnotation(severity, message) - .range(range); - - // Register lazy quick fixes - List fixes = diagnosticsForServer.getQuickFixesFor(diagnostic); - for (IntentionAction fix : fixes) { - builder.withFix(fix); - } - builder.create(); - } - -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java deleted file mode 100644 index 2736de86f..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticHandler.java +++ /dev/null @@ -1,100 +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 com.redhat.devtools.intellij.lsp4ij.operations.diagnostics; - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.util.concurrency.AppExecutorUtil; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileData; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.jetbrains.annotations.NotNull; - -import java.net.URI; -import java.util.concurrent.Callable; -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) { - Project project = languageServerWrapper.getProject(); - if (project == null || project.isDisposed()) { - return; - } - if (ApplicationManager.getApplication().isReadAccessAllowed()) { - updateDiagnostics(params, project); - } else { - // Cancel if needed the previous "textDocument/publishDiagnostics" for a given uri. - var coalesceBy = new CoalesceByKey("textDocument/publishDiagnostics", params.getUri()); - var executeInSmartMode = DumbService.getInstance(languageServerWrapper.getProject()).isDumb(); - var action = ReadAction.nonBlocking((Callable) () -> { - updateDiagnostics(params, project); - return null; - }).expireWith(languageServerWrapper) - .coalesceBy(coalesceBy); - if (executeInSmartMode) { - action.inSmartMode(project); - } - action.submit(AppExecutorUtil.getAppExecutorService()); - } - } - - private void updateDiagnostics(@NotNull PublishDiagnosticsParams params, @NotNull Project project) { - if (project.isDisposed()) { - return; - } - VirtualFile file = LSPIJUtils.findResourceFor(params.getUri()); - if (file == null) { - return; - } - final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - if (psiFile == null) { - return; - } - - // Update LSP diagnostic reported by the language server id - URI fileURI = LSPIJUtils.toUri(file); - LSPVirtualFileData data = languageServerWrapper.getLSPVirtualFileData(fileURI); - if (data != null) { - synchronized (data) { - data.updateDiagnostics(params.getDiagnostics()); - } - // Trigger Intellij validation to execute - // {@link LSPDiagnosticAnnotator}. - // which translates LSP Diagnostics into Intellij Annotation - DaemonCodeAnalyzer.getInstance(project).restart(psiFile); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java deleted file mode 100644 index 24231231e..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPDiagnosticsForServer.java +++ /dev/null @@ -1,109 +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 com.redhat.devtools.intellij.lsp4ij.operations.diagnostics; - -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.operations.codeactions.LSPLazyCodeActionIntentionAction; -import com.redhat.devtools.intellij.lsp4ij.operations.codeactions.LSPLazyCodeActions; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.ServerCapabilities; - -import java.util.*; - -/** - * 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 final LanguageServerWrapper languageServerWrapper; - - private final VirtualFile file; - - // Map which contains all current diagnostics (as key) and future which load associated quick fixes (as value) - private Map diagnostics; - - public LSPDiagnosticsForServer(LanguageServerWrapper languageServerWrapper, VirtualFile file) { - this.languageServerWrapper = languageServerWrapper; - this.file = file; - this.diagnostics = Collections.emptyMap(); - } - - /** - * Update the new LSP published diagnosics. - * - * @param diagnostics the new LSP published diagnosics - */ - public void update(List diagnostics) { - // initialize diagnostics map - this.diagnostics = toMap(diagnostics, this.diagnostics); - } - - private Map toMap(List diagnostics, Map existingsDiagnostics) { - Map map = new HashMap<>(diagnostics.size()); - for (Diagnostic diagnostic : diagnostics) { - // Get the existing LSP lazy code actions for the current diagnostic - LSPLazyCodeActions actions = existingsDiagnostics != null ? existingsDiagnostics.get(diagnostic) : null; - if (actions != null) { - // cancel the LSP textDocument/codeAction request if needed - actions.cancel(); - } - map.put(diagnostic, new LSPLazyCodeActions(diagnostic, file, languageServerWrapper)); - } - return map; - } - - /** - * Returns the current diagnostics for the file reported by the language server. - * - * @return the current diagnostics for the file reported by the language server. - */ - 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) { - boolean codeActionSupported = isCodeActionSupported(languageServerWrapper); - if (!codeActionSupported || diagnostics.isEmpty()) { - return Collections.emptyList(); - } - LSPLazyCodeActions codeActions = diagnostics.get(diagnostic); - return codeActions != null ? codeActions.getCodeActions() : Collections.emptyList(); - } - - private static boolean isCodeActionSupported(LanguageServerWrapper languageServerWrapper) { - if (!languageServerWrapper.isActive() || languageServerWrapper.isStopping()) { - // 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()); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPPSiElement.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPPSiElement.java deleted file mode 100644 index 5405d832c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/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 com.redhat.devtools.intellij.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/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPPsiReference.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/LSPPsiReference.java deleted file mode 100644 index 92d4c7e04..000000000 --- a/src/main/java/com/redhat/devtools/intellij/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 com.redhat.devtools.intellij.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/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/SeverityMapping.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/SeverityMapping.java deleted file mode 100644 index ab1a74d90..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/diagnostics/SeverityMapping.java +++ /dev/null @@ -1,103 +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 com.redhat.devtools.intellij.lsp4ij.operations.diagnostics; - -import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.codeInsight.daemon.HighlightDisplayKey; -import com.intellij.codeInspection.InspectionProfile; -import com.intellij.lang.annotation.HighlightSeverity; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Utility class to map language servers' {@link DiagnosticSeverity} to Intellij's {@link HighlightSeverity}, and vice-versa. - */ -public class SeverityMapping { - - public static String NONE_SEVERITY = "none"; - - private SeverityMapping() { - } - - /** - * Maps language server's {@link DiagnosticSeverity} to Intellij's {@link HighlightSeverity} - * @param severity the {@link DiagnosticSeverity} to map - * @return the matching {@link HighlightSeverity} - */ - public static @NotNull HighlightSeverity toHighlightSeverity(@Nullable DiagnosticSeverity severity) { - if (severity == null) { - return HighlightSeverity.INFORMATION; - } - switch (severity) { - case Warning: - return HighlightSeverity.WEAK_WARNING; - case Hint: - case Information: - return HighlightSeverity.INFORMATION; - default: - return HighlightSeverity.ERROR; - } - } - - /** - * Maps {@link HighlightSeverity} to {@link DiagnosticSeverity} levels used by language servers. - *
    - *
  • Any severity below HighlightSeverity.INFORMATION is mapped to null
  • - *
  • Any severity below HighlightSeverity.WEAK_WARNING is mapped to DiagnosticSeverity.Information
  • - *
  • Any severity below HighlightSeverity.ERROR is mapped to DiagnosticSeverity.Warning
  • - *
  • Any other severity is mapped to DiagnosticSeverity.Error
  • - *
- * - * @param severity the severity to map to a {@link DiagnosticSeverity} - * @return the matching {@link DiagnosticSeverity} - */ - public static @Nullable DiagnosticSeverity getSeverity(@NotNull HighlightSeverity severity) { - if (HighlightSeverity.INFORMATION.compareTo(severity) > 0) { - return null; - } - if (HighlightSeverity.WEAK_WARNING.compareTo(severity) > 0) { - return DiagnosticSeverity.Information; - } - if (HighlightSeverity.ERROR.compareTo(severity) > 0) { - return DiagnosticSeverity.Warning; - } - return DiagnosticSeverity.Error; - } - - /** - * Returns {@link DiagnosticSeverity} as lower case, or none if severity is null. - * @param severity the {@link DiagnosticSeverity} to transform as {@link String} - * @return {@link DiagnosticSeverity} as lower case, or none if severity is null. - */ - public static @NotNull String toString(@Nullable DiagnosticSeverity severity) { - return (severity == null)? NONE_SEVERITY : severity.name().toLowerCase(); - } - - public static DiagnosticSeverity getSeverity(String inspectionId, InspectionProfile profile) { - if (!isInspectionEnabled(inspectionId, profile)) { - return null; - } - return getSeverity(getErrorLevel(inspectionId, profile)); - } - - private static @NotNull HighlightSeverity getErrorLevel(String inspectionId, InspectionProfile profile) { - HighlightDisplayLevel level = profile.getErrorLevel(HighlightDisplayKey.find(inspectionId), null); - return level.getSeverity(); - } - - private static boolean isInspectionEnabled(@NotNull String inspectionId, @NotNull InspectionProfile profile) { - return profile.isToolEnabled(HighlightDisplayKey.find(inspectionId)); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java deleted file mode 100644 index 1fdfe5553..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java +++ /dev/null @@ -1,129 +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 com.redhat.devtools.intellij.lsp4ij.operations.documentLink; - -import com.intellij.codeInsight.daemon.impl.HighlightInfoType; -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.ExternalAnnotator; -import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; -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 com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileData; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import org.eclipse.lsp4j.DocumentLink; -import org.eclipse.lsp4j.DocumentLinkParams; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * Intellij {@link ExternalAnnotator} implementation which collect LSP document links and display them with underline style. - */ -public class LSPDocumentLinkAnnotator extends ExternalAnnotator, List> { - - private static final Logger LOGGER = LoggerFactory.getLogger(LSPDocumentLinkAnnotator.class); - - @Nullable - @Override - public List collectInformation(@NotNull PsiFile psiFile, @NotNull Editor editor, boolean hasErrors) { - Document document = editor.getDocument(); - VirtualFile file = LSPIJUtils.getFile(document); - if (file == null) { - return null; - } - List datas = new ArrayList<>(); - final CancellationSupport cancellationSupport = new CancellationSupport(); - try { - ProgressManager.checkCanceled(); - - URI uri = LSPIJUtils.toUri(file); - DocumentLinkParams params = new DocumentLinkParams(LSPIJUtils.toTextDocumentIdentifier(uri)); - - Project project = editor.getProject(); - BlockingDeque, LanguageServerWrapper>> documentLinks = new LinkedBlockingDeque<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(project).getLanguageServers(file, - capabilities -> capabilities.getDocumentLinkProvider() != null) - .thenAcceptAsync(languageServers -> - cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> Pair.pair( - cancellationSupport.execute(languageServer.getServer().getTextDocumentService().documentLink(params)) - , languageServer.getServerWrapper())) - .map(request -> request.getFirst().thenAcceptAsync(result -> { - if (result != null) { - documentLinks.add(Pair.pair(result, request.getSecond())); - } - })).toArray(CompletableFuture[]::new)))); - while (!future.isDone() || !documentLinks.isEmpty()) { - ProgressManager.checkCanceled(); - Pair, LanguageServerWrapper> links = documentLinks.poll(25, TimeUnit.MILLISECONDS); - if (links != null) { - LSPVirtualFileData data = links.getSecond().getLSPVirtualFileData(uri); - if (data != null) { - data.updateDocumentLink(links.getFirst()); - datas.add(data); - } - } - } - } catch (ProcessCanceledException cancellation) { - cancellationSupport.cancel(); - throw cancellation; - } catch (InterruptedException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - Thread.currentThread().interrupt(); - } - return datas; - } - - @Override - public @Nullable List doAnnotate(List wrapper) { - return wrapper; - } - - @Override - public void apply(@NotNull PsiFile file, List datas, @NotNull AnnotationHolder holder) { - if (datas.isEmpty()) { - return; - } - Document document = LSPIJUtils.getDocument(file.getVirtualFile()); - for (var data : datas) { - var documentLinkPerServer = data.getDocumentLinkForServer(); - for (var documentLink : documentLinkPerServer.getDocumentLinks()) { - TextRange range = LSPIJUtils.toTextRange(documentLink.getRange(), document); - holder.newSilentAnnotation(HighlightInfoType.HIGHLIGHTED_REFERENCE_SEVERITY) - .range(range) - .textAttributes(DefaultLanguageHighlighterColors.HIGHLIGHTED_REFERENCE) - .create(); - } - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkForServer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkForServer.java deleted file mode 100644 index 161cb3d36..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkForServer.java +++ /dev/null @@ -1,42 +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 com.redhat.devtools.intellij.lsp4ij.operations.documentLink; - -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; -import org.eclipse.lsp4j.DocumentLink; - -import java.util.Collections; -import java.util.List; - -/** - * LSP document links holder for a file reported by a language server. - * - * @author Angelo ZERR - */ -public class LSPDocumentLinkForServer { - - private List documentLinks; - - public LSPDocumentLinkForServer(LanguageServerWrapper languageServerWrapper, VirtualFile file) { - } - - public void update(List documentLinks) { - this.documentLinks = documentLinks; - } - - public List getDocumentLinks() { - return documentLinks != null ? documentLinks : Collections.emptyList(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkGotoDeclarationHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkGotoDeclarationHandler.java deleted file mode 100644 index 6960acfa4..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkGotoDeclarationHandler.java +++ /dev/null @@ -1,114 +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 com.redhat.devtools.intellij.lsp4ij.operations.documentLink; - -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.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileData; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import org.eclipse.lsp4j.DocumentLink; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; - -/** - * {@link GotoDeclarationHandler} implementation used to open LSP document link with CTrl+Click. - */ -public class LSPDocumentLinkGotoDeclarationHandler implements GotoDeclarationHandler { - - @Override - public PsiElement @Nullable [] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { - Document document = editor.getDocument(); - VirtualFile file = LSPIJUtils.getFile(document); - Module module = LSPIJUtils.getModule(file, sourceElement.getProject()); - Project project = module != null ? module.getProject() : null; - if (project == null || project.isDisposed()) { - return PsiElement.EMPTY_ARRAY; - } - - URI fileUri = LSPIJUtils.toUri(file); - Collection allLinks = getAllDocumentLink(fileUri, project); - if (allLinks.isEmpty()) { - return PsiElement.EMPTY_ARRAY; - } - // The file has some LSP document links - for (LSPDocumentLinkForServer links : allLinks) { - for (DocumentLink documentLink : links.getDocumentLinks()) { - TextRange range = LSPIJUtils.toTextRange(documentLink.getRange(), document); - if (range.contains(offset)) { - // The Ctrl+Click has been done in a LSP document link,try to open the document. - final String target = documentLink.getTarget(); - if (target != null && !target.isEmpty()) { - VirtualFile targetFile = LSPIJUtils.findResourceFor(target); - if (targetFile == null) { - // The LSP document link file doesn't exist, open a file dialog - // which asks if user want to create the file. - // At this step we cannot open a dialog directly, we need to open the dialog - // with invoke later. - ApplicationManager.getApplication().invokeLater(() -> { - int result = Messages.showYesNoDialog(LanguageServerBundle.message("lsp.create.file.confirm.dialog.message", target), - LanguageServerBundle.message("lsp.create.file.confirm.dialog.title"), Messages.getQuestionIcon()); - if (result == Messages.YES) { - try { - // Create file - VirtualFile newFile = LSPIJUtils.createFile(target); - if (newFile != null) { - // Open it in an editor - LSPIJUtils.openInEditor(newFile, null, project); - } - } catch (IOException e) { - Messages.showErrorDialog(LanguageServerBundle.message("lsp.create.file.error.dialog.message", target, e.getMessage()), - LanguageServerBundle.message("lsp.create.file.error.dialog.title")); - } - } - }); - // Return an empty result here. - // If user accepts to create the file, the open is done after the creation of teh file. - return PsiElement.EMPTY_ARRAY; - } - return new PsiElement[]{PsiManager.getInstance(project).findFile(targetFile)}; - } - } - } - } - return PsiElement.EMPTY_ARRAY; - } - - private Collection getAllDocumentLink(URI fileUri, Project project) { - Collection links = new ArrayList<>(); - LanguageServiceAccessor.getInstance(project) - .getStartedServers() - .forEach(ls -> { - LSPVirtualFileData data = ls.getLSPVirtualFileData(fileUri); - if (data != null) { - links.add(data.getDocumentLinkForServer()); - } - }); - return links; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java deleted file mode 100644 index 6540f7ab3..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPDocumentationProvider.java +++ /dev/null @@ -1,215 +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 com.redhat.devtools.intellij.lsp4ij.operations.documentation; - -import com.intellij.lang.documentation.DocumentationProviderEx; -import com.intellij.lang.documentation.ExternalDocumentationHandler; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.util.io.URLUtil; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.LSPCompletionProposal; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.parser.Parser; -import org.eclipse.lsp4j.MarkupContent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.util.List; -import java.util.stream.Collectors; - -/** - * {@link DocumentationProviderEx} implementation for LSP to support: - * - *
    - *
  • textDocument/hover
  • - *
  • documentation for completion item
  • - *
. - */ -public class LSPDocumentationProvider extends DocumentationProviderEx implements ExternalDocumentationHandler { - - private static final Parser PARSER = Parser.builder().build(); - private static final HtmlRenderer RENDERER = HtmlRenderer.builder().build(); - - private static final Key TARGET_OFFSET_KEY = new Key<>(LSPDocumentationProvider.class.getName()); - - private static final Key LSP_HOVER_KEY = new Key<>(LSPTextHoverForFile.class.getName()); - - @Nullable - @Override - public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { - return generateDoc(element, originalElement); - } - - @Override - public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement, int targetOffset) { - if (contextElement != null) { - // Store the offset where the hover has been triggered - contextElement.putUserData(TARGET_OFFSET_KEY, targetOffset); - } - return super.getCustomDocumentationElement(editor, file, contextElement, targetOffset); - } - - @Nullable - @Override - public String generateDoc(@NotNull PsiElement element, @Nullable PsiElement originalElement) { - try { - Project project = element.getProject(); - if (project.isDisposed()) { - return null; - } - Editor editor = null; - List markupContent = null; - if (element instanceof LSPPsiElementForLookupItem) { - // Show documentation for a given completion item in the "documentation popup" (see IJ Completion setting) - // (LSP textDocument/completion request) - editor = LSPIJUtils.editorForElement(element); - markupContent = ((LSPPsiElementForLookupItem) element).getDocumentation(); - } else { - // Show documentation for a hovered element (LSP textDocument/hover request). - if (originalElement == null) { - return null; - } - editor = LSPIJUtils.editorForElement(originalElement); - if (editor != null) { - LSPTextHoverForFile hover = editor.getUserData(LSP_HOVER_KEY); - if (hover == null) { - hover = new LSPTextHoverForFile(editor); - editor.putUserData(LSP_HOVER_KEY, hover); - } - int targetOffset = getTargetOffset(originalElement); - markupContent = hover.getHoverContent(originalElement, targetOffset, editor); - } - } - - if (editor == null || markupContent == null || markupContent.isEmpty()) { - return null; - } - String s = markupContent - .stream() - .map(m -> m.getValue()) - .collect(Collectors.joining("\n\n")); - return styleHtml(editor, RENDERER.render(PARSER.parse(s))); - } finally { - if (originalElement != null) { - originalElement.putUserData(TARGET_OFFSET_KEY, null); - } - } - } - - private static int getTargetOffset(PsiElement originalElement) { - Integer targetOffset = originalElement.getUserData(TARGET_OFFSET_KEY); - if (targetOffset != null) { - return targetOffset; - } - int startOffset = originalElement.getTextOffset(); - int textLength = originalElement.getTextLength(); - return startOffset + textLength / 2; - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { - if (object instanceof LSPCompletionProposal) { - MarkupContent documentation = ((LSPCompletionProposal) object).getDocumentation(); - if (documentation != null) { - return new LSPPsiElementForLookupItem(documentation, psiManager, element); - } - } - return null; - } - - @Nullable - @Override - public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { - return null; - } - - @Override - public boolean handleExternal(PsiElement element, PsiElement originalElement) { - return false; - } - - @Override - public boolean handleExternalLink(PsiManager psiManager, String link, PsiElement context) { - //Ignore non-local uri (http(s), mailto, ftp...) - if (URLUtil.URL_PATTERN.matcher(link).matches()) { - return false; - } - VirtualFile file = LSPIJUtils.findResourceFor(link); - if (file != null) { - FileEditorManager.getInstance(psiManager.getProject()).openFile(file, true, true); - return true; - } - return false; - } - - @Override - public boolean canFetchDocumentationLink(String link) { - return false; - } - - @Override - public @NotNull String fetchExternalDocumentation(@NotNull String link, @Nullable PsiElement element) { - return null; - } - - - public static String styleHtml(Editor editor, String htmlBody) { - if (htmlBody == null || htmlBody.isEmpty()) { - return htmlBody; - } - Color background = editor.getColorsScheme().getDefaultBackground(); - Color foreground = editor.getColorsScheme().getDefaultForeground(); - - StringBuilder html = new StringBuilder("") - .append(htmlBody) - .append(""); - return html.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); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java deleted file mode 100644 index bcedbe908..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPPsiElementForLookupItem.java +++ /dev/null @@ -1,68 +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 com.redhat.devtools.intellij.lsp4ij.operations.documentation; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.impl.FakePsiElement; -import org.eclipse.lsp4j.MarkupContent; - -import java.util.Collections; -import java.util.List; - -/** - * Implement a fake {@link PsiElement} which stores the completion item documentation. - */ -public class LSPPsiElementForLookupItem extends FakePsiElement { - private final MarkupContent documentation; - private final PsiManager psiManager; - private final PsiElement element; - - public LSPPsiElementForLookupItem(MarkupContent documentation, PsiManager psiManager, PsiElement element) { - this.psiManager = psiManager; - this.documentation = documentation; - this.element = element; - } - - public List getDocumentation() { - return documentation != null ? Collections.singletonList(documentation) : null; - } - - public PsiElement getNavigationElement() { - return element; - } - - @Override - public PsiElement getParent() { - return getNavigationElement(); - } - - @Override - public String getName() { - return ""; - } - - @Override - public PsiFile getContainingFile() { - return element.getContainingFile(); - } - - - @Override - public PsiManager getManager() { - return psiManager; - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java deleted file mode 100644 index 6b8b812be..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentation/LSPTextHoverForFile.java +++ /dev/null @@ -1,164 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij.operations.documentation; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.impl.EditorImpl; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationUtil; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * LSP textDocument/hover support for a given file. - */ -public class LSPTextHoverForFile implements Disposable { - - private static final Logger LOGGER = LoggerFactory.getLogger(LSPTextHoverForFile.class); - private PsiElement lastElement; - private int lastOffset = -1; - private CompletableFuture> lspRequest; - private CancellationSupport previousCancellationSupport; - - public LSPTextHoverForFile(Editor editor) { - if (editor instanceof EditorImpl) { - Disposer.register(((EditorImpl) editor).getDisposable(), this); - } - } - - public List getHoverContent(PsiElement element, int targetOffset, Editor editor) { - initiateHoverRequest(element, targetOffset); - try { - List result = lspRequest - .get(500, TimeUnit.MILLISECONDS).stream() - .filter(Objects::nonNull) - .map(LSPTextHoverForFile::getHoverString) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - // The LSP hover request are finished, don't need to cancel the previous LSP requests. - previousCancellationSupport = null; - return result; - } catch (ResponseErrorException | ExecutionException | CancellationException e) { - // do not report error if the server has cancelled the request - if (!CancellationUtil.isRequestCancelledException(e)) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } catch (TimeoutException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.warn(e.getLocalizedMessage(), e); - } - return null; - } - - - /** - * Initialize hover requests with hover (if available). - * - * @param element the PSI element. - * @param offset the target offset. - */ - private void initiateHoverRequest(PsiElement element, int offset) { - if (this.previousCancellationSupport != null) { - // The previous LSP hover request is not finished,cancel it - this.previousCancellationSupport.cancel(); - } - VirtualFile file = LSPIJUtils.getFile(element); - if (file == null) { - return; - } - final Document document = LSPIJUtils.getDocument(file); - if (document == null) { - return; - } - if (offset != -1 && (this.lspRequest == null || !element.equals(this.lastElement) || offset != this.lastOffset)) { - this.lastElement = element; - this.lastOffset = offset; - CancellationSupport cancellationSupport = new CancellationSupport(); - this.lspRequest = LanguageServiceAccessor.getInstance(element.getProject()) - .getLanguageServers(file, 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 - languageServers.stream() - .map(languageServer -> - cancellationSupport.execute( - languageServer.getServer().getTextDocumentService() - .hover(LSPIJUtils.toHoverParams(offset, document))) - .join() - ).filter(Objects::nonNull).collect(Collectors.toList())); - // store the current cancel support as previous - previousCancellationSupport = cancellationSupport; - } - } - - private static @Nullable MarkupContent getHoverString(Hover hover) { - Either>, MarkupContent> hoverContent = hover.getContents(); - if (hoverContent.isLeft()) { - List> contents = hoverContent.getLeft(); - if (contents == null || contents.isEmpty()) { - return null; - } - String s = 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")); - return new MarkupContent(s, MarkupKind.PLAINTEXT); - } else { - return hoverContent.getRight(); - } - } - - private static boolean isHoverCapable(ServerCapabilities capabilities) { - return (capabilities.getHoverProvider().isLeft() && capabilities.getHoverProvider().getLeft()) || capabilities.getHoverProvider().isRight(); - } - - @Override - public void dispose() { - if (this.previousCancellationSupport != null) { - // The previous LSP hover request is not finished,cancel it - this.previousCancellationSupport.cancel(); - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightPsiElement.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightPsiElement.java deleted file mode 100644 index 394caf7d6..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightPsiElement.java +++ /dev/null @@ -1,102 +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 com.redhat.devtools.intellij.lsp4ij.operations.highlight; - -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.NlsSafe; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.impl.PsiElementBase; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import org.eclipse.lsp4j.DocumentHighlight; -import org.eclipse.lsp4j.DocumentHighlightKind; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Implement a fake {@link PsiElement} which stores the required text edit (coming from Language server) to highlight. - * - * This class provides the capability to highlight part of code by using Language server TextEdit and not by using the PsiElement. - */ -public class LSPHighlightPsiElement extends PsiElementBase { - - private final TextRange textRange; - private final DocumentHighlightKind kind; - - public LSPHighlightPsiElement(TextRange textRange, DocumentHighlightKind kind) { - this.textRange = textRange; - this.kind = kind; - } - - public DocumentHighlightKind getKind() { - return kind; - } - - @Override - public @NotNull Language getLanguage() { - return null; - } - - @Override - public PsiElement @NotNull [] getChildren() { - return new PsiElement[0]; - } - - @Override - public PsiElement getParent() { - return null; - } - - @Override - public TextRange getTextRange() { - return textRange; - } - - @Override - public int getStartOffsetInParent() { - return 0; - } - - @Override - public int getTextLength() { - return 0; - } - - @Override - public @Nullable PsiElement findElementAt(int offset) { - return null; - } - - @Override - public int getTextOffset() { - return 0; - } - - @Override - public @NlsSafe String getText() { - return null; - } - - @Override - public char @NotNull [] textToCharArray() { - return new char[0]; - } - - @Override - public ASTNode getNode() { - return null; - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java deleted file mode 100644 index 14d19ceed..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandler.java +++ /dev/null @@ -1,54 +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 com.redhat.devtools.intellij.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.eclipse.lsp4j.DocumentHighlightKind; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.logging.Logger; - -public class LSPHighlightUsagesHandler extends HighlightUsagesHandlerBase { - 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 -> - { - if (target.getKind() == DocumentHighlightKind.Read) { - myReadUsages.add(target.getTextRange()); - } else { - myWriteUsages.add(target.getTextRange()); - } - }); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java deleted file mode 100644 index db7666550..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.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 com.redhat.devtools.intellij.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.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -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 psiFile) { - VirtualFile file = LSPIJUtils.getFile(psiFile); - if (file == null) { - return Collections.emptyList(); - } - List elements = new ArrayList<>(); - final CancellationSupport cancellationSupport = new CancellationSupport(); - try { - Document document = editor.getDocument(); - int offset = TargetElementUtil.adjustOffset(psiFile, document, editor.getCaretModel().getOffset()); - Position position = LSPIJUtils.toPosition(offset, document); - - ProgressManager.checkCanceled(); - - String uri = LSPIJUtils.toUriAsString(file); - TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri); - DocumentHighlightParams params = new DocumentHighlightParams(identifier, position); - BlockingDeque highlights = new LinkedBlockingDeque<>(); - - CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()) - .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) - .thenAcceptAsync(languageServers -> - cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> cancellationSupport.execute(languageServer.getServer().getTextDocumentService().documentHighlight(params))) - .map(request -> request.thenAcceptAsync(result -> { - if (result != null) { - highlights.addAll(result); - } - })).toArray(CompletableFuture[]::new)))); - while (!future.isDone() || !highlights.isEmpty()) { - ProgressManager.checkCanceled(); - DocumentHighlight highlight = highlights.poll(25, TimeUnit.MILLISECONDS); - if (highlight != null) { - TextRange textRange = LSPIJUtils.toTextRange(highlight.getRange(), document); - if (textRange != null) { - elements.add(new LSPHighlightPsiElement(textRange, highlight.getKind())); - } - } - } - } catch (ProcessCanceledException cancellation) { - cancellationSupport.cancel(); - throw cancellation; - } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, e, e::getLocalizedMessage); - } - return elements; - - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java deleted file mode 100644 index eebdbfae3..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java +++ /dev/null @@ -1,188 +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 com.redhat.devtools.intellij.lsp4ij.operations.inlayhint; - -import com.intellij.codeInsight.hints.InlayHintsSink; -import com.intellij.codeInsight.hints.presentation.InlayPresentation; -import com.intellij.codeInsight.hints.presentation.PresentationFactory; -import com.intellij.codeInsight.hints.presentation.SequencePresentation; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.AbstractLSPInlayProvider; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.LanguageServer; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -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; - -/** - * LSP textDocument/inlayHint support. - */ -public class LSPInlayHintInlayProvider extends AbstractLSPInlayProvider { - - private static final Key CANCELLATION_SUPPORT_KEY = new Key<>(LSPInlayHintInlayProvider.class.getName() + "-CancellationSupport"); - - public LSPInlayHintInlayProvider() { - super(CANCELLATION_SUPPORT_KEY); - } - - - @Override - protected void doCollect(@NotNull VirtualFile file, @NotNull Project project, @NotNull Editor editor, @NotNull PresentationFactory factory, @NotNull InlayHintsSink inlayHintsSink, @NotNull CancellationSupport cancellationSupport) throws InterruptedException { - Document document = editor.getDocument(); - URI fileUri = LSPIJUtils.toUri(file); - Range viewPortRange = getViewPortRange(editor); - InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(fileUri.toASCIIString()), viewPortRange); - BlockingDeque> pairs = new LinkedBlockingDeque<>(); - - CompletableFuture future = collect(project, file, param, pairs, cancellationSupport); - List>> inlayHints = createInlayHints(document, pairs, future); - inlayHints.stream() - .collect(Collectors.groupingBy(p -> p.first)) - .forEach((offset, list) -> - inlayHintsSink.addInlineElement(offset, false, toPresentation(editor, list, factory, cancellationSupport), false)); - } - - @NotNull - private List>> createInlayHints( - @NotNull Document document, - BlockingDeque> pairs, - CompletableFuture future) - throws InterruptedException { - List>> inlayHints = new ArrayList<>(); - while (!future.isDone() || !pairs.isEmpty()) { - ProgressManager.checkCanceled(); - Pair pair = pairs.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - int offset = LSPIJUtils.toOffset(pair.getFirst().getPosition(), document); - inlayHints.add(Pair.create(offset, pair)); - } - } - return inlayHints; - } - - private CompletableFuture collect(@NotNull Project project, @NotNull VirtualFile file, InlayHintParams param, BlockingDeque> pairs, CancellationSupport cancellationSupport) { - return LanguageServiceAccessor.getInstance(project) - .getLanguageServers(file, capabilities -> capabilities.getInlayHintProvider() != null) - .thenComposeAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> - cancellationSupport.execute(languageServer.getServer().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.getServer()))); - } - })) - .toArray(CompletableFuture[]::new)))); - } - - @NotNull - private static Range getViewPortRange(Editor editor) { - // LSP textDocument/inlayHnt request parameter expects to fill the visible view port range. - // As Intellij inlay hint provider is refreshed just only when editor is opened or editor content changed - // and not when editor is scrolling, the view port range must be created with full text document offsets. - Position start = new Position(0, 0); - Document document = editor.getDocument(); - Position end = LSPIJUtils.toPosition(document.getTextLength(), document); - return new Range(start, end); - } - - private InlayPresentation toPresentation(@NotNull Editor editor, - @NotNull List>> elements, - @NotNull PresentationFactory factory, - @NotNull CancellationSupport cancellationSupport) { - List presentations = new ArrayList<>(); - elements.forEach(p -> { - cancellationSupport.checkCanceled(); - 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 text = createInlayPresentation(editor.getProject(), factory, p, index, part); - if (part.getTooltip() != null && part.getTooltip().isLeft()) { - text = factory.withTooltip(part.getTooltip().getLeft(), text); - } - presentations.add(text); - index++; - } - } - }); - return factory.roundWithBackground(new SequencePresentation(presentations)); - } - - @NotNull - private InlayPresentation createInlayPresentation( - Project project, - PresentationFactory factory, - Pair> p, - int index, - InlayHintLabelPart part) { - InlayPresentation text = factory.smallText(part.getValue()); - if (hasCommand(part)) { - // InlayHintLabelPart defines a Command, create a clickable inlay hint - int finalIndex = index; - text = factory.referenceOnHover(text, (event, translated) -> - executeClientCommand(p.second.second, p.second.first, finalIndex, (Component) event.getSource(), project) - ); - } - return text; - } - - private static boolean hasCommand(InlayHintLabelPart part) { - Command command = part.getCommand(); - return (command != null && command.getCommand() != null && !command.getCommand().isEmpty()); - } - - 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 static boolean isResolveSupported(Either provider) { - return provider.isRight() && provider.getRight().getResolveProvider(); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java deleted file mode 100644 index a4ef8cfd0..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java +++ /dev/null @@ -1,130 +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 com.redhat.devtools.intellij.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 com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationSupport; -import com.redhat.devtools.intellij.lsp4ij.internal.CancellationUtil; -import com.redhat.devtools.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.jsonrpc.ResponseErrorException; -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.*; -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) { - VirtualFile file = LSPIJUtils.getFile(sourceElement); - if (file == null) { - return PsiElement.EMPTY_ARRAY; - } - URI uri = LSPIJUtils.toUri(file); - Document document = editor.getDocument(); - DefinitionParams params = new DefinitionParams(LSPIJUtils.toTextDocumentIdentifier(uri), LSPIJUtils.toPosition(offset, document)); - Set targets = new HashSet<>(); - final CancellationSupport cancellationSupport = new CancellationSupport(); - try { - Project project = editor.getProject(); - LanguageServiceAccessor.getInstance(project) - .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())) - .thenComposeAsync(languageServers -> - cancellationSupport.execute( - CompletableFuture.allOf( - languageServers - .stream() - .map(server -> - cancellationSupport.execute(server.getServer().getTextDocumentService().definition(params)) - .thenAcceptAsync(definitions -> targets.addAll(toElements(project, definitions)))) - .toArray(CompletableFuture[]::new)))) - .get(1_000, TimeUnit.MILLISECONDS); - } catch (ResponseErrorException | ExecutionException | CancellationException e) { - // do not report error if the server has cancelled the request - if (!CancellationUtil.isRequestCancelledException(e)) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } catch (TimeoutException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.warn(e.getLocalizedMessage(), e); - } - return targets.toArray(new PsiElement[targets.size()]); - } - - 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/com/redhat/devtools/intellij/lsp4ij/server/CannotStartProcessException.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/CannotStartProcessException.java deleted file mode 100644 index d32adb420..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/CannotStartProcessException.java +++ /dev/null @@ -1,31 +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 com.redhat.devtools.intellij.lsp4ij.server; - -import java.io.IOException; - -/** - * Language server exception when start process cannot be done. - */ -public class CannotStartProcessException extends LanguageServerException { - - public CannotStartProcessException(IOException e) { - super(e); - } - - public CannotStartProcessException(String message) { - super(message); - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/CannotStartServerException.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/CannotStartServerException.java deleted file mode 100644 index 284f45f33..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/CannotStartServerException.java +++ /dev/null @@ -1,24 +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 com.redhat.devtools.intellij.lsp4ij.server; - -/** - * Language server exception when language server cannot be done. - */ -public class CannotStartServerException extends LanguageServerException { - - public CannotStartServerException(String message, Throwable e) { - super(message, e); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/JavaProcessCommandBuilder.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/JavaProcessCommandBuilder.java deleted file mode 100644 index c52ea6745..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/JavaProcessCommandBuilder.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 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 com.redhat.devtools.intellij.lsp4ij.server; - -import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.settings.UserDefinedLanguageServerSettings; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * A builder to create Java process command. - */ -public class JavaProcessCommandBuilder { - - private final String languageId; - - private String javaPath; - - private String debugPort; - - private boolean debugSuspend; - private String jar; - - private String cp; - - public JavaProcessCommandBuilder(Project project, String languageId) { - this.languageId = languageId; - setJavaPath(computeJavaPath()); - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project).getLanguageServerSettings(languageId); - if (settings != null) { - setDebugPort(settings.getDebugPort()); - setDebugSuspend(settings.isDebugSuspend()); - } - } - - public JavaProcessCommandBuilder setJavaPath(String javaPath) { - this.javaPath = javaPath; - return this; - } - - public JavaProcessCommandBuilder setDebugPort(String debugPort) { - this.debugPort = debugPort; - return this; - } - - public JavaProcessCommandBuilder setDebugSuspend(boolean debugSuspend) { - this.debugSuspend = debugSuspend; - return this; - } - - public JavaProcessCommandBuilder setJar(String jar) { - this.jar = jar; - return this; - } - - public JavaProcessCommandBuilder setCp(String cp) { - this.cp = cp; - return this; - } - - public List create() { - List commands = new ArrayList<>(); - commands.add(javaPath); - if (debugPort != null && !debugPort.isEmpty()) { - String suspend = debugSuspend ? "y" : "n"; - commands.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=" + suspend + ",address=" + debugPort); - } - if (jar != null) { - commands.add("-jar"); - commands.add(jar); - } - if (cp != null) { - commands.add("-cp"); - commands.add(cp); - } - return commands; - } - - private static String computeJavaPath() { - return new File(System.getProperty("java.home"), - "bin/java" + (OS.current() == OS.WINDOWS ? ".exe" : "")).getAbsolutePath(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/LanguageServerException.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/LanguageServerException.java deleted file mode 100644 index 8ec1ca4a5..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/LanguageServerException.java +++ /dev/null @@ -1,32 +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 com.redhat.devtools.intellij.lsp4ij.server; - -/** - * Base class for language server exception. - */ -public class LanguageServerException extends RuntimeException { - - public LanguageServerException(String message, Throwable e) { - super(message, e); - } - - public LanguageServerException(Throwable e) { - super(e); - } - - public LanguageServerException(String message) { - super(message); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/OS.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/OS.java deleted file mode 100644 index f106595cf..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/OS.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij.server; - -import java.util.Locale; - -/** - * Enumerated type for operating systems. - *

- * Copied from https://github.com/smallrye/smallrye-common/blob/main/os/src/main/java/io/smallrye/common/os/OS.java - */ -public enum OS { - - /** - * IBM AIX operating system. - */ - AIX, - - /** - * Linux-based operating system. - */ - LINUX, - - /** - * Apple Macintosh operating system (e.g., macOS). - */ - MAC, - - /** - * Oracle Solaris operating system. - */ - SOLARIS, - - /** - * Microsoft Windows operating system. - */ - WINDOWS, - - /** - * Anything else different from the above. - */ - OTHER; - - private static final OS CURRENT_OS = determineCurrentOs(); - - private static OS determineCurrentOs() { - return parse(System.getProperty("os.name", "unknown")); - } - - static OS parse(String osName) { - osName = osName.toLowerCase(Locale.ENGLISH); - - if (osName.contains("linux")) { - return LINUX; - } - if (osName.contains("windows")) { - return WINDOWS; - } - if (osName.contains("mac") || osName.contains("darwin")) { - return MAC; - } - if (osName.contains("sunos") || osName.contains("solaris")) { - return SOLARIS; - } - if (osName.contains("aix")) { - return AIX; - } - return OTHER; - } - - /** - * @return {@code true} if this {@code OS} is known to be the - * operating system on which the current JVM is executing - */ - public boolean isCurrent() { - return this == CURRENT_OS; - } - - /** - * @return the current OS - */ - public static OS current() { - return CURRENT_OS; - } -} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/ProcessStreamConnectionProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/ProcessStreamConnectionProvider.java deleted file mode 100644 index b344fbb60..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/ProcessStreamConnectionProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij.server; - -import javax.annotation.Nullable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Objects; - -public abstract class ProcessStreamConnectionProvider implements StreamConnectionProvider { - 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 CannotStartProcessException { - if (this.commands == null || this.commands.isEmpty() || this.commands.stream().anyMatch(Objects::isNull)) { - throw new CannotStartProcessException("Unable to start language server: " + this.toString()); //$NON-NLS-1$ - } - ProcessBuilder builder = createProcessBuilder(); - try { - Process p = builder.start(); - this.process = p; - } catch (IOException e) { - throw new CannotStartProcessException(e); - } - } - - @Override - public boolean isAlive() { - return process != null ? process.isAlive() : false; - } - - @Override - public void ensureIsAlive() throws CannotStartProcessException { - // Wait few ms before checking the is alive flag. - synchronized (this.process) { - try { - this.process.wait(200); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - if (!isAlive()) { - throw new CannotStartProcessException("Unable to start language server: " + this.toString()); //$NON-NLS-1$ - } - } - - 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(); - } - - public @Nullable - Long getPid() { - final Process p = process; - return p == null ? null : p.pid(); - } - - @Override - public void stop() { - Process p = process; - if (p != null) { - p.destroy(); - process = null; - } - } - - public 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; - } - - @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/com/redhat/devtools/intellij/lsp4ij/server/StreamConnectionProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/StreamConnectionProvider.java deleted file mode 100644 index c9889883c..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/server/StreamConnectionProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.redhat.devtools.intellij.lsp4ij.server; - -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.services.LanguageServer; - -import javax.annotation.Nullable; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; - -public interface StreamConnectionProvider { - - void start() throws CannotStartProcessException; - - InputStream getInputStream(); - - 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. - */ - @Nullable - InputStream getErrorStream(); - - /** - * User provided initialization options. - */ - default Object getInitializationOptions(URI rootUri) { - return null; - } - - /** - * Returns an object that describes the experimental features supported - * by the client. - * - * @return an object whose fields represent the different 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 - */ - 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" - */ - default String getTrace(URI rootUri) { - return "off"; //$NON-NLS-1$ - } - - void stop(); - - /** - * Allows to hook custom behavior on messages. - * - * @param message a message. - * @param languageServer the language server receiving/sending the message. - * @param rootUri the root Uri. - */ - default void handleMessage(Message message, LanguageServer languageServer, URI rootUri) { - } - - /** - * Returns true if the connection provider is alive and false otherwise. - * - * @return true if the connection provider is alive and false otherwise. - */ - default boolean isAlive() { - return true; - } - - /** - * Ensure that process is alive. - * - * @throws CannotStartProcessException if process is not alive. - */ - default void ensureIsAlive() throws CannotStartProcessException { - if (!isAlive()) { - throw new CannotStartProcessException("Unable to start language server: " + this.toString()); //$NON-NLS-1$ - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerConfigurable.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerConfigurable.java deleted file mode 100644 index 5dcc83ca1..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerConfigurable.java +++ /dev/null @@ -1,121 +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 com.redhat.devtools.intellij.lsp4ij.settings; - -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.NamedConfigurable; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.NlsContexts; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -/** - * UI settings to configure a given language server: - * - *

    - *
  • Debug port
  • - *
  • Suspend and wait for a debugger
  • - *
- */ -public class LanguageServerConfigurable extends NamedConfigurable { - - private final LanguageServersRegistry.LanguageServerDefinition languageServerDefinition; - private final Project project; - - private LanguageServerView myView; - - public LanguageServerConfigurable(LanguageServersRegistry.LanguageServerDefinition languageServerDefinition, Runnable updater, Project project) { - super(false, updater); - this.languageServerDefinition = languageServerDefinition; - this.project = project; - } - - @Override - public void setDisplayName(String name) { - // Do nothing: the language server name is nt editable. - } - - @Override - public LanguageServersRegistry.LanguageServerDefinition getEditableObject() { - return languageServerDefinition; - } - - @Override - public @NlsContexts.DetailedDescription String getBannerSlogan() { - return languageServerDefinition.getDisplayName(); - } - - @Override - public JComponent createOptionsPanel() { - if (myView == null) { - myView = new LanguageServerView(languageServerDefinition); - } - return myView.getComponent(); - } - - @Override - public @NlsContexts.ConfigurableName String getDisplayName() { - return languageServerDefinition.getDisplayName(); - } - - @Override - public @Nullable Icon getIcon(boolean expanded) { - String serverId = languageServerDefinition.id; - return LanguageServersRegistry.getInstance().getServerIcon(serverId); - } - - @Override - public boolean isModified() { - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project) - .getLanguageServerSettings(languageServerDefinition.id); - if (settings == null) { - return true; - } - return !(myView.getDebugPort().equals(settings.getDebugPort()) - && myView.isDebugSuspend() == settings.isDebugSuspend() - && myView.getServerTrace() == settings.getServerTrace()); - } - - @Override - public void apply() throws ConfigurationException { - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = new UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings(); - settings.setDebugPort(myView.getDebugPort()); - settings.setDebugSuspend(myView.isDebugSuspend()); - settings.setServerTrace(myView.getServerTrace()); - UserDefinedLanguageServerSettings.getInstance(project).setLanguageServerSettings(languageServerDefinition.id, settings); - } - - @Override - public void reset() { - ServerTrace serverTrace = ServerTrace.off; - UserDefinedLanguageServerSettings.LanguageServerDefinitionSettings settings = UserDefinedLanguageServerSettings.getInstance(project) - .getLanguageServerSettings(languageServerDefinition.id); - if (settings != null) { - myView.setDebugPort(settings.getDebugPort()); - myView.setDebugSuspend(settings.isDebugSuspend()); - if (settings.getServerTrace() != null) { - serverTrace = settings.getServerTrace(); - } - } - myView.setServerTrace(serverTrace); - } - - @Override - public void disposeUIResources() { - if (myView != null) Disposer.dispose(myView); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerListConfigurable.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerListConfigurable.java deleted file mode 100644 index 306338ccd..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerListConfigurable.java +++ /dev/null @@ -1,106 +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 com.redhat.devtools.intellij.lsp4ij.settings; - -import com.intellij.openapi.options.SearchableConfigurable; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.MasterDetailsComponent; -import com.intellij.openapi.util.NlsContexts; -import com.intellij.ui.TreeUIHelper; -import com.intellij.ui.speedSearch.SpeedSearchSupply; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.Objects; -import java.util.Set; - -/** - * UI settings which show: - * - *
    - *
  • list of language server as master on the left
  • - *
  • the settings detail of the selected language server on the right
  • - *
- */ -public class LanguageServerListConfigurable extends MasterDetailsComponent implements SearchableConfigurable { - - @NonNls - private static final String ID = "LanguageServers"; - - private final Project project; - private final Set languageServeDefinitions; - - private boolean isTreeInitialized; - - public LanguageServerListConfigurable(Project project) { - this.project = project; - this.languageServeDefinitions = LanguageServersRegistry.getInstance().getAllDefinitions(); - } - - @Override - @NotNull - public JComponent createComponent() { - if (!isTreeInitialized) { - initTree(); - isTreeInitialized = true; - } - return super.createComponent(); - } - - @Override - public @NotNull @NonNls String getId() { - return ID; - } - - @Override - public @NlsContexts.ConfigurableName String getDisplayName() { - return LanguageServerBundle.message("language.servers"); - } - - @Nullable - @Override - public Runnable enableSearch(final String option) { - return () -> Objects.requireNonNull(SpeedSearchSupply.getSupply(myTree, true)).findAndSelectElement(option); - } - - @Override - protected void initTree() { - super.initTree(); - TreeUIHelper.getInstance() - .installTreeSpeedSearch(myTree, treePath -> ((MyNode) treePath.getLastPathComponent()).getDisplayName(), true); - } - - private MyNode addLanguageServerDefinitionNode(LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) { - MyNode node = new MyNode(new LanguageServerConfigurable(languageServerDefinition, TREE_UPDATER, project)); - addNode(node, myRoot); - return node; - } - - private void reloadTree() { - myRoot.removeAllChildren(); - for (LanguageServersRegistry.LanguageServerDefinition languageServeDefinition : languageServeDefinitions) { - addLanguageServerDefinitionNode(languageServeDefinition); - } - } - - @Override - public void reset() { - reloadTree(); - super.reset(); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerView.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerView.java deleted file mode 100644 index 4d1bb5771..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/LanguageServerView.java +++ /dev/null @@ -1,124 +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 com.redhat.devtools.intellij.lsp4ij.settings; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.ui.ComboBox; -import com.intellij.ui.IdeBorderFactory; -import com.intellij.ui.PortField; -import com.intellij.ui.components.JBCheckBox; -import com.intellij.util.ui.FormBuilder; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UI; -import com.redhat.devtools.intellij.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import java.awt.*; - -/** - * UI settings view to configure a given language server: - * - *
    - *
  • Debug port
  • - *
  • Suspend and wait for a debugger?
  • - *
- */ -public class LanguageServerView implements Disposable { - - private final JPanel myMainPanel; - private final PortField debugPortField = new PortField(); - private final JBCheckBox debugSuspendCheckBox = new JBCheckBox(LanguageServerBundle.message("language.server.debug.suspend")); - private final ComboBox serverTraceComboBox = new ComboBox<>(new DefaultComboBoxModel<>(ServerTrace.values())); - - public LanguageServerView(LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) { - JComponent descriptionPanel = createDescription(languageServerDefinition.description.trim()); - JPanel settingsPanel = createSettings(descriptionPanel); - TitledBorder title = IdeBorderFactory.createTitledBorder(languageServerDefinition.getDisplayName()); - settingsPanel.setBorder(title); - JPanel wrapper = JBUI.Panels.simplePanel(settingsPanel); - wrapper.setBorder(JBUI.Borders.emptyLeft(10)); - this.myMainPanel = wrapper; - } - - private JPanel createSettings(JComponent description) { - return FormBuilder.createFormBuilder() - .addComponent(description, 0) - .addLabeledComponent(LanguageServerBundle.message("language.server.debug.port"), debugPortField, 5) - .addComponent(debugSuspendCheckBox, 5) - .addLabeledComponent(LanguageServerBundle.message("language.server.trace"), serverTraceComboBox, 5) - .addComponentFillVertically(new JPanel(), 0) - .getPanel(); - } - - private JComponent createDescription(String description) { - /** - * Normally comments are below the controls. - * Here we want the comments to precede the controls, we therefore create an empty, 0-sized panel. - */ - JPanel titledComponent = UI.PanelFactory.grid().createPanel(); - titledComponent.setMinimumSize(JBUI.emptySize()); - titledComponent.setPreferredSize(JBUI.emptySize()); - if (description != null && !description.isBlank()) { - titledComponent = UI.PanelFactory.panel(titledComponent) - .withComment(description) - .resizeX(true) - .resizeY(true) - .createPanel(); - } - return titledComponent; - } - - public JComponent getComponent() { - return myMainPanel; - } - - public String getDebugPort() { - return debugPortField.getNumber() <= 0? "": Integer.toString(debugPortField.getNumber()); - } - - public void setDebugPort(String debugPort) { - int port = 0; - try { - port = Integer.parseInt(debugPort); - if (port < debugPortField.getMin() || port > debugPortField.getMax()) { - port = 0; - } - } catch (Exception ignore) {} - debugPortField.setNumber(port); - } - - public boolean isDebugSuspend() { - return debugSuspendCheckBox.isSelected(); - } - - public void setDebugSuspend(boolean debugSuspend) { - debugSuspendCheckBox.setSelected(debugSuspend); - } - - public ServerTrace getServerTrace() { - return (ServerTrace) serverTraceComboBox.getSelectedItem(); - } - - public void setServerTrace(ServerTrace serverTrace) { - serverTraceComboBox.setSelectedItem(serverTrace); - } - - @Override - public void dispose() { - - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/ServerTrace.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/ServerTrace.java deleted file mode 100644 index 37b5aace7..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/ServerTrace.java +++ /dev/null @@ -1,36 +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 com.redhat.devtools.intellij.lsp4ij.settings; - -/** - * Language server trace level. - */ -public enum ServerTrace { - - off, - messages, - verbose; - - public static ServerTrace getServerTrace(String serverTrace) { - if (serverTrace == null || serverTrace.isEmpty()) { - return ServerTrace.off; - } - try { - return ServerTrace.valueOf(serverTrace); - } - catch(Exception e) { - return ServerTrace.off; - } - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/UserDefinedLanguageServerSettings.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/UserDefinedLanguageServerSettings.java deleted file mode 100644 index 3469637a0..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/settings/UserDefinedLanguageServerSettings.java +++ /dev/null @@ -1,142 +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 com.redhat.devtools.intellij.lsp4ij.settings; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.*; -import com.intellij.openapi.project.Project; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.xmlb.annotations.Tag; -import com.intellij.util.xmlb.annotations.XCollection; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * User defined language server settings for a given Language server definition - * - *
    - *
  • Debug port
  • - *
  • Suspend and wait for a debugger
  • - *
  • Trace LSP requests/responses/notifications
  • - *
- */ -@State( - name = "LanguageServerSettingsState", - storages = @Storage("LanguageServersSettings.xml") -) -public class UserDefinedLanguageServerSettings implements PersistentStateComponent { - - private volatile MyState myState = new MyState(); - - private final List myChangeHandlers = ContainerUtil.createConcurrentList(); - - public static UserDefinedLanguageServerSettings getInstance(@NotNull Project project) { - return project.getService(UserDefinedLanguageServerSettings.class); - } - - @Nullable - @Override - public MyState getState() { - return myState; - } - - @Override - public void loadState(@NotNull MyState state) { - myState = state; - } - - public LanguageServerDefinitionSettings getLanguageServerSettings(String languageSeverId) { - return myState.myState.get(languageSeverId); - } - - public void setLanguageServerSettings(String languageSeverId, LanguageServerDefinitionSettings settings) { - myState.myState.put(languageSeverId, settings); - fireStateChanged(); - } - - public static class LanguageServerDefinitionSettings { - - private String debugPort; - - private boolean debugSuspend; - - private ServerTrace serverTrace; - - public String getDebugPort() { - return debugPort; - } - - public void setDebugPort(String debugPort) { - this.debugPort = debugPort; - } - - public boolean isDebugSuspend() { - return debugSuspend; - } - - public void setDebugSuspend(boolean debugSuspend) { - this.debugSuspend = debugSuspend; - } - - public ServerTrace getServerTrace() { - return serverTrace; - } - - public void setServerTrace(ServerTrace serverTrace) { - this.serverTrace = serverTrace; - } - } - - public static class MyState { - @Tag("state") - @XCollection - public Map myState = new TreeMap<>(); - - MyState() { - } - - } - - /** - * Adds the given changeHandler to the list of registered change handlers - * @param changeHandler the changeHandler to remove - */ - public void addChangeHandler(@NotNull Runnable changeHandler) { - myChangeHandlers.add(changeHandler); - } - - /** - * Removes the given changeHandler from the list of registered change handlers - * @param changeHandler the changeHandler to remove - */ - public void removeChangeHandler(@NotNull Runnable changeHandler) { - myChangeHandlers.remove(changeHandler); - } - - /** - * Notifies all registered change handlers when the state changed - */ - public void fireStateChanged() { - for (Runnable handler : myChangeHandlers) { - handler.run(); - } - } - -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapper.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapper.java deleted file mode 100644 index 7b6059712..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapper.java +++ /dev/null @@ -1,105 +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 com.redhat.devtools.intellij.lsp4ij.ui; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.util.IconLoader; -import org.eclipse.lsp4j.CompletionItemKind; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.Icon; - -/** - * Maps LSP4J kinds to Intellij Icons. See the JetBrains icon list for reference. - */ -public class IconMapper { - - // Copied from IntelliJ icons. To be removed once the minimal supported version of IDEA is > 213 - // See https://github.com/JetBrains/intellij-community/blob/50157fc8eec4af77f67bd468ada4dff39daa1b88/platform/util/ui/src/com/intellij/icons/AllIcons.java#L415 - // Original https://github.com/JetBrains/intellij-community/blob/50157fc8eec4af77f67bd468ada4dff39daa1b88/platform/icons/src/nodes/template.svg - public static final @NotNull Icon Template = load("images/nodes/template.svg"); - - // Copied from IntelliJ icons. To be removed once the minimal supported version of IDEA is > 232 - // See https://github.com/JetBrains/intellij-community/blob/50157fc8eec4af77f67bd468ada4dff39daa1b88/platform/util/ui/src/com/intellij/icons/ExpUiIcons.java#L226 - // Original light https://github.com/JetBrains/intellij-community/blob/50157fc8eec4af77f67bd468ada4dff39daa1b88/platform/icons/src/expui/fileTypes/text.svg - // Original dark https://github.com/JetBrains/intellij-community/blob/50157fc8eec4af77f67bd468ada4dff39daa1b88/platform/icons/src/expui/fileTypes/text_dark.svg - public static final @NotNull Icon Text = load("images/expui/fileTypes/text.svg"); - - - private IconMapper(){ - } - - - /** - * Maps LSP4J {@link CompletionItemKind} to Intellij Icons - */ - public static @Nullable Icon getIcon(@Nullable CompletionItemKind kind) { - if (kind == null) { - return null; - } - - switch (kind) { - case Snippet: - return Template; - case Text: - return Text; - case Constructor: - return AllIcons.Nodes.ClassInitializer; - case Method: - return AllIcons.Nodes.Method; - case Function: - return AllIcons.Nodes.Function; - case EnumMember://No matching icon, IDEA show enum members as fields - case Field: - return AllIcons.Nodes.Field; - case Value: //No matching icon - case Reference: - case Variable: - return AllIcons.Nodes.Variable; - case Class: - return AllIcons.Nodes.Class; - case Interface: - return AllIcons.Nodes.Interface; - case Module: - return AllIcons.Nodes.Module; - case Property: - return AllIcons.Nodes.Property; - case Unit: - return AllIcons.Nodes.Test; - case Enum: - return AllIcons.Nodes.Enum; - case File: - return AllIcons.FileTypes.Any_type; - case Folder: - return AllIcons.Nodes.Folder; - case Constant: - case Keyword: - return AllIcons.Nodes.Constant; - case TypeParameter: - return AllIcons.Nodes.Parameter; - //No matching icons, no fallback - case Struct: - case Event: - case Operator: - case Color: - default: - return AllIcons.Nodes.EmptyNode; - } - } - - private static @NotNull Icon load(String iconPath) { - return IconLoader.getIcon(iconPath, IconMapper.class); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/Messages.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/Messages.java deleted file mode 100644 index df51a3df2..000000000 --- a/src/main/java/com/redhat/devtools/intellij/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 com.redhat.devtools.intellij.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/com/redhat/devtools/intellij/lsp4ij/ui/components/InspectionHyperlink.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/components/InspectionHyperlink.java deleted file mode 100644 index 7d9e13ac2..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ui/components/InspectionHyperlink.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 com.redhat.devtools.intellij.lsp4ij.ui.components; - -import com.intellij.ide.DataManager; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.options.ex.Settings; -import com.intellij.openapi.util.NlsContexts; -import com.intellij.profile.codeInspection.ui.ErrorsConfigurable; -import com.intellij.ui.HyperlinkLabel; -import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.event.HyperlinkEvent; - -/** - * HyperlinkLabel that opens an inspection settings page - */ -public class InspectionHyperlink extends HyperlinkLabel { - - /** - * Creates a new hyperlink to an inspection settings page - * @param label the hyperlink label - * @param inspectionGroupPath the group path to open in the inspections settings - */ - public InspectionHyperlink(@NotNull @NlsContexts.LinkLabel String label, @NotNull String inspectionGroupPath) { - super(label); - addHyperlinkListener(e -> { - if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) { - @NotNull DataContext dataContext = DataManager.getInstance().getDataContext(this); - @Nullable Settings settings = Settings.KEY.getData(dataContext); - if (settings != null) { - @Nullable ErrorsConfigurable inspections = settings.find(ErrorsConfigurable.class); - if (inspections != null) { - settings.select(inspections).doWhenDone(new Runnable() { - @Override - public void run() { - inspections.selectInspectionGroup(new String[]{inspectionGroupPath}); - } - }); - } - } - } - }); - } -} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java index 8e6e70195..b15f5684e 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedListener.java @@ -27,7 +27,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiTreeChangeAdapter; import com.intellij.psi.PsiTreeChangeEvent; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/AbstractStaticPropertiesProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/AbstractStaticPropertiesProvider.java index 23bae3c48..f2c9b8ab1 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/AbstractStaticPropertiesProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/AbstractStaticPropertiesProvider.java @@ -6,7 +6,6 @@ import com.intellij.util.Query; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.IPropertiesCollector.MergingStrategy; import org.eclipse.lsp4mp.commons.metadata.ConfigurationMetadata; -import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -118,6 +117,8 @@ protected InputStream getInputStream() throws IOException { } private static Gson createGson() { + // EnumTypeAdapter from LSP4J should be used, but we cannot use EnumTypeAdapter from LSP4J + // coming from LSP4IJ to avoid classpath issues we use a copy of EnumTypeAdapter return new GsonBuilder().registerTypeAdapterFactory(new EnumTypeAdapter.Factory()).create(); } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/EnumTypeAdapter.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/EnumTypeAdapter.java new file mode 100644 index 000000000..751ccfc6f --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/EnumTypeAdapter.java @@ -0,0 +1,106 @@ +/****************************************************************************** + * Copyright (c) 2016-2017 TypeFox 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 Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + * Copied from https://github.com/eclipse-lsp4j/lsp4j/blob/main/org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/json/adapters/EnumTypeAdapter.java + ******************************************************************************/ +package com.redhat.devtools.intellij.lsp4mp4ij.psi.core; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * A custom type adapter for enums that uses integer values. + */ +public class EnumTypeAdapter> extends TypeAdapter { + + public static class Factory implements TypeAdapterFactory { + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Class rawType = typeToken.getRawType(); + if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) + return null; + if (!rawType.isEnum()) + rawType = rawType.getSuperclass(); + try { + return new EnumTypeAdapter(rawType); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + } + + private static String VALUE_FIELD_NAME = "value"; + + private final Map nameToConstant = new HashMap<>(); + private final Map valueToConstant = new HashMap<>(); + private final Map constantToValue = new HashMap<>(); + + EnumTypeAdapter(Class classOfT) throws IllegalAccessException { + try { + Field valueField = classOfT.getDeclaredField(VALUE_FIELD_NAME); + if (valueField.getType() != int.class && valueField.getType() != Integer.class) + throw new IllegalArgumentException("The field 'value' must contain an integer value."); + valueField.setAccessible(true); + for (T constant : classOfT.getEnumConstants()) { + nameToConstant.put(constant.name(), constant); + Integer constValue = (Integer) valueField.get(constant); + valueToConstant.put(constValue, constant); + constantToValue.put(constant, constValue); + } + } catch (NoSuchFieldException e) { + for (T constant : classOfT.getEnumConstants()) { + nameToConstant.put(constant.name(), constant); + int constValue = constant.ordinal(); + valueToConstant.put(constValue, constant); + constantToValue.put(constant, constValue); + } + } + } + + @Override + public T read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + if (peek == JsonToken.NULL) { + in.nextNull(); + return null; + } else if (peek == JsonToken.NUMBER) { + return valueToConstant.get(in.nextInt()); + } else { + String string = in.nextString(); + try { + return valueToConstant.get(Integer.parseInt(string)); + } catch (NumberFormatException e) { + return nameToConstant.get(string); + } + } + } + + @Override + public void write(JsonWriter out, T value) throws IOException { + if (value != null) + out.value(constantToValue.get(value)); + else + out.value((String) null); + } + +} diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java index 6478e4783..8e79cb439 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileOpenURIAction.java +++ b/src/main/java/com/redhat/devtools/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 com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import java.util.List; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileUpdateConfigurationAction.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileUpdateConfigurationAction.java index 9170f6d80..791832dec 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileUpdateConfigurationAction.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/command/MicroprofileUpdateConfigurationAction.java @@ -24,8 +24,8 @@ import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.FakePsiElement; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections.MicroProfilePropertiesUnassignedInspection; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections.MicroProfilePropertiesUnknownInspection; import org.eclipse.lsp4j.Command; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesDuplicatesInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesDuplicatesInspection.java index 310b2ed9a..1697f133a 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesDuplicatesInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesDuplicatesInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for duplicate properties in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesExpressionsInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesExpressionsInspection.java index 5438b3a2f..0e831b5c8 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesExpressionsInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesExpressionsInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for expression values in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesGlobalInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesGlobalInspection.java index 24a8702be..c01c085cc 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesGlobalInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesGlobalInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for general validation in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesRequiredInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesRequiredInspection.java index 9fd408288..97a7bb371 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesRequiredInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesRequiredInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for missing required properties in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesSyntaxInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesSyntaxInspection.java index 13a318021..c2130750b 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesSyntaxInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesSyntaxInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for property syntax errors in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnassignedInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnassignedInspection.java index f0791a415..0b3e035db 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnassignedInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnassignedInspection.java @@ -13,9 +13,8 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; -import org.jetbrains.annotations.NotNull; /** * Dummy inspection for unknown properties in Microprofile properties files diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnknownInspection.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnknownInspection.java index 5ca04a4b7..7886d2841 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnknownInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/inspections/MicroProfilePropertiesUnknownInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; import java.util.Arrays; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java index 1b86db9b3..1d6f58d5b 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/core/ls/PsiUtilsLSImpl.java @@ -16,7 +16,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -30,7 +29,7 @@ import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.PsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.quarkus.javadoc.JavadocContentAccess; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4mp.commons.ClasspathKind; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java index d7b1e4679..087a2f782 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/internal/jaxrs/java/DefaultJaxRsInfoProvider.java @@ -19,7 +19,7 @@ import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileInspectionsInfo.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileInspectionsInfo.java index e994db2c7..ba53b0de3 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileInspectionsInfo.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileInspectionsInfo.java @@ -17,8 +17,8 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.openapi.project.Project; import com.intellij.profile.codeInspection.InspectionProfileManager; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.operations.diagnostics.SeverityMapping; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.inspections.*; import org.eclipse.lsp4j.DiagnosticSeverity; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileView.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileView.java index a565972ff..0d8e6b9d8 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileView.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/MicroProfileView.java @@ -16,7 +16,7 @@ import com.intellij.openapi.Disposable; import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.JBUI; -import com.redhat.devtools.intellij.lsp4ij.ui.components.InspectionHyperlink; +import com.redhat.devtools.lsp4ij.ui.components.InspectionHyperlink; import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; import javax.swing.*; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/UserDefinedMicroProfileSettings.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/UserDefinedMicroProfileSettings.java index b41f9d0d7..bb62176f8 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/UserDefinedMicroProfileSettings.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/UserDefinedMicroProfileSettings.java @@ -19,7 +19,7 @@ import com.intellij.openapi.project.Project; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.annotations.Tag; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping; +import com.redhat.devtools.lsp4ij.operations.diagnostics.SeverityMapping; import org.eclipse.lsp4j.DiagnosticSeverity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/java/MicroProfileJavaView.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/java/MicroProfileJavaView.java index 1336c59ca..e808645db 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/java/MicroProfileJavaView.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/java/MicroProfileJavaView.java @@ -19,7 +19,6 @@ import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UI; -import com.redhat.devtools.intellij.lsp4ij.LanguageServersRegistry; import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; import javax.swing.*; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/properties/MicroProfilePropertiesConfigurable.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/properties/MicroProfilePropertiesConfigurable.java index f24577b82..ec68df35a 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/properties/MicroProfilePropertiesConfigurable.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/settings/properties/MicroProfilePropertiesConfigurable.java @@ -17,7 +17,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.NamedConfigurable; import com.intellij.openapi.util.NlsContexts; -import com.redhat.devtools.intellij.lsp4ij.settings.UserDefinedLanguageServerSettings; import com.redhat.devtools.intellij.lsp4mp4ij.MicroProfileBundle; import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings; diff --git a/src/main/java/com/redhat/devtools/intellij/microprofile/lang/MicroProfileServerIconProvider.java b/src/main/java/com/redhat/devtools/intellij/microprofile/lang/MicroProfileServerIconProvider.java index aec9742b4..b7ab0555d 100644 --- a/src/main/java/com/redhat/devtools/intellij/microprofile/lang/MicroProfileServerIconProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/microprofile/lang/MicroProfileServerIconProvider.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.microprofile.lang; -import com.redhat.devtools.intellij.lsp4ij.ServerIconProvider; +import com.redhat.devtools.lsp4ij.ServerIconProvider; import javax.swing.*; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java index cf7b70f5f..c27ebf699 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java @@ -20,7 +20,7 @@ import com.intellij.openapi.roots.RootPolicy; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.quarkus.facet.QuarkusFacet; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/json/QuarkusJsonSchemaProvider.java b/src/main/java/com/redhat/devtools/intellij/quarkus/json/QuarkusJsonSchemaProvider.java index 6b919f102..384337199 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/json/QuarkusJsonSchemaProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/json/QuarkusJsonSchemaProvider.java @@ -14,7 +14,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; import com.jetbrains.jsonSchema.extension.SchemaType; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; import com.redhat.devtools.intellij.quarkus.QuarkusProjectService; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lang/QuarkusServerIconProvider.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lang/QuarkusServerIconProvider.java index 4f7bc9ae6..03c3e9727 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lang/QuarkusServerIconProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lang/QuarkusServerIconProvider.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.quarkus.lang; -import com.redhat.devtools.intellij.lsp4ij.ServerIconProvider; +import com.redhat.devtools.lsp4ij.ServerIconProvider; import javax.swing.*; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java index 409b349c2..68c9cb25b 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/AbstractQuarkusDocumentMatcher.java @@ -13,8 +13,8 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.AbstractDocumentMatcher; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.AbstractDocumentMatcher; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; /** diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java index 4fd094b62..95dc79e8c 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusLanguageClient.java @@ -21,8 +21,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.ProfileChangeAdapter; import com.intellij.util.messages.MessageBusConnection; -import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey; -import com.redhat.devtools.intellij.lsp4ij.client.IndexAwareLanguageClient; +import com.redhat.devtools.lsp4ij.client.CoalesceByKey; +import com.redhat.devtools.lsp4ij.client.IndexAwareLanguageClient; import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.ProjectLabelManager; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.PropertiesManager; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusServer.java b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusServer.java index cec31d4b9..7efe4ac61 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusServer.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/lsp/QuarkusServer.java @@ -11,18 +11,16 @@ package com.redhat.devtools.intellij.quarkus.lsp; import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.server.JavaProcessCommandBuilder; -import com.redhat.devtools.intellij.lsp4ij.server.ProcessStreamConnectionProvider; +import com.redhat.devtools.lsp4ij.server.JavaProcessCommandBuilder; +import com.redhat.devtools.lsp4ij.server.ProcessStreamConnectionProvider; import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings; import com.redhat.devtools.intellij.quarkus.TelemetryService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.net.URI; import java.nio.file.Path; import java.util.Arrays; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusExtensionsStep.java b/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusExtensionsStep.java index dbcda9852..e71d7be74 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusExtensionsStep.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusExtensionsStep.java @@ -20,7 +20,7 @@ import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ui.JBUI; -import com.redhat.devtools.intellij.lsp4ij.internal.StringUtils; +import com.redhat.devtools.lsp4ij.internal.StringUtils; import com.redhat.devtools.intellij.quarkus.QuarkusConstants; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java index e8f3f0f94..151197f1f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/QuteLanguageSubstitutor.java @@ -22,7 +22,7 @@ import com.intellij.openapi.roots.RootPolicy; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.LanguageSubstitutor; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java index 968fde66b..c9a2222e8 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteFileIndentOptionsProvider.java @@ -26,7 +26,7 @@ import com.intellij.psi.codeStyle.FileIndentOptionsProvider; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.qute.lang.QuteFileType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java index d02a19e16..ef2c4d917 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/AbstractQuteDocumentMatcher.java @@ -13,8 +13,8 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.AbstractDocumentMatcher; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.AbstractDocumentMatcher; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; /** diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java index 16d03e032..b66d54683 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteDocumentMatcherForTemplateFile.java @@ -12,7 +12,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.isQuteTemplate; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java index 558e3ec23..b40fa5ec4 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteLanguageClient.java @@ -21,8 +21,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.ProfileChangeAdapter; import com.intellij.util.messages.MessageBusConnection; -import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey; -import com.redhat.devtools.intellij.lsp4ij.client.IndexAwareLanguageClient; +import com.redhat.devtools.lsp4ij.client.CoalesceByKey; +import com.redhat.devtools.lsp4ij.client.IndexAwareLanguageClient; import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteServer.java b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteServer.java index 1146a6808..1b908e4ce 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteServer.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/lsp/QuteServer.java @@ -11,18 +11,16 @@ package com.redhat.devtools.intellij.qute.lsp; import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.server.JavaProcessCommandBuilder; -import com.redhat.devtools.intellij.lsp4ij.server.ProcessStreamConnectionProvider; +import com.redhat.devtools.lsp4ij.server.JavaProcessCommandBuilder; +import com.redhat.devtools.lsp4ij.server.ProcessStreamConnectionProvider; import com.redhat.devtools.intellij.quarkus.TelemetryService; import com.redhat.devtools.intellij.qute.settings.UserDefinedQuteSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.net.URI; import java.nio.file.Path; import java.util.Arrays; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteAction.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteAction.java index 45fdd6465..e25a8d4ef 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteAction.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteAction.java @@ -13,7 +13,7 @@ import com.google.gson.JsonPrimitive; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import java.util.List; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteGenerateTemplateAction.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteGenerateTemplateAction.java index 37c038aed..cc686768f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteGenerateTemplateAction.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteGenerateTemplateAction.java @@ -16,8 +16,8 @@ import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LanguageServiceAccessor; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.LanguageServiceAccessor; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.ExecuteCommandOptions; import org.eclipse.lsp4j.ExecuteCommandParams; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteJavaDefinitionAction.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteJavaDefinitionAction.java index c553008e9..cbcb971d9 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteJavaDefinitionAction.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteJavaDefinitionAction.java @@ -14,8 +14,8 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.qute.psi.QuteSupportForTemplate; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteOpenURIAction.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteOpenURIAction.java index a88df2f0b..56064fef2 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteOpenURIAction.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteOpenURIAction.java @@ -12,7 +12,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; public class QuteOpenURIAction extends QuteAction { diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteUpdateConfigurationAction.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteUpdateConfigurationAction.java index e05e2e1e2..7d06c5e7e 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteUpdateConfigurationAction.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/command/QuteUpdateConfigurationAction.java @@ -29,10 +29,10 @@ import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.FakePsiElement; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping; +import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.operations.diagnostics.SeverityMapping; import com.redhat.devtools.intellij.qute.psi.core.inspections.QuteGlobalInspection; import com.redhat.devtools.intellij.qute.psi.core.inspections.QuteUndefinedObjectInspection; import org.eclipse.lsp4j.Command; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteGlobalInspection.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteGlobalInspection.java index 9fc0db9c0..81fa69b34 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteGlobalInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteGlobalInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.qute.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; import com.redhat.devtools.intellij.qute.QuteBundle; /** diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedNamespaceInspection.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedNamespaceInspection.java index 1e2a28a9e..9b25ba6ca 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedNamespaceInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedNamespaceInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.qute.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for undefined namespaces in Qute templates diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedObjectInspection.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedObjectInspection.java index 575b9d65d..87c9b5942 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedObjectInspection.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/core/inspections/QuteUndefinedObjectInspection.java @@ -13,7 +13,7 @@ *******************************************************************************/ package com.redhat.devtools.intellij.qute.psi.core.inspections; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspection; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspection; /** * Dummy inspection for undefined objects in Qute templates diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java index de4d5b16f..ec68cf521 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java @@ -20,7 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.qute.psi.internal.AnnotationLocationSupport; import com.redhat.devtools.intellij.qute.psi.utils.AnnotationUtils; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/QuteJavaCodeLensCollector.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/QuteJavaCodeLensCollector.java index dde31daed..abb1366d3 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/QuteJavaCodeLensCollector.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/QuteJavaCodeLensCollector.java @@ -15,7 +15,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.qute.psi.QuteCommandConstants; import com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java index fca2fa112..8fb5ebd24 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java @@ -24,7 +24,7 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.DataModelProviderRegistry; import com.redhat.qute.commons.QuteProjectScope; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java index 278251cfc..c8fc70641 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/utils/PsiQuteProjectUtils.java @@ -17,7 +17,7 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants; import com.redhat.qute.commons.ProjectInfo; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteInspectionsInfo.java b/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteInspectionsInfo.java index b17546799..187aab645 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteInspectionsInfo.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteInspectionsInfo.java @@ -17,8 +17,8 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.openapi.project.Project; import com.intellij.profile.codeInspection.InspectionProfileManager; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping; -import com.redhat.devtools.intellij.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; +import com.redhat.devtools.lsp4ij.operations.diagnostics.SeverityMapping; +import com.redhat.devtools.lsp4ij.inspections.AbstractDelegateInspectionWithExclusions; import com.redhat.devtools.intellij.qute.psi.core.inspections.QuteGlobalInspection; import com.redhat.devtools.intellij.qute.psi.core.inspections.QuteUndefinedNamespaceInspection; import com.redhat.devtools.intellij.qute.psi.core.inspections.QuteUndefinedObjectInspection; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteView.java b/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteView.java index cc765d7c6..bb34491a6 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteView.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/settings/QuteView.java @@ -17,8 +17,7 @@ import com.intellij.ui.components.JBCheckBox; import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UI; -import com.redhat.devtools.intellij.lsp4ij.ui.components.InspectionHyperlink; +import com.redhat.devtools.lsp4ij.ui.components.InspectionHyperlink; import com.redhat.devtools.intellij.qute.QuteBundle; import javax.swing.*; diff --git a/src/main/java/com/redhat/devtools/intellij/qute/settings/UserDefinedQuteSettings.java b/src/main/java/com/redhat/devtools/intellij/qute/settings/UserDefinedQuteSettings.java index 22e987516..ce4269a93 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/settings/UserDefinedQuteSettings.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/settings/UserDefinedQuteSettings.java @@ -19,7 +19,7 @@ import com.intellij.openapi.project.Project; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.annotations.Tag; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping; +import com.redhat.devtools.lsp4ij.operations.diagnostics.SeverityMapping; import org.eclipse.lsp4j.DiagnosticSeverity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java index 4e5e27270..c03f75ee6 100644 --- a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/renarde/java/RenardeJaxRsInfoProvider.java @@ -17,7 +17,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.psi.*; import com.intellij.util.KeyedLazyInstanceEP; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; diff --git a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java index f47521c67..b902a8e29 100644 --- a/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java +++ b/src/main/java/com/redhat/microprofile/psi/internal/quarkus/route/java/ReactiveRouteJaxRsInfoProvider.java @@ -17,7 +17,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.psi.*; import com.intellij.util.KeyedLazyInstanceEP; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils; diff --git a/src/main/resources/META-INF/lsp4ij-quarkus.xml b/src/main/resources/META-INF/lsp4ij-quarkus.xml index 0bf0fe817..e83e63180 100644 --- a/src/main/resources/META-INF/lsp4ij-quarkus.xml +++ b/src/main/resources/META-INF/lsp4ij-quarkus.xml @@ -1,5 +1,5 @@ - + - - - - - - + + + - - + implementationClass="com.redhat.devtools.lsp4ij.operations.inlayhint.LSPInlayHintInlayProvider"/> + - + + + + @@ -27,27 +36,25 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/META-INF/lsp4ij.xml b/src/main/resources/META-INF/lsp4ij.xml deleted file mode 100644 index c884e9c91..000000000 --- a/src/main/resources/META-INF/lsp4ij.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 639e97ad1..e66c12605 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -323,6 +323,7 @@ com.intellij.modules.lang com.intellij.modules.java com.intellij.properties + com.redhat.devtools.lsp4ij com.redhat.devtools.intellij.telemetry org.jetbrains.idea.maven org.jetbrains.plugins.gradle @@ -580,7 +581,6 @@ - diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AdvancedTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AdvancedTest.java deleted file mode 100644 index 9076f8230..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/AdvancedTest.java +++ /dev/null @@ -1,39 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class AdvancedTest { - - @Test - public void textAndlaceholdersAndTabStop() { - LspSnippetNode[] actual = parse("{#for ${1:item} in ${2:items}}\n\t{${1:item}.${3:name}}$0\n{/for}"); - assertEquals(actual, text("{#for "), // - placeholder(1, "item", 1), // ${1:item} - text(" in "), // - placeholder(2, "items", 1), // ${2:items} - text("}\n\t{"), // - placeholder(1, "item", 1), // ${1:item} - text("."), // - placeholder(3, "name", 1), // ${3:name} - text("}"), // - tabstop(0), // - text("\n{/for}")); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ChoiceTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ChoiceTest.java deleted file mode 100644 index afe6908d0..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ChoiceTest.java +++ /dev/null @@ -1,29 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class ChoiceTest { - - @Test - public void simpleText() { - LspSnippetNode[] actual = parse("${1|auto,idea,vscode,eclipse,netbeans|}"); - assertEquals(actual, choice(1, "auto", "idea", "vscode", "eclipse", "netbeans")); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ExtractSnippetLinkedPositionTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ExtractSnippetLinkedPositionTest.java deleted file mode 100644 index 60388255a..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/ExtractSnippetLinkedPositionTest.java +++ /dev/null @@ -1,57 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LinkedPositionResult; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class ExtractSnippetLinkedPositionTest { - - @Test - public void linkedPositions() { - LinkedPositionResult actual = parseLinkedPosition( - "{#for ${1:item} in ${2:items}}\n\t{${1:item}.${3:name}}$0\n{/for}", null); - assertEquals(actual, "{#for item in items}\n\t{item.name}\n{/for}", // - position("item", 6, 4), // - position("items", 14, 5), // - position("item", 23, 4), // - position("name", 28, 4)); - } - - @Test - public void linkedPositionsWithIndentOptions() { - LspSnippetIndentOptions indentOptions = new LspSnippetIndentOptions(4, true, "\r\n"); - LinkedPositionResult actual = parseLinkedPosition( - "{#for ${1:item} in ${2:items}}\n\t{${1:item}.${3:name}}$0\n{/for}", indentOptions); - assertEquals(actual, "{#for item in items}\r\n {item.name}\r\n{/for}", // - position("item", 6, 4), // - position("items", 14, 5), // - position("item", 27, 4), // - position("name", 32, 4)); - } - - @Test - public void linkedPositionsWithIndentOptionsAndCLRF() { - LspSnippetIndentOptions indentOptions = new LspSnippetIndentOptions(4, true, "\r\n"); - LinkedPositionResult actual = parseLinkedPosition( - "{#for ${1:item} in ${2:items}}\r\n\t{${1:item}.${3:name}}$0\r\n{/for}", indentOptions); - assertEquals(actual, "{#for item in items}\r\n {item.name}\r\n{/for}", // - position("item", 6, 4), // - position("items", 14, 5), // - position("item", 27, 4), // - position("name", 32, 4)); - } -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetAssert.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetAssert.java deleted file mode 100644 index f8b467f3f..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/LspSnippetAssert.java +++ /dev/null @@ -1,85 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.*; -import org.junit.Assert; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import static org.junit.Assert.assertArrayEquals; - -public class LspSnippetAssert { - private LspSnippetAssert() { - - } - - public static LspSnippetNode[] parse(String snippet) { - LspSnippetHandlerImpl handler = new LspSnippetHandlerImpl(); - LspSnippetParser parser = new LspSnippetParser(handler); - parser.parse(snippet); - return handler.getNodes(); - } - - public static LinkedPositionResult parseLinkedPosition(String snippet, LspSnippetIndentOptions indentOptions) { - ExtractSnippetLinkedPositionHandler handler = new ExtractSnippetLinkedPositionHandler(indentOptions); - LspSnippetParser parser = new LspSnippetParser(handler); - parser.parse(snippet); - return handler.getResult(); - } - - public static void assertEquals(LspSnippetNode[] actual, LspSnippetNode... expected) { - assertArrayEquals(expected, actual); - } - - public static TabstopNode tabstop(int index) { - return new TabstopNode(index); - } - - public static VariableNode variable(String name) { - return new VariableNode(name); - } - - public static TextNode text(String text) { - return new TextNode(text); - } - - public static PlaceholderNode placeholder(int index, String name, int level) { - return new PlaceholderNode(index, name, level); - } - - public static ChoiceNode choice(int index, String... choices) { - return choice(index, null, choices); - } - - public static ChoiceNode choice(String name, String... choices) { - return choice(null, name, choices); - } - - private static ChoiceNode choice(Integer index, String name, String... choices) { - List list = Arrays.stream(choices).collect(Collectors.toList()); - return new ChoiceNode(index, name, list); - } - - public static void assertEquals(LinkedPositionResult actual, String templateContent, LinkedPosition... positions) { - Assert.assertEquals(templateContent, actual.getTemplateContent()); - assertArrayEquals(positions, actual.getLinkedPositions()); - } - - public static LinkedPosition position(String name, int offset, int length) { - return new LinkedPosition(name, offset, length); - } -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/PlaceholderTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/PlaceholderTest.java deleted file mode 100644 index fbbec32fc..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/PlaceholderTest.java +++ /dev/null @@ -1,29 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class PlaceholderTest { - - @Test - public void simpleText() { - LspSnippetNode[] actual = parse("${1:name}"); - assertEquals(actual, placeholder(1, "name", 1)); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TabstopTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TabstopTest.java deleted file mode 100644 index a9941ba8e..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TabstopTest.java +++ /dev/null @@ -1,52 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class TabstopTest { - - @Test - public void onlyTabstop() { - LspSnippetNode[] actual = parse("$123"); - assertEquals(actual, tabstop(123)); - } - - @Test - public void tabstopWithText() { - LspSnippetNode[] actual = parse("abcd $123 efgh"); - assertEquals(actual, - text("abcd "), // - tabstop(123), // - text(" efgh")); - } - - @Test - public void tabstopInBracket() { - LspSnippetNode[] actual = parse("${123}"); - assertEquals(actual, tabstop(123)); - } - - @Test - public void tabstopInBracketWithText() { - LspSnippetNode[] actual = parse("abcd ${123} efgh"); - assertEquals(actual, text("abcd "), // - tabstop(123), // - text(" efgh")); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TextTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TextTest.java deleted file mode 100644 index 0664ed68d..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/TextTest.java +++ /dev/null @@ -1,29 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class TextTest { - - @Test - public void simpleText() { - LspSnippetNode[] actual = parse("abcd efgh"); - assertEquals(actual, text("abcd efgh")); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/VariableTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/VariableTest.java deleted file mode 100644 index 12a655ecd..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/VariableTest.java +++ /dev/null @@ -1,52 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler.LspSnippetNode; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetAssert.*; - -public class VariableTest { - - @Test - public void onlyVariable() { - LspSnippetNode[] actual = parse("$name"); - assertEquals(actual, variable("name")); - } - - @Test - public void variableWithText() { - LspSnippetNode[] actual = parse("abcd $name efgh"); - assertEquals(actual, - text("abcd "), // - variable("name"), // - text(" efgh")); - } - - @Test - public void variableInBracket() { - LspSnippetNode[] actual = parse("${name}"); - assertEquals(actual, variable("name")); - } - - @Test - public void variableInBracketWithText() { - LspSnippetNode[] actual = parse("abcd ${name} efgh"); - assertEquals(actual, text("abcd "), // - variable("name"), // - text(" efgh")); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ChoiceNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ChoiceNode.java deleted file mode 100644 index 9dc9efa3f..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ChoiceNode.java +++ /dev/null @@ -1,87 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -import java.util.List; -import java.util.stream.Collectors; - -public class ChoiceNode implements LspSnippetNode { - - private final Integer index; - - private final String name; - - private final List choices; - - public ChoiceNode(Integer index, String name, List choices) { - this.index = index; - this.name = name; - this.choices = choices; - } - - public Integer getIndex() { - return index; - } - - public String getName() { - return name; - } - - public List getChoices() { - return choices; - } - - @Override - public String toString() { - return "choice(" + index + "," + name + ",[" + choices.stream().collect(Collectors.joining(",")) + "])"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((choices == null) ? 0 : choices.hashCode()); - result = prime * result + ((index == null) ? 0 : index.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ChoiceNode other = (ChoiceNode) obj; - if (choices == null) { - if (other.choices != null) - return false; - } else if (!choices.equals(other.choices)) - return false; - if (index == null) { - if (other.index != null) - return false; - } else if (!index.equals(other.index)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ExtractSnippetLinkedPositionHandler.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ExtractSnippetLinkedPositionHandler.java deleted file mode 100644 index c69029514..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/ExtractSnippetLinkedPositionHandler.java +++ /dev/null @@ -1,81 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.DefaultLspSnippetHandler; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetIndentOptions; -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetVariableConstants; - -import java.util.ArrayList; -import java.util.List; - -public class ExtractSnippetLinkedPositionHandler extends DefaultLspSnippetHandler { - - private final List linkedPositions; - - public ExtractSnippetLinkedPositionHandler(LspSnippetIndentOptions indentOptions) { - super(name -> { - switch (name) { - case LspSnippetVariableConstants.TM_FILENAME: - return "foo.txt"; - } - return name; - }, indentOptions); - this.linkedPositions = new ArrayList<>(); - } - - @Override - public void startPlaceholder(int index, String name, int level) { - // ex : ${1:name} - addLinkedPosition(name); - super.startPlaceholder(index, name, level); - } - - @Override - public void variable(String name) { - String resolved = super.resolveVariable(name); - if (resolved == null) { - addLinkedPosition(name); - } - super.variable(resolved != null ? resolved : name); - } - - @Override - public void choice(int index, List choices) { - String value = choices.isEmpty() ? "" : choices.get(0); - addLinkedPosition(value); - super.choice(index, choices); - } - - @Override - public void choice(String name, List choices) { - addLinkedPosition(name); - super.choice(name, choices); - } - - private void addLinkedPosition(String name) { - int offset = getCurrentOffset(); - int length = name != null ? name.length() : 0; - linkedPositions.add(new LinkedPosition(name, offset, length)); - } - - public List getLinkedPositions() { - return linkedPositions; - } - - public LinkedPositionResult getResult() { - return new LinkedPositionResult(getTemplateContent(), linkedPositions); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPosition.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPosition.java deleted file mode 100644 index ede115430..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPosition.java +++ /dev/null @@ -1,80 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public class LinkedPosition { - - private final String name; - - private final int offset; - - private final int length; - - public LinkedPosition(String name, int offset, int length) { - this.name = name; - this.offset = offset; - this.length = length; - } - - public String getName() { - return name; - } - - public int getOffset() { - return offset; - } - - public int getLength() { - return length; - } - - - @Override - public String toString() { - return "position(" + getName() + "," + getOffset() + "," + getLength() + ")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + length; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + offset; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LinkedPosition other = (LinkedPosition) obj; - if (length != other.length) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (offset != other.offset) - return false; - return true; - } - - -} \ No newline at end of file diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPositionResult.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPositionResult.java deleted file mode 100644 index 18c1ebf8b..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LinkedPositionResult.java +++ /dev/null @@ -1,39 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -import java.util.List; - -public class LinkedPositionResult { - - private final String templateContent; - - private final List linkedPositions; - - public LinkedPositionResult(String templateContent, List linkedPositions) { - super(); - this.templateContent = templateContent; - this.linkedPositions = linkedPositions; - } - - public String getTemplateContent() { - return templateContent; - } - - - public LinkedPosition[] getLinkedPositions() { - return linkedPositions.toArray(new LinkedPosition[linkedPositions.size()]); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetHandlerImpl.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetHandlerImpl.java deleted file mode 100644 index 3308ba0de..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetHandlerImpl.java +++ /dev/null @@ -1,83 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -import com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.LspSnippetHandler; - -import java.util.ArrayList; -import java.util.List; - -public class LspSnippetHandlerImpl implements LspSnippetHandler { - - private final List nodes; - - private final List placeholderStack; - - public LspSnippetHandlerImpl() { - this.nodes = new ArrayList<>(); - this.placeholderStack = new ArrayList<>(); - } - - @Override - public void startSnippet() { - - } - - @Override - public void endSnippet() { - - } - - @Override - public void text(String text) { - nodes.add(new TextNode(text)); - } - - @Override - public void tabstop(int index) { - nodes.add(new TabstopNode(index)); - } - - @Override - public void variable(String name) { - nodes.add(new VariableNode(name)); - } - - @Override - public void startPlaceholder(int index, String name, int level) { - PlaceholderNode placeholder = new PlaceholderNode(index, name, level); - placeholderStack.add(placeholder); - nodes.add(placeholder); - } - - @Override - public void endPlaceholder(int level) { - placeholderStack.remove(level - 1); - } - - @Override - public void choice(int index, List choices) { - nodes.add(new ChoiceNode(index, null, choices)); - } - - @Override - public void choice(String name, List choices) { - nodes.add(new ChoiceNode(null, name, choices)); - } - - public LspSnippetNode[] getNodes() { - return nodes.toArray(new LspSnippetNode[nodes.size()]); - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetNode.java deleted file mode 100644 index af1d29861..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/LspSnippetNode.java +++ /dev/null @@ -1,18 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public interface LspSnippetNode { - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/PlaceholderNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/PlaceholderNode.java deleted file mode 100644 index 5cc597750..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/PlaceholderNode.java +++ /dev/null @@ -1,76 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public class PlaceholderNode implements LspSnippetNode { - - private final int index; - private final String name; - private final int level; - - public PlaceholderNode(int index, String name, int level) { - this.index = index; - this.name = name; - this.level = level; - } - - public int getIndex() { - return index; - } - - public String getName() { - return name; - } - - public int getLevel() { - return level; - } - - @Override - public String toString() { - return "placeholder(" + index + "," + name + "," + level + ")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + index; - result = prime * result + level; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PlaceholderNode other = (PlaceholderNode) obj; - if (index != other.index) - return false; - if (level != other.level) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TabstopNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TabstopNode.java deleted file mode 100644 index 82e7fe217..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TabstopNode.java +++ /dev/null @@ -1,55 +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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public class TabstopNode implements LspSnippetNode { - - private final int index; - - public TabstopNode(int index) { - this.index = index; - } - - public int getIndex() { - return index; - } - - @Override - public String toString() { - return "tabstop(" + index + ")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + index; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - TabstopNode other = (TabstopNode) obj; - if (index != other.index) - return false; - return true; - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TextNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TextNode.java deleted file mode 100644 index 6d7796493..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/TextNode.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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public class TextNode implements LspSnippetNode { - - private final String text; - - public TextNode(String text) { - this.text = text; - } - - public String getText() { - return text; - } - - @Override - public String toString() { - return "text(" + getText() + ")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((text == null) ? 0 : text.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - TextNode other = (TextNode) obj; - if (text == null) { - if (other.text != null) - return false; - } else if (!text.equals(other.text)) - return false; - return true; - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/VariableNode.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/VariableNode.java deleted file mode 100644 index 451c77d53..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/snippet/handler/VariableNode.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 com.redhat.devtools.intellij.lsp4ij.operations.completion.snippet.handler; - -public class VariableNode implements LspSnippetNode { - - private final String name; - - public VariableNode(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return "variable(" + getName() + ")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VariableNode other = (VariableNode) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; - } - -} diff --git a/src/test/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapperTest.java b/src/test/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapperTest.java deleted file mode 100644 index d962f854d..000000000 --- a/src/test/java/com/redhat/devtools/intellij/lsp4ij/ui/IconMapperTest.java +++ /dev/null @@ -1,32 +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 com.redhat.devtools.intellij.lsp4ij.ui; - -import org.eclipse.lsp4j.CompletionItemKind; -import org.junit.Test; - -import static com.redhat.devtools.intellij.lsp4ij.ui.IconMapper.getIcon; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class IconMapperTest { - - @Test - public void getIconTest() { - assertNull(getIcon(null)); - for (CompletionItemKind value : CompletionItemKind.values()) { - assertNotNull(getIcon(value), "Missing matching icon for "+value); - } - } -} diff --git a/src/test/java/com/redhat/devtools/intellij/quarkus/completion/MavenApplicationPropertiesCompletionTest.java b/src/test/java/com/redhat/devtools/intellij/quarkus/completion/MavenApplicationPropertiesCompletionTest.java index 24f26082f..2117e9f0c 100644 --- a/src/test/java/com/redhat/devtools/intellij/quarkus/completion/MavenApplicationPropertiesCompletionTest.java +++ b/src/test/java/com/redhat/devtools/intellij/quarkus/completion/MavenApplicationPropertiesCompletionTest.java @@ -9,7 +9,6 @@ *******************************************************************************/ package com.redhat.devtools.intellij.quarkus.completion; -import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.module.Module; @@ -18,7 +17,6 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.intellij.MavenEditorTest; -import com.redhat.devtools.intellij.lsp4ij.ConnectDocumentToLanguageServerSetupParticipant; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.MicroProfileMavenProjectName; import com.redhat.devtools.intellij.quarkus.QuarkusDeploymentSupport; import org.junit.Test; diff --git a/src/test/java/com/redhat/devtools/intellij/quarkus/validation/BuildItemDiagnosticsTest.java b/src/test/java/com/redhat/devtools/intellij/quarkus/validation/BuildItemDiagnosticsTest.java index c6767a489..b258fd09d 100644 --- a/src/test/java/com/redhat/devtools/intellij/quarkus/validation/BuildItemDiagnosticsTest.java +++ b/src/test/java/com/redhat/devtools/intellij/quarkus/validation/BuildItemDiagnosticsTest.java @@ -10,15 +10,9 @@ package com.redhat.devtools.intellij.quarkus.validation; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtilCore; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.intellij.MavenModuleImportingTestCase; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; -import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.java.MicroProfileHealthErrorCode; import com.redhat.devtools.intellij.quarkus.QuarkusConstants; import com.redhat.devtools.intellij.quarkus.psi.internal.builditems.QuarkusBuildItemErrorCode; import org.eclipse.lsp4j.Diagnostic; diff --git a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaCodeLensTest.java b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaCodeLensTest.java index fb5e34e35..b672523f2 100644 --- a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaCodeLensTest.java +++ b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaCodeLensTest.java @@ -15,11 +15,8 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.redhat.devtools.intellij.GradleTestCase; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; -import com.redhat.devtools.intellij.qute.psi.QuteMavenModuleImportingTestCase; -import com.redhat.devtools.intellij.qute.psi.QuteMavenProjectName; import com.redhat.devtools.intellij.qute.psi.QuteSupportForJava; import com.redhat.qute.commons.QuteJavaCodeLensParams; import org.apache.commons.io.FileUtils; diff --git a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaDiagnosticsTest.java b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaDiagnosticsTest.java index 612e623fa..0bf657e0c 100644 --- a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaDiagnosticsTest.java +++ b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/GradleJavaDiagnosticsTest.java @@ -15,10 +15,8 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.redhat.devtools.intellij.GradleTestCase; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; -import com.redhat.devtools.intellij.qute.psi.QuteMavenModuleImportingTestCase; -import com.redhat.devtools.intellij.qute.psi.QuteMavenProjectName; import com.redhat.devtools.intellij.qute.psi.QuteSupportForJava; import com.redhat.devtools.intellij.qute.psi.internal.java.QuteErrorCode; import com.redhat.qute.commons.QuteJavaDiagnosticsParams; diff --git a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaCodeLensTest.java b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaCodeLensTest.java index 81fb658cc..3eeb489cc 100644 --- a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaCodeLensTest.java +++ b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaCodeLensTest.java @@ -13,7 +13,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.qute.psi.QuteMavenModuleImportingTestCase; import com.redhat.devtools.intellij.qute.psi.QuteMavenProjectName; diff --git a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDiagnosticsTest.java b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDiagnosticsTest.java index e54be88cf..c9792549c 100644 --- a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDiagnosticsTest.java +++ b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDiagnosticsTest.java @@ -13,7 +13,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.qute.psi.QuteMavenModuleImportingTestCase; import com.redhat.devtools.intellij.qute.psi.QuteMavenProjectName; diff --git a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDocumentLinkTest.java b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDocumentLinkTest.java index 864937fcd..2d3ee2b93 100644 --- a/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDocumentLinkTest.java +++ b/src/test/java/com/redhat/devtools/intellij/qute/psi/java/MavenJavaDocumentLinkTest.java @@ -14,7 +14,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.qute.psi.QuteMavenModuleImportingTestCase; import com.redhat.devtools.intellij.qute.psi.QuteMavenProjectName;