diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java index 60dbcb528..b56fed0d3 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/ConnectDocumentToLanguageServerSetupParticipant.java @@ -46,21 +46,21 @@ public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile f if (DumbService.isDumb(project)) { // Force the start of all languages servers mapped with the given file when indexation is finished DumbService.getInstance(project).runWhenSmart(() -> { - startLanguageServer(source, document); + startLanguageServer(source, file); }); } else { // Force the start of all languages servers mapped with the given file immediately - startLanguageServer(source, document); + startLanguageServer(source, file); } } } - private static void startLanguageServer(@NotNull FileEditorManager source, Document document) { + private static void startLanguageServer(@NotNull FileEditorManager source, @NotNull VirtualFile file) { // 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(source.getProject()) - .getLanguageServers(document, null); + .getLanguageServers(file, null); } } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java index 25c73a27f..c76bc8c28 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/DocumentContentSynchronizer.java @@ -21,10 +21,10 @@ 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 javax.annotation.Nonnull; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -33,29 +33,30 @@ public class DocumentContentSynchronizer implements DocumentListener { private final static Logger LOGGER = LoggerFactory.getLogger(DocumentContentSynchronizer.class); - private final @Nonnull LanguageServerWrapper languageServerWrapper; - private final @Nonnull Document document; - private final @Nonnull URI fileUri; + private final @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 @Nonnull + final @NotNull CompletableFuture didOpenFuture; - public DocumentContentSynchronizer(@Nonnull LanguageServerWrapper languageServerWrapper, - @Nonnull Document document, + public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServerWrapper, + @NotNull URI fileUri, + @NotNull Document document, TextDocumentSyncKind syncKind) { this.languageServerWrapper = languageServerWrapper; - this.fileUri = LSPIJUtils.toUri(document); + 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(fileUri.toString()); + textDocument.setUri(this.fileUri); textDocument.setText(document.getText()); Language contentTypes = LSPIJUtils.getDocumentLanguage(document, languageServerWrapper.getProject()); diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java index 5adb53889..9e2ab6278 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; 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; @@ -140,12 +141,20 @@ public static URI toUri(Document document) { return file != null ? toUri(file) : null; } - public static VirtualFile getFile(Document document) { + public static @Nullable VirtualFile getFile(Document document) { return FileDocumentManager.getInstance().getFile(document); } - public static Document getDocument(VirtualFile docFile) { - return FileDocumentManager.getInstance().getDocument(docFile); + 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)); } /** @@ -405,11 +414,11 @@ public static Language getDocumentLanguage(Document document, Project project) { return getFileLanguage(file, project); } - public static VirtualFile findResourceFor(URI uri) { + public static @Nullable VirtualFile findResourceFor(URI uri) { return LocalFileSystem.getInstance().findFileByIoFile(Paths.get(uri).toFile()); } - public static VirtualFile findResourceFor(String uri) { + 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 { @@ -423,7 +432,7 @@ public static VirtualFile findResourceFor(String uri) { public static Editor[] editorsForFile(VirtualFile file) { Editor[] editors = new Editor[0]; - Document document = FileDocumentManager.getInstance().getDocument(file); + Document document = getDocument(file); if (document != null) { editors = editorsForFile(file, document); } @@ -518,4 +527,5 @@ public static String getProjectUri(Project project) { } 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 index 9ed3f55c7..4add3006b 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileData.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileData.java @@ -16,6 +16,10 @@ 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. @@ -28,16 +32,31 @@ public class LSPVirtualFileData { private final LSPDocumentLinkForServer documentLinkForServer; - public LSPVirtualFileData(LanguageServerWrapper languageServerWrapper, VirtualFile file) { + 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 LSPDiagnosticsForServer getLSPDiagnosticsForServer() { + 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/LSPVirtualFileWrapper.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java deleted file mode 100644 index 0ee78ab38..000000000 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPVirtualFileWrapper.java +++ /dev/null @@ -1,191 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 Red Hat Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 - * which is available at https://www.apache.org/licenses/LICENSE-2.0. - * - * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 - * - * Contributors: - * Red Hat Inc. - initial API and implementation - * Some piece of code has been inspired by https://github.com/ballerina-platform/lsp4intellij/blob/master/src/main/java/org/wso2/lsp4intellij/editor/EditorEventManager.java - *******************************************************************************/ -package com.redhat.devtools.intellij.lsp4ij; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.LSPDiagnosticsForServer; -import com.redhat.devtools.intellij.lsp4ij.operations.documentLink.LSPDocumentLinkForServer; -import com.redhat.devtools.intellij.lsp4ij.operations.documentation.LSPTextHoverForFile; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DocumentLink; -import org.eclipse.lsp4j.MarkupContent; - -import java.util.List; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * LSP wrapper for VirtualFile which maintains the current diagnostics - * for all language servers mapped with this file. - * - * @author Angelo ZERR - */ -public class LSPVirtualFileWrapper implements Disposable { - - private static final Key KEY = new Key<>(LSPVirtualFileWrapper.class.getName()); - - private final VirtualFile file; - - private final Map dataPerServer; - private final LSPTextHoverForFile hover; - - LSPVirtualFileWrapper(VirtualFile file) { - this.file = file; - this.dataPerServer = new HashMap<>(); - this.hover = new LSPTextHoverForFile(); - Module project = LSPIJUtils.getModule(file); - if (project != null) { - Disposer.register(project, this); - } - } - - public VirtualFile getFile() { - return file; - } - - // ------------------------ LSP Diagnostics - - /** - * Update the new published diagnostics for the given language server id. - * - * @param diagnostics the new diagnostics list - * @param languageServerWrapper the language server id which has published those diagnostics. - */ - public void updateDiagnostics(List diagnostics, LanguageServerWrapper languageServerWrapper) { - LSPDiagnosticsForServer diagnosticsForServer = getLSPVirtualFileData(languageServerWrapper).getLSPDiagnosticsForServer(); - diagnosticsForServer.update(diagnostics); - } - - /** - * Returns all current diagnostics reported by all language servers mapped with the file. - * - * @return all current diagnostics reported by all language servers mapped with the file. - */ - public Collection getAllDiagnostics() { - return getData(LSPVirtualFileData::getLSPDiagnosticsForServer); - } - - // ------------------------ LSP textDocument/documentLink - - /** - * Update the new collected documentLinks for the given language server id. - * - * @param documentLinks the new documentLink list - * @param languageServerWrapper the language server id which has collected those documentLinks. - */ - public void updateDocumentLink(List documentLinks, LanguageServerWrapper languageServerWrapper) { - LSPDocumentLinkForServer documentLinkForServer = getLSPVirtualFileData(languageServerWrapper).getDocumentLinkForServer(); - documentLinkForServer.update(documentLinks); - } - - /** - * Returns all current document link reported by all language servers mapped with the file. - * - * @return all current document link reported by all language servers mapped with the file. - */ - public Collection getAllDocumentLink() { - return getData(LSPVirtualFileData::getDocumentLinkForServer); - } - - - public List getHoverContent(PsiElement element, int targetOffset, Editor editor) { - return hover.getHoverContent(element, targetOffset, editor); - } - - // ------------------------ Other methods - - private LSPVirtualFileData getLSPVirtualFileData(LanguageServerWrapper languageServerWrapper) { - LSPVirtualFileData dataForServer = dataPerServer.get(languageServerWrapper); - if (dataForServer != null) { - return dataForServer; - } - return getOrCreateLSPVirtualFileData(languageServerWrapper); - } - - private synchronized LSPVirtualFileData getOrCreateLSPVirtualFileData(LanguageServerWrapper languageServerWrapper) { - LSPVirtualFileData dataForServer = dataPerServer.get(languageServerWrapper); - if (dataForServer != null) { - return dataForServer; - } - dataForServer = new LSPVirtualFileData(languageServerWrapper, getFile()); - dataPerServer.put(languageServerWrapper, dataForServer); - return dataForServer; - } - - private Collection getData(Function mapper) { - if (dataPerServer.isEmpty()) { - return Collections.emptyList(); - } - return dataPerServer - .values() - .stream() - .map(mapper) - .collect(Collectors.toList()); - } - - // ------------------------ Static accessor - - @Override - public void dispose() { - file.putUserData(KEY, null); - this.dataPerServer.clear(); - } - - /** - * Returns the LSPVirtualFileWrapper for the given file. - * - * @param file the virtual file. - * @return the LSPVirtualFileWrapper for the given file. - */ - public static LSPVirtualFileWrapper getLSPVirtualFileWrapper(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - return wrapper; - } - return getOrCreateLSPVirtualFileWrapper(file); - } - - private static synchronized LSPVirtualFileWrapper getOrCreateLSPVirtualFileWrapper(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - return wrapper; - } - wrapper = new LSPVirtualFileWrapper(file); - file.putUserData(KEY, wrapper); - return wrapper; - } - - public static boolean hasWrapper(VirtualFile file) { - return file != null && file.getUserData(KEY) != null; - } - - public static void dispose(VirtualFile file) { - LSPVirtualFileWrapper wrapper = file.getUserData(KEY); - if (wrapper != null) { - wrapper.dispose(); - } - } - -} \ 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 index e82c696f6..a336ca1bb 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServerWrapper.java @@ -17,6 +17,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; @@ -37,11 +38,10 @@ 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 javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.net.URI; @@ -72,8 +72,6 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f URI uri = LSPIJUtils.toUri(file); if (uri != null) { try { - // Remove the cached file wrapper if needed - LSPVirtualFileWrapper.dispose(file); // Disconnect the given file from the current language servers disconnect(uri, !isDisposed()); } catch (Exception e) { @@ -82,7 +80,6 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f } } - @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { if (event.getPropertyName().equals(VirtualFile.PROP_NAME) && event.getOldValue() instanceof String) { @@ -101,10 +98,10 @@ public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { public void contentsChanged(@NotNull VirtualFileEvent event) { URI uri = LSPIJUtils.toUri(event.getFile()); if (uri != null) { - DocumentContentSynchronizer documentListener = connectedDocuments.get(uri); + LSPVirtualFileData documentListener = connectedDocuments.get(uri); if (documentListener != null) { // 1. Send a textDocument/didSave for the saved file - documentListener.documentSaved(); + documentListener.getSynchronizer().documentSaved(); } // 2. Send a workspace/didChangeWatchedFiles didChangeWatchedFiles(fe(uri, FileChangeType.Changed)); @@ -148,8 +145,7 @@ private FileEvent fe(URI uri, FileChangeType type) { private @NotNull URI didClose(VirtualFile virtualParentFile, String fileName) { File parent = VfsUtilCore.virtualToIoFile(virtualParentFile); URI uri = LSPIJUtils.toUri(new File(parent, fileName)); - DocumentContentSynchronizer documentListener = connectedDocuments.get(uri); - if (documentListener != null) { + if (isConnectedTo(uri)) { disconnect(uri, false); } return uri; @@ -157,22 +153,22 @@ private FileEvent fe(URI uri, FileChangeType type) { private void didChangeWatchedFiles(FileEvent... changes) { LanguageServerWrapper.this.sendNotification(ls -> { - DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(changes)); - ls.getWorkspaceService() - .didChangeWatchedFiles(params); - }); + DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(changes)); + ls.getWorkspaceService() + .didChangeWatchedFiles(params); + }); } } private Listener fileBufferListener = new Listener(); private MessageBusConnection messageBusConnection; - @Nonnull + @NotNull public final LanguageServersRegistry.LanguageServerDefinition serverDefinition; @Nullable protected final Project initialProject; - @Nonnull - protected Map connectedDocuments; + @NotNull + protected Map connectedDocuments; @Nullable protected final URI initialPath; protected final InitializeParams initParams = new InitializeParams(); @@ -205,23 +201,23 @@ private void didChangeWatchedFiles(FileEvent... changes) { /** * Map containing unregistration handlers for dynamic capability registrations. */ - private @Nonnull + private @NotNull Map dynamicRegistrations = new HashMap<>(); private boolean initiallySupportsWorkspaceFolders = false; /* Backwards compatible constructor */ - public LanguageServerWrapper(@Nonnull Project project, @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition) { + public LanguageServerWrapper(@NotNull Project project, @NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition) { this(project, serverDefinition, null); } - public LanguageServerWrapper(@Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @Nullable URI initialPath) { + 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, @Nonnull LanguageServersRegistry.LanguageServerDefinition serverDefinition, + private LanguageServerWrapper(@Nullable Project project, @NotNull LanguageServersRegistry.LanguageServerDefinition serverDefinition, @Nullable URI initialPath) { this.initialProject = project; this.initialPath = initialPath; @@ -301,16 +297,13 @@ public synchronized void start() throws LanguageServerException { numberOfRestartAttempts++; } } - final var filesToReconnect = new HashMap(); + final var filesToReconnect = new ArrayList(); if (this.languageServer != null) { if (isActive()) { return; } else { - for (Map.Entry entry : this.connectedDocuments.entrySet()) { - // Get the Document from the VirtualFile to use the proper Document Instance - // (not an old instanceof the synchronizer which could be out of dated). - Document document = getDocument(entry.getKey(), entry.getValue()); - filesToReconnect.put(entry.getKey(), document); + for (Map.Entry entry : this.connectedDocuments.entrySet()) { + filesToReconnect.add(entry.getKey()); } stop(); } @@ -389,11 +382,11 @@ public synchronized void start() throws LanguageServerException { }).thenRun(() -> { this.languageServer.initialized(new InitializedParams()); }).thenRun(() -> { - final Map toReconnect = filesToReconnect; + final List toReconnect = filesToReconnect; initializeFuture.thenRunAsync(() -> { - for (Map.Entry fileToReconnect : toReconnect.entrySet()) { + for (URI fileToReconnect : toReconnect) { try { - connect(fileToReconnect.getKey(), fileToReconnect.getValue()); + connect(fileToReconnect); } catch (IOException e) { throw new RuntimeException(e); } @@ -423,25 +416,6 @@ public synchronized void start() throws LanguageServerException { } } - /** - * Returns the document instance from the virtual file which matches the given uri and the document from teh synchronizer otherwise. - * - * @param uri the document Uri - * @param synchronizer the document synchronizer. - * @return the document instance from the virtual file which matches the given uri and the document from teh synchronizer otherwise. - */ - private static Document getDocument(URI uri, DocumentContentSynchronizer synchronizer) { - Document document = synchronizer.getDocument(); - VirtualFile file = LSPIJUtils.findResourceFor(uri); - if (file != null) { - Document documentFromFile = LSPIJUtils.getDocument(file); - if (documentFromFile != null) { - document = documentFromFile; - } - } - return document; - } - private CompletableFuture initServer(final URI rootURI) { final var workspaceClientCapabilities = SupportedFeatures.getWorkspaceClientCapabilities(); @@ -676,34 +650,17 @@ private void exitLanguageServerInstance(LanguageServer languageServerInstance) t } /** - * @param file - * @param document - * @return null if not connection has happened, a future tracking the connection state otherwise - * @throws IOException - */ - public @Nullable - CompletableFuture connect(@Nonnull VirtualFile file, Document document) throws IOException { - return connect(LSPIJUtils.toUri(file), document); - } - - /** - * @param document + * 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 @Nullable - CompletableFuture connect(Document document) throws IOException { - VirtualFile file = LSPIJUtils.getFile(document); - + public CompletableFuture<@Nullable LanguageServer> connect(VirtualFile file) throws IOException { if (file != null && file.exists()) { - return connect(file, document); - } else { - URI uri = LSPIJUtils.toUri(document); - if (uri != null) { - return connect(uri, document); - } + return connect(LSPIJUtils.toUri(file)); } - return null; + return CompletableFuture.completedFuture(null); } /** @@ -727,28 +684,23 @@ public boolean canOperate(Project project) { * @return null if not connection has happened, a future that completes when file is initialized otherwise * @noreference internal so far */ - private CompletableFuture connect(@Nonnull URI absolutePath, Document document) throws IOException { + private CompletableFuture connect(@NotNull URI fileUri) throws IOException { removeStopTimer(false); - final URI thePath = absolutePath; // should be useless - if (this.connectedDocuments.containsKey(thePath)) { + if (this.connectedDocuments.containsKey(fileUri)) { return CompletableFuture.completedFuture(languageServer); } start(); if (this.initializeFuture == null) { - return null; - } - if (document == null) { - VirtualFile docFile = LSPIJUtils.findResourceFor(thePath); - document = LSPIJUtils.getDocument(docFile); + return CompletableFuture.completedFuture(null); } - if (document == null) { - return null; + VirtualFile file = LSPIJUtils.findResourceFor(fileUri); + if (file == null) { + return CompletableFuture.completedFuture(null); } - final Document theDocument = document; return initializeFuture.thenComposeAsync(theVoid -> { synchronized (connectedDocuments) { - if (this.connectedDocuments.containsKey(thePath)) { + if (this.connectedDocuments.containsKey(fileUri)) { return CompletableFuture.completedFuture(null); } Either syncOptions = initializeFuture == null ? null @@ -761,10 +713,15 @@ private CompletableFuture connect(@Nonnull URI absolutePath, Doc syncKind = syncOptions.getLeft(); } } - DocumentContentSynchronizer listener = new DocumentContentSynchronizer(this, theDocument, syncKind); - theDocument.addDocumentListener(listener); - LanguageServerWrapper.this.connectedDocuments.put(thePath, listener); - return listener.didOpenFuture; + + 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); } @@ -774,11 +731,12 @@ private void disconnect(URI path) { } private void disconnect(URI path, boolean stopIfNoOpenedFiles) { - DocumentContentSynchronizer documentListener = this.connectedDocuments.remove(path); - if (documentListener != null) { + LSPVirtualFileData data = this.connectedDocuments.remove(path); + if (data != null) { // Remove the listener from the old document stored in synchronizer - documentListener.getDocument().removeDocumentListener(documentListener); - documentListener.documentClosed(); + DocumentContentSynchronizer synchronizer = data.getSynchronizer(); + synchronizer.getDocument().removeDocumentListener(synchronizer); + synchronizer.documentClosed(); } if (stopIfNoOpenedFiles && this.connectedDocuments.isEmpty()) { if (this.serverDefinition.lastDocumentDisconnectedTimeout != 0 && !ApplicationManager.getApplication().isUnitTestMode()) { @@ -799,6 +757,10 @@ 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. @@ -821,7 +783,7 @@ public LanguageServer getServer() { * server to be initialized. If done in the UI stream, a job will be created * displaying that the server is being initialized */ - @Nonnull + @NotNull public CompletableFuture getInitializedServer() { try { start(); @@ -854,7 +816,7 @@ protected Void compute(@NotNull ProgressIndicator indicator) throws Exception { * * @param fn LS notification to send */ - public void sendNotification(@Nonnull Consumer fn) { + 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 @@ -953,7 +915,7 @@ public void registerCapability(RegistrationParams params) { }); } - private void addRegistration(@Nonnull Registration reg, @Nonnull Runnable unregistrationHandler) { + 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$ @@ -1022,23 +984,28 @@ void unregisterCommands(List cmds) { } int getVersion(VirtualFile file) { - if (file != null && LSPIJUtils.toUri(file) != null) { - DocumentContentSynchronizer documentContentSynchronizer = connectedDocuments.get(LSPIJUtils.toUri(file)); - if (documentContentSynchronizer != null) { - return documentContentSynchronizer.getVersion(); + 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(@Nonnull Document document) { - if (this.isConnectedTo(LSPIJUtils.toUri(document))) { + public boolean canOperate(@NotNull VirtualFile file) { + if (this.isConnectedTo(LSPIJUtils.toUri(file))) { return true; } if (this.initialProject == null && this.connectedDocuments.isEmpty()) { return true; } - VirtualFile file = LSPIJUtils.getFile(document); if (file != null && file.exists() && canOperate(LSPIJUtils.getProject(file))) { return true; } diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java index fd822afb3..3f2720ecf 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java @@ -12,19 +12,17 @@ import com.intellij.lang.Language; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.module.Module; 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 javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.IOException; import java.net.URI; import java.util.*; @@ -51,21 +49,21 @@ private LanguageServiceAccessor(Project project) { private final Set startedServers = new HashSet<>(); private Map providersToLSDefinitions = new HashMap<>(); - @Nonnull - public CompletableFuture> getLanguageServers(@Nonnull Document document, + @NotNull + public CompletableFuture> getLanguageServers(@NotNull VirtualFile file, Predicate filter) { - URI uri = LSPIJUtils.toUri(document); + URI uri = LSPIJUtils.toUri(file); if (uri == null) { return CompletableFuture.completedFuture(Collections.emptyList()); } final List servers = Collections.synchronizedList(new ArrayList<>()); try { - return CompletableFuture.allOf(getLSWrappers(document).stream().map(wrapper -> + return CompletableFuture.allOf(getLSWrappers(file).stream().map(wrapper -> wrapper.getInitializedServer() .thenComposeAsync(server -> { if (server != null && wrapper.isEnabled() && (filter == null || filter.test(wrapper.getServerCapabilities()))) { try { - return wrapper.connect(document); + return wrapper.connect(file); } catch (IOException ex) { LOGGER.warn(ex.getLocalizedMessage(), ex); } @@ -104,24 +102,24 @@ public void projectClosing(Project project) { } /** - * Get the requested language server instance for the given document. Starts the + * Get the requested language server instance for the given file. Starts the * language server if not already started. * - * @param document the document for which the initialized LanguageServer shall be returned - * @param serverId the ID of the LanguageServer to be returned - * @param capabilitesPredicate a predicate to check capabilities + * @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(Document document, + public CompletableFuture getInitializedLanguageServer(VirtualFile file, LanguageServersRegistry.LanguageServerDefinition lsDefinition, Predicate capabilitiesPredicate) throws IOException { - URI initialPath = LSPIJUtils.toUri(document); - LanguageServerWrapper wrapper = getLSWrapperForConnection(document, lsDefinition, initialPath); + URI initialPath = LSPIJUtils.toUri(file); + LanguageServerWrapper wrapper = getLSWrapperForConnection(file, lsDefinition, initialPath); if (wrapper != null && capabilitiesComply(wrapper, capabilitiesPredicate)) { - wrapper.connect(document); + wrapper.connect(file); return wrapper.getInitializedServer(); } return null; @@ -146,11 +144,10 @@ private static boolean capabilitiesComply(LanguageServerWrapper wrapper, || capabilitiesPredicate.test(wrapper.getServerCapabilities()); } - @Nonnull - private Collection getLSWrappers(@Nonnull Document document) { + @NotNull + private Collection getLSWrappers(@NotNull VirtualFile file) { LinkedHashSet res = new LinkedHashSet<>(); - VirtualFile file = LSPIJUtils.getFile(document); - URI uri = LSPIJUtils.toUri(document); + URI uri = LSPIJUtils.toUri(file); if (uri == null) { return Collections.emptyList(); } @@ -159,14 +156,14 @@ private Collection getLSWrappers(@Nonnull Document docume // look for running language servers via content-type Queue contentTypes = new LinkedList<>(); Set processedContentTypes = new HashSet<>(); - contentTypes.add(LSPIJUtils.getDocumentLanguage(document, project)); + contentTypes.add(LSPIJUtils.getFileLanguage(file, project)); synchronized (startedServers) { // already started compatible servers that fit request res.addAll(startedServers.stream() .filter(wrapper -> { try { - return wrapper.isEnabled() && (wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(document, wrapper.serverDefinition, project)); + return wrapper.isEnabled() && (wrapper.isConnectedTo(path) || LanguageServersRegistry.getInstance().matches(file, wrapper.serverDefinition, project)); } catch (ProcessCanceledException cancellation) { throw cancellation; } catch (Exception e) { @@ -174,7 +171,7 @@ private Collection getLSWrappers(@Nonnull Document docume return false; } }) - .filter(wrapper -> wrapper.canOperate(document)) + .filter(wrapper -> wrapper.canOperate(file)) .collect(Collectors.toList())); while (!contentTypes.isEmpty()) { @@ -192,7 +189,7 @@ private Collection getLSWrappers(@Nonnull Document docume continue; } if (startedServers.stream().anyMatch(wrapper -> wrapper.serverDefinition.equals(serverDefinition) - && wrapper.canOperate(document))) { + && wrapper.canOperate(file))) { // we already checked a compatible LS with this definition continue; } @@ -209,7 +206,7 @@ private Collection getLSWrappers(@Nonnull Document docume } } - private LanguageServerWrapper getLSWrapperForConnection(Document document, + 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 @@ -218,7 +215,7 @@ private LanguageServerWrapper getLSWrapperForConnection(Document document, LanguageServerWrapper wrapper = null; synchronized (startedServers) { - for (LanguageServerWrapper startedWrapper : getStartedLSWrappers(document)) { + for (LanguageServerWrapper startedWrapper : getStartedLSWrappers(file)) { if (startedWrapper.serverDefinition.equals(serverDefinition)) { wrapper = startedWrapper; break; @@ -235,8 +232,8 @@ private LanguageServerWrapper getLSWrapperForConnection(Document document, } private List getStartedLSWrappers( - Document document) { - return getStartedLSWrappers(wrapper -> wrapper.canOperate(document)); + VirtualFile file) { + return getStartedLSWrappers(wrapper -> wrapper.canOperate(file)); } private List getStartedLSWrappers(Predicate predicate) { @@ -246,7 +243,7 @@ private List getStartedLSWrappers(Predicate getMatchingStartedWrappers(@Nonnull VirtualFile file, + private Collection getMatchingStartedWrappers(@NotNull VirtualFile file, @Nullable Predicate request) { synchronized (startedServers) { return startedServers.stream().filter(wrapper -> wrapper.isConnectedTo(LSPIJUtils.toUri(file)) @@ -264,7 +261,7 @@ private Collection getMatchingStartedWrappers(@Nonnull Vi * @param request * @return list of Language Servers */ - @Nonnull + @NotNull public List getActiveLanguageServers(Predicate request) { return getLanguageServers(null, request, true); } @@ -277,7 +274,7 @@ public List getActiveLanguageServers(Predicate getLanguageServers(@Nullable Project project, Predicate request, boolean onlyActiveLS) { List serverInfos = new ArrayList<>(); 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 index bbb044bbe..2de873fa3 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/commands/CommandExecutor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/commands/CommandExecutor.java @@ -130,13 +130,13 @@ private static boolean executeCommandServerSide(Project project, Command command private static CompletableFuture getLanguageServerForCommand(Project project, Command command, URI documentUri, LanguageServersRegistry.LanguageServerDefinition languageServerDefinition) throws IOException { - Document document = LSPIJUtils.getDocument(documentUri); - if (document == null) { + 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(document, languageServerDefinition, serverCapabilities -> { + .getInitializedLanguageServer(file, languageServerDefinition, serverCapabilities -> { ExecuteCommandOptions provider = serverCapabilities.getExecuteCommandProvider(); return provider != null && provider.getCommands().contains(command.getCommand()); }); 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 index f8dbb5d2f..384e3100c 100644 --- 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 @@ -23,6 +23,7 @@ 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.PsiElement; import com.intellij.psi.PsiFile; import com.redhat.devtools.intellij.lsp4ij.AbstractLSPInlayProvider; @@ -74,12 +75,13 @@ public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @ Document document = editor.getDocument(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - URI docURI = LSPIJUtils.toUri(document); + VirtualFile file = LSPIJUtils.getFile(psiElement); + URI docURI = LSPIJUtils.toUri(file); if (docURI != null) { CodeLensParams param = new CodeLensParams(new TextDocumentIdentifier(docURI.toString())); BlockingDeque> pairs = new LinkedBlockingDeque<>(); - CompletableFuture future = collect(document, project, param, pairs, cancellationSupport); + CompletableFuture future = collect(file, project, param, pairs, cancellationSupport); List>> codeLenses = createCodeLenses(document, pairs, future, cancellationSupport); codeLenses.stream() .collect(Collectors.groupingBy(p -> p.first)) @@ -122,9 +124,9 @@ private List>> createCodeLenses(Doc return codelenses; } - private CompletableFuture collect(Document document, Project project, CodeLensParams param, BlockingDeque> pairs, CancellationSupport cancellationSupport) { + private CompletableFuture collect(VirtualFile file, Project project, CodeLensParams param, BlockingDeque> pairs, CancellationSupport cancellationSupport) { return LanguageServiceAccessor.getInstance(project) - .getLanguageServers(document, capabilities -> capabilities.getCodeLensProvider() != null) + .getLanguageServers(file, capabilities -> capabilities.getCodeLensProvider() != null) .thenComposeAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() .map(languageServer -> 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 index 11eeb7bd8..7052c627c 100644 --- 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 @@ -22,6 +22,7 @@ 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; @@ -35,6 +36,7 @@ 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; @@ -51,15 +53,16 @@ public class LSPCompletionContributor extends CompletionContributor { public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { Document document = parameters.getEditor().getDocument(); Editor editor = parameters.getEditor(); - PsiFile file = parameters.getOriginalFile(); - Project project = file.getProject(); + PsiFile psiFile = parameters.getOriginalFile(); + Project project = psiFile.getProject(); int offset = parameters.getOffset(); - + VirtualFile file = psiFile.getVirtualFile(); + URI uri = LSPIJUtils.toUri(file); ProgressManager.checkCanceled(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(project, document); + CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(project, file); cancellationSupport.execute(completionLanguageServersFuture); ProgressManager.checkCanceled(); @@ -68,7 +71,7 @@ public void fillCompletionVariants(@NotNull CompletionParameters parameters, @No 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(LSPIJUtils.toUri(document), offset, document); + CompletionParams params = LSPIJUtils.toCompletionParams(uri, offset, document); BlockingDeque, CompletionList>, LanguageServerItem>> proposals = new LinkedBlockingDeque<>(); CompletableFuture future = completionLanguageServersFuture @@ -87,7 +90,7 @@ public void fillCompletionVariants(@NotNull CompletionParameters parameters, @No Either, CompletionList> completion = pair.getFirst(); if (completion != null) { CompletionPrefix completionPrefix = new CompletionPrefix(offset, document); - addCompletionItems(file, editor, completionPrefix, pair.getFirst(), pair.getSecond(), result, cancellationSupport); + addCompletionItems(psiFile, editor, completionPrefix, pair.getFirst(), pair.getSecond(), result, cancellationSupport); } } } @@ -169,8 +172,8 @@ private static LookupElement createErrorProposal(int offset, Exception ex) { return LookupElementBuilder.create("Error while computing completion", ""); } - private static CompletableFuture> initiateLanguageServers(Project project, Document document) { - return LanguageServiceAccessor.getInstance(project).getLanguageServers(document, + private static CompletableFuture> initiateLanguageServers(Project project, VirtualFile file) { + return LanguageServiceAccessor.getInstance(project).getLanguageServers(file, capabilities -> { CompletionOptions provider = capabilities.getCompletionProvider(); if (provider != null) { 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 index bc4eb4c34..4692c18d7 100644 --- 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 @@ -24,14 +24,14 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileWrapper; +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.eclipse.lsp4j.DiagnosticSeverity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; +import java.net.URI; import java.util.List; import static com.redhat.devtools.intellij.lsp4ij.operations.diagnostics.SeverityMapping.toHighlightSeverity; @@ -40,61 +40,62 @@ * 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 { +public class LSPDiagnosticAnnotator extends ExternalAnnotator { @Nullable @Override - public LSPVirtualFileWrapper collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { - try { - return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file.getVirtualFile()); - } catch (Exception e) { - return null; - } + public Boolean collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { + return Boolean.TRUE; } @Override - public @Nullable LSPVirtualFileWrapper doAnnotate(LSPVirtualFileWrapper wrapper) { - return wrapper; + public @Nullable Boolean doAnnotate(Boolean unused) { + return Boolean.TRUE; } - @Override - public void apply(@NotNull PsiFile file, LSPVirtualFileWrapper wrapper, @NotNull AnnotationHolder holder) { - // Get current LSP diagnostics of the current file - final Collection diagnosticsPerServer = wrapper.getAllDiagnostics(); - Document document = LSPIJUtils.getDocument(file.getVirtualFile()); + @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 - for (var ds : - diagnosticsPerServer) { - // Loop for LSP diagnostics to transform it to Intellij annotation. - for (Diagnostic diagnostic : ds.getDiagnostics()) { - ProgressManager.checkCanceled(); - createAnnotation(diagnostic, document, ds, holder); - } - } - } + // 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(); + 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); + // 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(); - } + // 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 index 2839893b8..85d39698b 100644 --- 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 @@ -16,7 +16,6 @@ import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -24,11 +23,12 @@ import com.intellij.psi.PsiManager; import com.intellij.util.concurrency.AppExecutorUtil; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileWrapper; +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 java.net.URI; import java.util.function.Consumer; /** @@ -48,7 +48,7 @@ public LSPDiagnosticHandler(LanguageServerWrapper languageServerWrapper) { @Override public void accept(PublishDiagnosticsParams params) { Project project = languageServerWrapper.getProject(); - if(project.isDisposed()) { + if (project.isDisposed()) { return; } if (ApplicationManager.getApplication().isReadAccessAllowed()) { @@ -68,26 +68,31 @@ public void accept(PublishDiagnosticsParams params) { } public void updateDiagnostics(PublishDiagnosticsParams params) { //ApplicationManager.getApplication().runReadAction(() -> { - VirtualFile file = LSPIJUtils.findResourceFor(params.getUri()); - if (file == null) { - return; - } - Project project = LSPIJUtils.getProject(file); - if (project == null || project.isDisposed()) { - return; - } - final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); - if (psiFile == null) { - return; - } - LSPVirtualFileWrapper wrapper = LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file); - synchronized (wrapper) { - // Update LSP diagnostic reported by the language server id - wrapper.updateDiagnostics(params.getDiagnostics(), languageServerWrapper); + VirtualFile file = LSPIJUtils.findResourceFor(params.getUri()); + if (file == null) { + return; + } + Project project = LSPIJUtils.getProject(file); + if (project == null || project.isDisposed()) { + 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/documentLink/LSPDocumentLinkAnnotator.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java index e7f40db9a..3f0bd3df9 100644 --- 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 @@ -23,8 +23,12 @@ import com.intellij.openapi.progress.ProgressManager; 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.*; +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; @@ -34,31 +38,40 @@ import org.slf4j.LoggerFactory; import java.net.URI; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +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 { +public class LSPDocumentLinkAnnotator extends ExternalAnnotator, List> { private static final Logger LOGGER = LoggerFactory.getLogger(LSPDocumentLinkAnnotator.class); @Nullable @Override - public LSPVirtualFileWrapper collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { + public List collectInformation(@NotNull PsiFile psiFile, @NotNull Editor editor, boolean hasErrors) { URI uri = LSPIJUtils.toUri(editor.getDocument()); if (uri == null) { return null; } + 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(); DocumentLinkParams params = new DocumentLinkParams(LSPIJUtils.toTextDocumentIdentifier(uri)); - Document document = editor.getDocument(); BlockingDeque, LanguageServerWrapper>> documentLinks = new LinkedBlockingDeque<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(document, + CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(file, capabilities -> capabilities.getDocumentLinkProvider() != null) .thenAcceptAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() @@ -74,8 +87,11 @@ public LSPVirtualFileWrapper collectInformation(@NotNull PsiFile file, @NotNull ProgressManager.checkCanceled(); Pair, LanguageServerWrapper> links = documentLinks.poll(25, TimeUnit.MILLISECONDS); if (links != null) { - LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file.getVirtualFile()) - .updateDocumentLink(links.getFirst(), links.getSecond()); + LSPVirtualFileData data = links.getSecond().getLSPVirtualFileData(uri); + if (data != null) { + data.updateDocumentLink(links.getFirst()); + datas.add(data); + } } } } catch (ProcessCanceledException cancellation) { @@ -85,21 +101,22 @@ public LSPVirtualFileWrapper collectInformation(@NotNull PsiFile file, @NotNull LOGGER.warn(e.getLocalizedMessage(), e); Thread.currentThread().interrupt(); } - return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file.getVirtualFile()); + return datas; } @Override - public @Nullable LSPVirtualFileWrapper doAnnotate(LSPVirtualFileWrapper wrapper) { + public @Nullable List doAnnotate(List wrapper) { return wrapper; } @Override - public void apply(@NotNull PsiFile file, LSPVirtualFileWrapper wrapper, @NotNull AnnotationHolder holder) { - if (wrapper == null) { + public void apply(@NotNull PsiFile file, List datas, @NotNull AnnotationHolder holder) { + if (datas.isEmpty()) { return; } - Document document = LSPIJUtils.getDocument(wrapper.getFile()); - for (var documentLinkPerServer : wrapper.getAllDocumentLink()) { + 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) 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 index 85cf4e21e..7b540dd9b 100644 --- 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 @@ -25,12 +25,15 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileWrapper; +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; /** @@ -42,16 +45,14 @@ public class LSPDocumentLinkGotoDeclarationHandler implements GotoDeclarationHan public PsiElement @Nullable [] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { Document document = editor.getDocument(); VirtualFile file = LSPIJUtils.getFile(document); - if (!LSPVirtualFileWrapper.hasWrapper(file)) { - return PsiElement.EMPTY_ARRAY; - } Module module = LSPIJUtils.getModule(file); Project project = module != null ? module.getProject() : null; if (project == null || project.isDisposed()) { return PsiElement.EMPTY_ARRAY; } - LSPVirtualFileWrapper wrapper = LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file); - Collection allLinks = wrapper.getAllDocumentLink(); + + URI fileUri = LSPIJUtils.toUri(file); + Collection allLinks = getAllDocumentLink(fileUri, project); if (allLinks.isEmpty()) { return PsiElement.EMPTY_ARRAY; } @@ -97,4 +98,17 @@ public class LSPDocumentLinkGotoDeclarationHandler implements GotoDeclarationHan } 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 index 1aba08130..0d48905e0 100644 --- 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 @@ -21,7 +21,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils; -import com.redhat.devtools.intellij.lsp4ij.LSPVirtualFileWrapper; import com.redhat.devtools.intellij.lsp4ij.operations.completion.LSPCompletionProposal; import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.parser.Parser; @@ -31,7 +30,6 @@ import java.awt.*; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; /** @@ -49,6 +47,8 @@ public class LSPDocumentationProvider extends DocumentationProviderEx implements 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) { @@ -85,10 +85,14 @@ public String generateDoc(@NotNull PsiElement element, @Nullable PsiElement orig return null; } editor = LSPIJUtils.editorForElement(originalElement); - VirtualFile file = originalElement.getContainingFile().getVirtualFile(); - if (LSPVirtualFileWrapper.hasWrapper(file)) { + 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 = LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file).getHoverContent(originalElement, targetOffset, editor); + markupContent = hover.getHoverContent(originalElement, targetOffset, editor); } } @@ -107,28 +111,6 @@ public String generateDoc(@NotNull PsiElement element, @Nullable PsiElement orig } } - @Nullable - public List getMarkupContents(PsiElement element, @Nullable PsiElement originalElement) { - if (element instanceof LSPPsiElementForLookupItem) { - // Show documentation for a given completion item in the "documentation popup" (see IJ Completion setting) - // (LSP textDocument/completion request) - return ((LSPPsiElementForLookupItem) element).getDocumentation(); - } - - Editor editor = LSPIJUtils.editorForElement(originalElement); - if (editor == null) { - return null; - } - - // Show documentation for a hovered element (LSP textDocument/hover request). - VirtualFile file = originalElement.getContainingFile().getVirtualFile(); - if (LSPVirtualFileWrapper.hasWrapper(file)) { - int targetOffset = getTargetOffset(originalElement); - return LSPVirtualFileWrapper.getLSPVirtualFileWrapper(file).getHoverContent(originalElement, targetOffset, editor); - } - return null; - } - private static int getTargetOffset(PsiElement originalElement) { Integer targetOffset = originalElement.getUserData(TARGET_OFFSET_KEY); if (targetOffset != null) { 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 index e8d271d51..7fab76466 100644 --- 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 @@ -10,10 +10,15 @@ ******************************************************************************/ 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; @@ -34,7 +39,7 @@ /** * LSP textDocument/hover support for a given file. */ -public class LSPTextHoverForFile { +public class LSPTextHoverForFile implements Disposable { private static final Logger LOGGER = LoggerFactory.getLogger(LSPTextHoverForFile.class); private PsiElement lastElement; @@ -42,6 +47,12 @@ public class LSPTextHoverForFile { 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 { @@ -81,13 +92,15 @@ private void initiateHoverRequest(PsiElement element, int offset) { this.previousCancellationSupport.cancel(); } PsiDocumentManager manager = PsiDocumentManager.getInstance(element.getProject()); - final Document document = manager.getDocument(element.getContainingFile()); + PsiFile psiFile = element.getContainingFile(); + VirtualFile file = LSPIJUtils.getFile(element); + final Document document = manager.getDocument(psiFile); 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(document, capabilities -> isHoverCapable(capabilities)) + .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() @@ -137,4 +150,11 @@ 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/LSPHighlightUsagesHandlerFactory.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java index db28edcb8..829a29869 100644 --- 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 @@ -49,11 +49,11 @@ public class LSPHighlightUsagesHandlerFactory implements HighlightUsagesHandlerF return targets.isEmpty() ? null : new LSPHighlightUsagesHandler(editor, file, targets); } - private List getTargets(Editor editor, PsiFile file) { + private List getTargets(Editor editor, PsiFile psiFile) { List elements = new ArrayList<>(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - int offset = TargetElementUtil.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset()); + int offset = TargetElementUtil.adjustOffset(psiFile, editor.getDocument(), editor.getCaretModel().getOffset()); Document document = editor.getDocument(); Position position; position = LSPIJUtils.toPosition(offset, document); @@ -67,7 +67,7 @@ private List getTargets(Editor editor, PsiFile file) { BlockingDeque highlights = new LinkedBlockingDeque<>(); CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()) - .getLanguageServers(document, capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) + .getLanguageServers(psiFile.getVirtualFile(), capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) .thenAcceptAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() .map(languageServer -> cancellationSupport.execute(languageServer.getServer().getTextDocumentService().documentHighlight(params))) 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 index e06283d8c..f89f2965d 100644 --- 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 @@ -23,6 +23,7 @@ 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.PsiElement; import com.intellij.psi.PsiFile; import com.redhat.devtools.intellij.lsp4ij.AbstractLSPInlayProvider; @@ -41,7 +42,6 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.BlockingDeque; import java.util.concurrent.CompletableFuture; @@ -72,15 +72,16 @@ public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @ Document document = editor.getDocument(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - URI docURI = LSPIJUtils.toUri(document); - if (docURI == null) { + VirtualFile file = LSPIJUtils.getFile(psiFile); + if (file == null) { return false; } + URI docURI = LSPIJUtils.toUri(file); Range viewPortRange = getViewPortRange(editor); InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(docURI.toString()), viewPortRange); BlockingDeque> pairs = new LinkedBlockingDeque<>(); - CompletableFuture future = collect(psiElement.getProject(), document, param, pairs, cancellationSupport); + CompletableFuture future = collect(psiElement.getProject(), file, param, pairs, cancellationSupport); List>> inlayHints = createInlayHints(document, pairs, future); inlayHints.stream() .collect(Collectors.groupingBy(p -> p.first)) @@ -115,9 +116,9 @@ private List>> createInlayHints( return inlayHints; } - private CompletableFuture collect(@NotNull Project project, @NotNull Document document, InlayHintParams param, BlockingDeque> pairs, CancellationSupport cancellationSupport) { + private CompletableFuture collect(@NotNull Project project, @NotNull VirtualFile file, InlayHintParams param, BlockingDeque> pairs, CancellationSupport cancellationSupport) { return LanguageServiceAccessor.getInstance(project) - .getLanguageServers(document, capabilities -> capabilities.getInlayHintProvider() != null) + .getLanguageServers(file, capabilities -> capabilities.getInlayHintProvider() != null) .thenComposeAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() .map(languageServer -> cancellationSupport.execute(languageServer.getServer().getTextDocumentService().inlayHint(param)) 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 index 1fef0ca9c..4d1c40614 100644 --- 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 @@ -53,12 +53,13 @@ public class LSPGotoDeclarationHandler implements GotoDeclarationHandler { public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { URI uri = LSPIJUtils.toUri(editor.getDocument()); if (uri != null) { + VirtualFile file = LSPIJUtils.getFile(sourceElement); DefinitionParams params = new DefinitionParams(LSPIJUtils.toTextDocumentIdentifier(uri), LSPIJUtils.toPosition(offset, editor.getDocument())); Set targets = new HashSet<>(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { LanguageServiceAccessor.getInstance(editor.getProject()) - .getLanguageServers(editor.getDocument(), capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())) + .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())) .thenComposeAsync(languageServers -> cancellationSupport.execute( CompletableFuture.allOf( diff --git a/src/main/resources/META-INF/lsp4ij.xml b/src/main/resources/META-INF/lsp4ij.xml index f78df8d07..6e9ed2fdc 100644 --- a/src/main/resources/META-INF/lsp4ij.xml +++ b/src/main/resources/META-INF/lsp4ij.xml @@ -1,60 +1,67 @@ - - - - - - - - - + + + + + + + + + - - - - + + + - - + + - - - - - - + + + + + + - - - - - - - - - + + + + + + + + - - - + + +