From 3f6c56af5bf416a8feef666e223d87ffdcad1731 Mon Sep 17 00:00:00 2001 From: azerr Date: Tue, 17 Dec 2024 11:22:38 +0100 Subject: [PATCH] feat: Path patching API Fixes #702 Signed-off-by: azerr --- .../lsp4ij/DocumentContentSynchronizer.java | 5 +- .../devtools/lsp4ij/LSPFileListener.java | 37 +++--- .../redhat/devtools/lsp4ij/LSPIJUtils.java | 124 ++++++++++++++++-- .../lsp4ij/LanguageServerWrapper.java | 95 ++++++-------- .../devtools/lsp4ij/ServerMessageHandler.java | 5 +- .../client/IndexAwareLanguageClient.java | 6 +- .../lsp4ij/client/LanguageClientImpl.java | 8 +- .../client/features/FileUriSupport.java | 73 +++++++++++ .../client/features/LSPClientFeatures.java | 69 ++++++---- .../lsp4ij/commands/CommandExecutor.java | 35 ++--- .../commands/editor/ShowReferencesAction.java | 18 ++- .../AbstractLSPDocumentFeatureSupport.java | 8 ++ .../features/AbstractLSPGoToAction.java | 20 +-- .../lsp4ij/features/LSPPsiElementFactory.java | 23 ++-- .../LSPCallHierarchyIncomingCallsSupport.java | 3 +- .../LSPCallHierarchyOutgoingCallsSupport.java | 3 +- .../LSPCallHierarchyTreeStructureBase.java | 11 +- .../LSPPrepareCallHierarchySupport.java | 2 + .../LSPIntentionCodeActionSupport.java | 5 +- .../features/codeLens/LSPCodeLensSupport.java | 2 + .../features/color/LSPColorSupport.java | 6 +- .../completion/LSPCompletionSupport.java | 2 + .../declaration/LSPDeclarationSupport.java | 35 ++--- .../declaration/LSPGoToDeclarationAction.java | 5 +- .../diagnostics/LSPDiagnosticHandler.java | 3 +- ...LSPDocumentLinkGotoDeclarationHandler.java | 6 +- .../documentLink/LSPDocumentLinkSupport.java | 5 +- .../LSPDocumentSymbolSupport.java | 10 +- .../LSPDocumentationLinkHandler.java | 4 +- .../documentation/LSPDocumentationTarget.java | 4 + .../documentation/LSPHoverSupport.java | 5 +- .../foldingRange/LSPFoldingRangeSupport.java | 11 +- .../formatting/LSPFormattingSupport.java | 18 ++- .../LSPHierarchyTreeStructureBase.java | 6 +- .../highlight/LSPHighlightSupport.java | 5 +- .../LSPGoToImplementationAction.java | 6 +- .../LSPImplementationSupport.java | 25 ++-- .../inlayhint/LSPInlayHintsSupport.java | 2 + .../navigation/LSPDefinitionSupport.java | 31 +++-- .../navigation/LSPGotoDeclarationHandler.java | 10 +- .../LSPRenamePsiElementProcessor.java | 17 ++- .../references/LSPGoToReferenceAction.java | 6 +- .../references/LSPReferenceSupport.java | 24 ++-- .../rename/LSPPrepareRenameSupport.java | 5 +- .../features/rename/LSPRenameSupport.java | 8 +- .../LSPSelectionRangeSupport.java | 11 +- .../LSPSemanticTokensSupport.java | 11 +- .../LSPSignatureHelpSupport.java | 5 +- .../LSPGoToTypeDefinitionAction.java | 6 +- .../LSPTypeDefinitionSupport.java | 25 ++-- .../LSPPrepareTypeHierarchySupport.java | 6 +- .../LSPTypeHierarchySubtypesSupport.java | 7 +- .../LSPTypeHierarchySupertypesSupport.java | 11 +- .../LSPTypeHierarchyTreeStructureBase.java | 2 +- .../LSPWorkspaceImplementationsSearch.java | 10 +- .../LSPWorkspaceSymbolSupport.java | 13 +- .../LSPWorkspaceTypeDeclarationProvider.java | 10 +- .../workspaceSymbol/WorkspaceSymbolData.java | 22 +++- .../lsp4ij/hint/LSPNavigationLinkHandler.java | 2 +- .../lsp4ij/usages/LSPUsageSearcher.java | 2 +- .../lsp4ij/usages/LSPUsageSupport.java | 37 ++++-- .../usages/LSPUsageTriggeredPsiElement.java | 7 +- .../lsp4ij/usages/LSPUsagesManager.java | 30 +++-- .../devtools/lsp4ij/usages/LocationData.java | 7 + 64 files changed, 688 insertions(+), 347 deletions(-) create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/client/features/FileUriSupport.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/usages/LocationData.java diff --git a/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java b/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java index 89a110ac0..e235d9f78 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java @@ -22,7 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -46,12 +45,12 @@ public class DocumentContentSynchronizer implements DocumentListener { @NotNull final CompletableFuture didOpenFuture; public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServerWrapper, - @NotNull URI fileUri, + @NotNull String fileUri, @NotNull VirtualFile file, @NotNull Document document, @Nullable TextDocumentSyncKind syncKind) { this.languageServerWrapper = languageServerWrapper; - this.fileUri = fileUri.toASCIIString(); + this.fileUri = fileUri; this.syncKind = syncKind != null ? syncKind : TextDocumentSyncKind.Full; this.document = document; diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LSPFileListener.java b/src/main/java/com/redhat/devtools/lsp4ij/LSPFileListener.java index 7eb7a9e09..d7a8ea8c1 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LSPFileListener.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LSPFileListener.java @@ -20,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -55,14 +54,11 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f return; } // Manage textDocument/didClose - URI uri = LSPIJUtils.toUri(file); - if (uri != null) { - try { - // Disconnect the given file from the current language servers - languageServerWrapper.disconnect(uri, !languageServerWrapper.isDisposed()); - } catch (Exception e) { - LOGGER.warn("Error while disconnecting the file '" + uri + "' from all language servers", e); - } + try { + // Disconnect the given file from the current language servers + languageServerWrapper.disconnect(file, !languageServerWrapper.isDisposed()); + } catch (Exception e) { + LOGGER.warn("Error while disconnecting the file '" + file.getUrl() + "' from language server '" + languageServerWrapper.getServerDefinition().getDisplayName() + "'.", e); } } @@ -96,7 +92,7 @@ private void moveFile(URI oldFileUri, VirtualFile newFile) { if (isMatchFilePatterns(oldFileUri, WatchKind.Delete)) { changes.add(fe(oldFileUri, FileChangeType.Deleted)); } - URI newFileUri = LSPIJUtils.toUri(newFile); + URI newFileUri = languageServerWrapper.toUri(newFile); if (isMatchFilePatterns(newFileUri, WatchKind.Create)) { changes.add(fe(newFileUri, FileChangeType.Created)); } @@ -109,7 +105,7 @@ private void moveFile(URI oldFileUri, VirtualFile newFile) { @Override public void contentsChanged(@NotNull VirtualFileEvent event) { VirtualFile file = event.getFile(); - URI uri = LSPIJUtils.toUri(file); + URI uri = languageServerWrapper.toUri(file); if (uri != null) { LSPVirtualFileData documentListener = languageServerWrapper.connectedDocuments.get(uri); if (documentListener != null) { @@ -126,7 +122,7 @@ public void contentsChanged(@NotNull VirtualFileEvent event) { @Override public void fileCreated(@NotNull VirtualFileEvent event) { VirtualFile file = event.getFile(); - URI uri = LSPIJUtils.toUri(file); + URI uri = languageServerWrapper.toUri(file); if (isMatchFilePatterns(uri, WatchKind.Create)) { // 2. Send a workspace/didChangeWatchedFiles with 'Created' file change type. didChangeWatchedFiles(fe(uri, FileChangeType.Created)); @@ -136,7 +132,7 @@ public void fileCreated(@NotNull VirtualFileEvent event) { @Override public void fileDeleted(@NotNull VirtualFileEvent event) { VirtualFile file = event.getFile(); - URI uri = LSPIJUtils.toUri(file); + URI uri = languageServerWrapper.toUri(file); if (isMatchFilePatterns(uri, WatchKind.Delete)) { // Send a workspace/didChangeWatchedFiles with 'Deleted' file change type. didChangeWatchedFiles(fe(uri, FileChangeType.Deleted)); @@ -166,20 +162,21 @@ private FileEvent fe(URI uri, FileChangeType type) { * to a different directory. * * @param virtualParentFile directory of the file after renaming - * @param oldFileName file name before renaming - * @param virtualNewFile virtual file after renaming + * @param oldFileName file name before renaming + * @param virtualNewFile virtual file after renaming * @return URI of the file before renaming */ - private @NotNull URI didRename(VirtualFile virtualParentFile, String oldFileName, VirtualFile virtualNewFile) { - File parentFile = VfsUtilCore.virtualToIoFile(virtualParentFile); - URI oldUri = LSPIJUtils.toUri(new File(parentFile, oldFileName)); + private @NotNull URI didRename(@NotNull VirtualFile virtualParentFile, + @NotNull String oldFileName, + @NotNull VirtualFile virtualNewFile) { + URI parentFileUri = languageServerWrapper.toUri(virtualParentFile); + URI oldUri = parentFileUri.resolve(oldFileName); boolean docIsConnected = languageServerWrapper.isConnectedTo(oldUri); if (docIsConnected) { // 1. Send a textDocument/didClose for the old file name languageServerWrapper.disconnect(oldUri, false); // 2. Send a textDocument/didOpen for the new file name - File newFile = VfsUtilCore.virtualToIoFile(virtualNewFile); - URI newUri = LSPIJUtils.toUri(newFile); + URI newUri = languageServerWrapper.toUri(virtualNewFile); if (!languageServerWrapper.isConnectedTo(newUri)) { languageServerWrapper.connect(virtualNewFile, null); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java index bb992d064..2a25552d8 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java @@ -29,13 +29,16 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.internal.SimpleLanguageUtils; import com.redhat.devtools.lsp4ij.internal.StringUtils; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.apache.commons.io.FileUtils; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; @@ -56,8 +59,11 @@ * Utilities class for LSP. */ public class LSPIJUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(LSPIJUtils.class); + private static final Key DEFAULT_LSP_FILE_URI_KEY = Key.create("lsp.file.uri"); + private static final String JAR_PROTOCOL = "jar"; private static final String JRT_PROTOCOL = "jrt"; @@ -88,6 +94,9 @@ public class LSPIJUtils { /** * Open the LSP location in an editor. + *

+ * Not used but declared to support backward compatibility. + *

* * @param location the LSP location. * @param project the project. @@ -95,14 +104,31 @@ public class LSPIJUtils { */ public static boolean openInEditor(@Nullable Location location, @NotNull Project project) { + return openInEditor(location, null, project); + } + + /** + * Open the LSP location in an editor. + * + * @param location the LSP location. + * @param fileUriSupport the file Uri support. + * @param project the project. + * @return true if the file was opened and false otherwise. + */ + public static boolean openInEditor(@Nullable Location location, + @Nullable FileUriSupport fileUriSupport, + @NotNull Project project) { if (location == null) { return false; } - return openInEditor(location.getUri(), location.getRange() != null ? location.getRange().getStart() : null, project); + return openInEditor(location.getUri(), location.getRange() != null ? location.getRange().getStart() : null, fileUriSupport, project); } /** * Open the given fileUri with the given position in an editor. + *

+ * Not used but declared to support backward compatibility. + *

* * @param fileUri the file Uri. * @param position the position. @@ -112,12 +138,31 @@ public static boolean openInEditor(@Nullable Location location, public static boolean openInEditor(@NotNull String fileUri, @Nullable Position position, @NotNull Project project) { - return openInEditor(fileUri, position, true, project); + return openInEditor(fileUri, position, null, project); } /** * Open the given fileUri with the given position in an editor. * + * @param fileUri the file Uri. + * @param position the position. + * @param fileUriSupport the file Uri support. + * @param project the project. + * @return true if the file was opened and false otherwise. + */ + public static boolean openInEditor(@NotNull String fileUri, + @Nullable Position position, + @Nullable FileUriSupport fileUriSupport, + @NotNull Project project) { + return openInEditor(fileUri, position, true, fileUriSupport, project); + } + + /** + * Open the given fileUri with the given position in an editor. + *

+ * Not used but declared to support backward compatibility. + *

+ * * @param fileUri the file Uri. * @param position the position. * @param focusEditor true if editor will take the focus and false otherwise. @@ -128,7 +173,55 @@ public static boolean openInEditor(@NotNull String fileUri, @Nullable Position position, boolean focusEditor, @NotNull Project project) { - return openInEditor(fileUri, position, focusEditor, false, project); + return openInEditor(fileUri, position, focusEditor, null, project); + } + + /** + * Open the given fileUri with the given position in an editor. + * + * @param fileUri the file Uri. + * @param position the position. + * @param focusEditor true if editor will take the focus and false otherwise. + * @param fileUriSupport the file Uri support. + * @param project the project. + * @return true if the file was opened and false otherwise. + */ + public static boolean openInEditor(@NotNull String fileUri, + @Nullable Position position, + boolean focusEditor, + @Nullable FileUriSupport fileUriSupport, + @NotNull Project project) { + return openInEditor(fileUri, position, focusEditor, false, fileUriSupport, project); + } + + /** + * Open the given fileUrl in an editor. + *

+ * Not used but declared to support backward compatibility. + *

+ * + *

+ * the following syntax is supported for fileUrl: + *

    + *
  • file:///C:/Users/username/foo.txt
  • + *
  • C:/Users/username/foo.txt
  • + *
  • file:///C:/Users/username/foo.txt#L1:5
  • + *
+ *

+ * + * @param fileUri the file Uri to open. + * @param position the position. + * @param focusEditor true if editor will take the focus and false otherwise. + * @param createFileIfNeeded true if file must be created if doesn't exist and false otherwise. + * @param project the project. + * @return true if file Url can be opened and false otherwise. + */ + public static boolean openInEditor(@NotNull String fileUri, + @Nullable Position position, + boolean focusEditor, + boolean createFileIfNeeded, + @NotNull Project project) { + return openInEditor(fileUri, position, focusEditor, createFileIfNeeded, null, project); } /** @@ -147,6 +240,7 @@ public static boolean openInEditor(@NotNull String fileUri, * @param position the position. * @param focusEditor true if editor will take the focus and false otherwise. * @param createFileIfNeeded true if file must be created if doesn't exist and false otherwise. + * @param fileUriSupport the file Uri support. * @param project the project. * @return true if file Url can be opened and false otherwise. */ @@ -154,6 +248,7 @@ public static boolean openInEditor(@NotNull String fileUri, @Nullable Position position, boolean focusEditor, boolean createFileIfNeeded, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project) { if (position == null) { // Try to get position information from the fileUri @@ -173,7 +268,7 @@ public static boolean openInEditor(@NotNull String fileUri, fileUri = fileUri.substring(0, hashIndex); } } - VirtualFile file = findResourceFor(fileUri); + VirtualFile file = FileUriSupport.findFileByUri(fileUri, fileUriSupport); if (file == null && createFileIfNeeded) { // The file doesn't exist, // open a dialog to confirm the creation of the file. @@ -348,7 +443,13 @@ private static Language doGetFileLanguage(@NotNull VirtualFile file, @NotNull Pr } public static @NotNull URI toUri(@NotNull VirtualFile file) { - return toUri(VfsUtilCore.virtualToIoFile(file)); + URI fileUri = file.getUserData(DEFAULT_LSP_FILE_URI_KEY); + if (fileUri == null) { + // Cache the file Uri to avoid recomputing again + fileUri = toUri(VfsUtilCore.virtualToIoFile(file)); + file.putUserData(DEFAULT_LSP_FILE_URI_KEY, fileUri); + } + return fileUri; } public static @Nullable String toUriAsString(@NotNull PsiFile psFile) { @@ -969,11 +1070,7 @@ private static void applyRenameFile(RenameFile renameFile) { } public static TextDocumentIdentifier toTextDocumentIdentifier(VirtualFile file) { - return toTextDocumentIdentifier(toUri(file)); - } - - public static TextDocumentIdentifier toTextDocumentIdentifier(final URI uri) { - return new TextDocumentIdentifier(uri.toASCIIString()); + return new TextDocumentIdentifier(toUriAsString(file)); } /** @@ -1222,7 +1319,8 @@ public static String getProjectUri(Project project) { * @param locations the locations/links * @return the most specific location information that was found based on the provided locations/links */ - public static @NotNull List getLocations(@Nullable Either, List> locations) { + public static @NotNull List getLocations(@Nullable Either, List> locations, + @Nullable LanguageServerItem languageServer) { if (locations == null) { // textDocument/definition may return null return Collections.emptyList(); @@ -1230,13 +1328,13 @@ public static String getProjectUri(Project project) { if (locations.isLeft()) { return locations.getLeft() .stream() - .map(l -> new Location(l.getUri(), l.getRange())) + .map(l -> new LocationData(new Location(l.getUri(), l.getRange()), languageServer)) .toList(); } return locations.getRight() .stream() - .map(l -> new Location(l.getTargetUri(), l.getTargetSelectionRange() != null ? l.getTargetSelectionRange() : l.getTargetRange())) + .map(l -> new LocationData(new Location(l.getTargetUri(), l.getTargetSelectionRange() != null ? l.getTargetSelectionRange() : l.getTargetRange()), languageServer)) .toList(); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java index 8a073afa3..1cd9b935d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java @@ -24,6 +24,7 @@ import com.intellij.psi.PsiFile; import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.lsp4ij.client.LanguageClientImpl; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.files.operations.FileOperationsManager; import com.redhat.devtools.lsp4ij.internal.ClientCapabilitiesFactory; @@ -71,7 +72,7 @@ public class LanguageServerWrapper implements Disposable { @NotNull protected final Project initialProject; @NotNull - protected Map connectedDocuments; + protected Map connectedDocuments; @Nullable protected final URI initialPath; protected final InitializeParams initParams = new InitializeParams(); @@ -259,7 +260,7 @@ public synchronized void start() throws LanguageServerException { if (isActive()) { return; } else { - for (Map.Entry entry : this.connectedDocuments.entrySet()) { + for (Map.Entry entry : this.connectedDocuments.entrySet()) { filesToReconnect.add(entry.getValue().getFile()); } stop(); @@ -348,7 +349,7 @@ public synchronized void start() throws LanguageServerException { }).thenRun(() -> this.languageServer.initialized(new InitializedParams())).thenRun(() -> { initializeFuture.thenRunAsync(() -> { for (VirtualFile fileToReconnect : filesToReconnect) { - connect(LSPIJUtils.toUri(fileToReconnect), fileToReconnect, null); + connect(fileToReconnect, null); } }); @@ -618,7 +619,7 @@ public boolean canOperate(Project project) { } /** - * Connect the given file Uri to the language server and returns the language server instance when: + * Connect the given file to the language server and returns the language server instance when: * *
    *
  • language server is initialized
  • @@ -630,43 +631,21 @@ public boolean canOperate(Project project) { * the method return a CompletableFuture which returns null. *

    * - * @param file the file to connect to the language server - * @param document the document of the file and null otherwise. In the null case, the document will be retrieved from the file - * by using a blocking read action. - * @return the completable future with the language server instance or null. - */ - CompletableFuture<@Nullable LanguageServer> connect(@NotNull VirtualFile file, - @Nullable Document document) { - if (file != null && file.exists()) { - return connect(LSPIJUtils.toUri(file), file, document); - } - return CompletableFuture.completedFuture(null); - } - - /** - * Connect the given file Uri to the language server and returns the language server instance when: - * - *
      - *
    • language server is initialized
    • - *
    • didOpen for the given file Uri happens
    • - *
    - * - *

    - * In other case (ex : language server initialize future is null, file cannot be retrieved by the given Uri), - * the method return a CompletableFuture which returns null. - *

    - * - * @param fileUri the file Uri to connect to the language server - * @param optionalFile the file which matches the non-nullable file uri and null otherwise. In the null case, the file will be retrieved by the file Uri. + * @param file the file to connect to the language server * @param optionalDocument the document of the file and null otherwise. In the null case, the document will be retrieved from the file * by using a blocking read action. * @return the completable future with the language server instance or null. */ - private CompletableFuture connect(@NotNull URI fileUri, - @Nullable VirtualFile optionalFile, - @Nullable Document optionalDocument) { + CompletableFuture<@Nullable LanguageServer> connect(@NotNull VirtualFile file, + @Nullable Document optionalDocument) { removeStopTimer(false); + URI fileUri = toUri(file); + if (fileUri == null) { + // Invalid file uri + return CompletableFuture.completedFuture(null); + } + var ls = getLanguageServerWhenDidOpen(fileUri); if (ls != null) { return ls; @@ -680,15 +659,6 @@ private CompletableFuture connect(@NotNull URI fileUri, return CompletableFuture.completedFuture(null); } - if (optionalFile == null) { - optionalFile = LSPIJUtils.findResourceFor(fileUri); - } - if (optionalFile == null) { - // The file cannot be retrieved by the given uri, return null as language server. - return CompletableFuture.completedFuture(null); - } - - final @NotNull VirtualFile fileToConnect = optionalFile; return initializeFuture.thenComposeAsync(theVoid -> { // Here, the "initialize" future is initialized @@ -700,7 +670,7 @@ private CompletableFuture connect(@NotNull URI fileUri, // Here the file is not already connected // To connect the file, we need the document instance to add LSP document listener to manage didOpen, didChange, etc. - Document document = optionalDocument != null ? optionalDocument : LSPIJUtils.getDocument(fileToConnect); + Document document = optionalDocument != null ? optionalDocument : LSPIJUtils.getDocument(file); synchronized (connectedDocuments) { // Check again if file is already opened (within synchronized block) @@ -709,9 +679,9 @@ private CompletableFuture connect(@NotNull URI fileUri, return ls2; } - DocumentContentSynchronizer synchronizer = createDocumentContentSynchronizer(fileUri, fileToConnect, document); + DocumentContentSynchronizer synchronizer = createDocumentContentSynchronizer(fileUri.toASCIIString(), file, document); document.addDocumentListener(synchronizer); - LSPVirtualFileData data = new LSPVirtualFileData(new LanguageServerItem(languageServer, this), fileToConnect, synchronizer); + LSPVirtualFileData data = new LSPVirtualFileData(new LanguageServerItem(languageServer, this), file, synchronizer); LanguageServerWrapper.this.connectedDocuments.put(fileUri, data); return getLanguageServerWhenDidOpen(synchronizer.didOpenFuture); @@ -720,7 +690,10 @@ private CompletableFuture connect(@NotNull URI fileUri, } @Nullable - private CompletableFuture getLanguageServerWhenDidOpen(@NotNull URI fileUri) { + private CompletableFuture getLanguageServerWhenDidOpen(@Nullable URI fileUri) { + if (fileUri == null) { + return null; + } var existingData = connectedDocuments.get(fileUri); if (existingData != null) { // The file is already connected. @@ -744,7 +717,7 @@ private CompletableFuture getLanguageServerWhenDidOpen(Completab } @NotNull - private DocumentContentSynchronizer createDocumentContentSynchronizer(@NotNull URI fileUri, + private DocumentContentSynchronizer createDocumentContentSynchronizer(@NotNull String fileUri, @NotNull VirtualFile file, @NotNull Document document) { Either syncOptions = initializeFuture == null ? null @@ -760,8 +733,16 @@ private DocumentContentSynchronizer createDocumentContentSynchronizer(@NotNull U return new DocumentContentSynchronizer(this, fileUri, file, document, syncKind); } - public void disconnect(URI path, boolean stopIfNoOpenedFiles) { - LSPVirtualFileData data = this.connectedDocuments.remove(path); + void disconnect(@NotNull VirtualFile file, boolean stopIfNoOpenedFiles) { + URI fileUri = toUri(file); + disconnect(fileUri, stopIfNoOpenedFiles); + } + + void disconnect(@Nullable URI fileUri, boolean stopIfNoOpenedFiles) { + if (fileUri == null) { + return; + } + LSPVirtualFileData data = this.connectedDocuments.remove(fileUri); if (data != null) { // Remove the listener from the old document stored in synchronizer DocumentContentSynchronizer synchronizer = data.getSynchronizer(); @@ -799,11 +780,9 @@ void decrementKeepAlive() { /** * 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 boolean isConnectedTo(URI fileUri) { + return connectedDocuments.containsKey(fileUri); } /** @@ -1065,7 +1044,7 @@ int getVersion(VirtualFile file) { } public boolean canOperate(@NotNull VirtualFile file) { - if (this.isConnectedTo(LSPIJUtils.toUri(file))) { + if (this.isConnectedTo(toUri(file))) { return true; } if (this.connectedDocuments.isEmpty()) { @@ -1187,4 +1166,8 @@ private synchronized LSPClientFeatures getOrCreateClientFeatures() { return clientFeatures; } + URI toUri(@NotNull VirtualFile file) { + return FileUriSupport.getFileUri(file, getClientFeatures()); + } + } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/ServerMessageHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/ServerMessageHandler.java index 34d4f6ad1..5a1294908 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/ServerMessageHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/ServerMessageHandler.java @@ -26,12 +26,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopupListener; import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.console.LSPConsoleToolWindowPanel; import com.redhat.devtools.lsp4ij.features.documentation.MarkdownConverter; import com.redhat.devtools.lsp4ij.internal.StringUtils; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; import org.eclipse.lsp4j.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.concurrent.CompletableFuture; @@ -157,6 +159,7 @@ public void onClosed(@NotNull LightweightWindowEvent event) { } public static CompletableFuture showDocument(@NotNull ShowDocumentParams params, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project) { String uri = params.getUri(); if (StringUtils.isEmpty(uri)) { @@ -180,7 +183,7 @@ public static CompletableFuture showDocument(@NotNull ShowDo CompletableFuture future = new CompletableFuture<>(); ApplicationManager.getApplication() .executeOnPooledThread(() -> { - if (LSPIJUtils.openInEditor(uri, position, focusEditor, project)) { + if (LSPIJUtils.openInEditor(uri, position, focusEditor, false, fileUriSupport, project)) { future.complete(SHOW_DOCUMENT_RESULT_WITH_SUCCESS); } future.complete(SHOW_DOCUMENT_RESULT_WITH_FAILURE); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/IndexAwareLanguageClient.java b/src/main/java/com/redhat/devtools/lsp4ij/client/IndexAwareLanguageClient.java index b1976d11c..1491d42f4 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/IndexAwareLanguageClient.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/IndexAwareLanguageClient.java @@ -17,16 +17,14 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.client.indexing.ProjectIndexingManager; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; -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); @@ -68,7 +66,7 @@ protected CompletableFuture runAsBackground(String progressTitle, Functio * @return the file path to display in the progress bar. */ protected String getFilePath(String fileUri) { - VirtualFile file = LSPIJUtils.findResourceFor(fileUri); + VirtualFile file = FileUriSupport.findFileByUri(fileUri, getClientFeatures()); if (file != null) { Module module = LSPIJUtils.getModule(file, getProject()); if (module != null) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/LanguageClientImpl.java b/src/main/java/com/redhat/devtools/lsp4ij/client/LanguageClientImpl.java index df61774cb..f591449ef 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/LanguageClientImpl.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/LanguageClientImpl.java @@ -10,7 +10,6 @@ ******************************************************************************/ package com.redhat.devtools.lsp4ij.client; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.openapi.Disposable; @@ -21,6 +20,7 @@ import com.intellij.psi.PsiFile; import com.intellij.util.concurrency.AppExecutorUtil; import com.redhat.devtools.lsp4ij.*; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.diagnostics.LSPDiagnosticHandler; import com.redhat.devtools.lsp4ij.features.progress.LSPProgressManager; import com.redhat.devtools.lsp4ij.internal.editor.EditorFeatureManager; @@ -79,6 +79,10 @@ public LanguageServerDefinition getServerDefinition() { return wrapper.getServerDefinition(); } + public LSPClientFeatures getClientFeatures() { + return wrapper.getClientFeatures(); + } + @Override public void telemetryEvent(Object object) { // TODO @@ -96,7 +100,7 @@ public void showMessage(MessageParams messageParams) { @Override public CompletableFuture showDocument(ShowDocumentParams params) { - return ServerMessageHandler.showDocument(params, getProject()); + return ServerMessageHandler.showDocument(params, getClientFeatures(), getProject()); } @Override diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/FileUriSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/FileUriSupport.java new file mode 100644 index 000000000..4fad5ec78 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/FileUriSupport.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2024 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 declaration + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.client.features; + +import com.intellij.openapi.vfs.VirtualFile; +import com.redhat.devtools.lsp4ij.LSPIJUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; + +/** + * File Uri support. + */ +public interface FileUriSupport { + + public static final FileUriSupport DEFAULT_FILE_URI_SUPPORT = new FileUriSupport() { + + @Override + public @Nullable URI getFileUri(@NotNull VirtualFile file) { + return LSPIJUtils.toUri(file); + } + + @Override + public @Nullable VirtualFile findFileByUri(@NotNull String fileUri) { + return LSPIJUtils.findResourceFor(fileUri); + } + }; + + /** + * Returns the file Uri from the given virtual file and null otherwise. + * @param file the virtual file. + * @return the file Uri from the given virtual file and null otherwise. + */ + @Nullable + URI getFileUri(@NotNull VirtualFile file); + + /** + * Returns the virtual file found by the given file Uri and null otherwise. + * @param fileUri the file Uri. + * @return the virtual file found by the given file Uri and null otherwise. + */ + @Nullable + VirtualFile findFileByUri(@NotNull String fileUri); + + @Nullable + public static URI getFileUri(@NotNull VirtualFile file, + @Nullable FileUriSupport fileUriSupport) { + URI fileUri = fileUriSupport != null ? fileUriSupport.getFileUri(file) : null; + if (fileUri != null) { + return fileUri; + } + return DEFAULT_FILE_URI_SUPPORT.getFileUri(file); + } + + @Nullable + public static VirtualFile findFileByUri(@NotNull String fileUri, + @Nullable FileUriSupport fileUriSupport) { + VirtualFile file = fileUriSupport != null ? fileUriSupport.findFileByUri(fileUri) : null; + if (file != null) { + return file; + } + return DEFAULT_FILE_URI_SUPPORT.findFileByUri(fileUri); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java index 9b2822435..51fbfdf5b 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java @@ -26,11 +26,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.net.URI; + /** * LSP client features. */ @ApiStatus.Experimental -public class LSPClientFeatures implements Disposable { +public class LSPClientFeatures implements Disposable, FileUriSupport { private LanguageServerWrapper serverWrapper; @@ -84,6 +86,50 @@ public class LSPClientFeatures implements Disposable { private LSPWorkspaceSymbolFeature workspaceSymbolFeature; + /** + * Returns true if the language server is enabled for the given file and false otherwise. Default to true + * + * @param file the file for test + * @return true if the language server is enabled for the input and false otherwise. Default to true + */ + public boolean isEnabled(@NotNull VirtualFile file) { + return true; + } + + /** + * Determines whether or not the language grammar for the file is case-sensitive. + * + * @param file the file + * @return true if the file's language grammar is case-sensitive; otherwise false + */ + public boolean isCaseSensitive(@NotNull PsiFile file) { + // Default to case-insensitive + return false; + } + + /** + * Overrides this method if you need to generate custom URI. + * + * @param file the virtual file. + * + * @return the file Uri and null otherwise. + */ + @Override + public @Nullable URI getFileUri(@NotNull VirtualFile file) { + return null; + } + + /** + * Overrides this method if you need to support custom file uri. + * + * @param fileUri the file Uri. + * @return the virtual file and null otherwise. + */ + @Override + public @Nullable VirtualFile findFileByUri(@NotNull String fileUri) { + return null; + } + /** * Returns the project. * @@ -955,27 +1001,6 @@ public final LSPClientFeatures setWorkspaceSymbolFeature(@NotNull LSPWorkspaceSy return this; } - /** - * Returns true if the language server is enabled for the given file and false otherwise. Default to true - * - * @param file the file for test - * @return true if the language server is enabled for the input and false otherwise. Default to true - */ - public boolean isEnabled(@NotNull VirtualFile file) { - return true; - } - - /** - * Determines whether or not the language grammar for the file is case-sensitive. - * - * @param file the file - * @return true if the file's language grammar is case-sensitive; otherwise false - */ - public boolean isCaseSensitive(@NotNull PsiFile file) { - // Default to case-insensitive - return false; - } - /** * Set the language server wrapper. * diff --git a/src/main/java/com/redhat/devtools/lsp4ij/commands/CommandExecutor.java b/src/main/java/com/redhat/devtools/lsp4ij/commands/CommandExecutor.java index e60cf0592..417fcb4cb 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/commands/CommandExecutor.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/commands/CommandExecutor.java @@ -27,13 +27,13 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; -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.PsiFile; import com.redhat.devtools.lsp4ij.*; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.features.documentation.MarkdownConverter; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.services.LanguageServer; @@ -110,9 +110,9 @@ public static LSPCommandResponse executeCommand(LSPCommandContext context) { // 3. tentative fallback if (file != null && command.getArguments() != null) { - Document document = LSPIJUtils.getDocument(file); - if (document != null) { - WorkspaceEdit edit = createWorkspaceEdit(command.getArguments(), document); + LanguageServerItem languageServer = context.getPreferredLanguageServer(); + FileUriSupport fileUriSupport = languageServer != null ? languageServer.getClientFeatures() : null; + WorkspaceEdit edit = createWorkspaceEdit(command.getArguments(), file, fileUriSupport); if (edit != null) { if (ApplicationManager.getApplication().isWriteAccessAllowed()) { LSPIJUtils.applyWorkspaceEdit(edit); @@ -123,7 +123,6 @@ public static LSPCommandResponse executeCommand(LSPCommandContext context) { }); } return LSPCommandResponse.FOUND; - } } } if (context.isShowNotificationError()) { @@ -152,9 +151,9 @@ public static LSPCommandResponse executeCommand(LSPCommandContext context) { * do nothing. * @param languageServer the language server for which the {@code command} is * applicable. - * @param preferredLanguageServerId - * @param project - * @return true if the LSP command on server side has been executed successfully and false otherwise. + * @param preferredLanguageServerId the preferred language server Id. + * @param project the project. + * @return the LSP command response. */ private static LSPCommandResponse executeCommandServerSide(@NotNull Command command, @Nullable LanguageServerItem languageServer, @@ -297,9 +296,11 @@ private static final class Pair { * workspace edit... */ @Nullable - private static WorkspaceEdit createWorkspaceEdit(List commandArguments, @NotNull Document document) { + private static WorkspaceEdit createWorkspaceEdit(@NotNull List commandArguments, + @NotNull VirtualFile initialFile, + @Nullable FileUriSupport fileUriSupport) { Map> changes = new HashMap<>(); - URI initialUri = LSPIJUtils.toUri(document); + URI initialUri = FileUriSupport.getFileUri(initialFile, fileUriSupport); Pair> currentEntry = new Pair<>(initialUri, new ArrayList<>()); commandArguments.stream().flatMap(item -> { if (item instanceof List) { @@ -309,10 +310,10 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, } }).forEach(arg -> { if (arg instanceof String) { - changes.put(currentEntry.key.toString(), currentEntry.value); - VirtualFile resource = LSPIJUtils.findResourceFor((String) arg); + changes.put(currentEntry.key.toASCIIString(), currentEntry.value); + VirtualFile resource = FileUriSupport.findFileByUri((String) arg, fileUriSupport); if (resource != null) { - currentEntry.key = LSPIJUtils.toUri(resource); + currentEntry.key = FileUriSupport.getFileUri(resource, fileUriSupport); currentEntry.value = new ArrayList<>(); } } else if (arg instanceof WorkspaceEdit) { @@ -327,10 +328,10 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, } } else if (arg instanceof JsonPrimitive json) { if (json.isString()) { - changes.put(currentEntry.key.toString(), currentEntry.value); - VirtualFile resource = LSPIJUtils.findResourceFor(json.getAsString()); + changes.put(currentEntry.key.toASCIIString(), currentEntry.value); + VirtualFile resource = FileUriSupport.findFileByUri(json.getAsString(), fileUriSupport); if (resource != null) { - currentEntry.key = LSPIJUtils.toUri(resource); + currentEntry.key = FileUriSupport.getFileUri(resource, fileUriSupport); currentEntry.value = new ArrayList<>(); } } @@ -357,7 +358,7 @@ private static WorkspaceEdit createWorkspaceEdit(List commandArguments, } }); if (!currentEntry.value.isEmpty()) { - changes.put(currentEntry.key.toString(), currentEntry.value); + changes.put(currentEntry.key.toASCIIString(), currentEntry.value); } if (changes.isEmpty()) { return null; diff --git a/src/main/java/com/redhat/devtools/lsp4ij/commands/editor/ShowReferencesAction.java b/src/main/java/com/redhat/devtools/lsp4ij/commands/editor/ShowReferencesAction.java index b14b9e0da..7f8df927d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/commands/editor/ShowReferencesAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/commands/editor/ShowReferencesAction.java @@ -16,10 +16,13 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import com.redhat.devtools.lsp4ij.commands.LSPCommand; import com.redhat.devtools.lsp4ij.commands.LSPCommandAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import com.redhat.devtools.lsp4ij.usages.LSPUsagesManager; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; @@ -79,14 +82,21 @@ protected void commandPerformed(@NotNull LSPCommand command, @NotNull AnActionEv if (array == null) { return; } + + DataContext dataContext = e.getDataContext(); + LanguageServerItem languageServer = dataContext.getData(CommandExecutor.LSP_COMMAND_LANGUAGE_SERVER); + // Get LSP4J Location from the JSON locations array - final List locations = new ArrayList<>(); + final List locations = new ArrayList<>(); for (int i = 0; i < array.size(); i++) { - locations.add(JSONUtils.toModel(array.get(i), Location.class)); + locations.add(new LocationData(JSONUtils.toModel(array.get(i), Location.class), languageServer)); } - DataContext dataContext = e.getDataContext(); + // Call "Find Usages" in popup mode. - LSPUsagesManager.getInstance(project).findShowUsagesInPopup(locations, LSPUsageType.References, dataContext, (MouseEvent) e.getInputEvent()); + LSPUsagesManager.getInstance(project).findShowUsagesInPopup(locations, + LSPUsageType.References, + dataContext, + (MouseEvent) e.getInputEvent()); } @Override diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPDocumentFeatureSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPDocumentFeatureSupport.java index fdc8e798d..ee11f1164 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPDocumentFeatureSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPDocumentFeatureSupport.java @@ -13,7 +13,9 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LanguageServerItem; import com.redhat.devtools.lsp4ij.LanguageServiceAccessor; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.TextDocumentIdentifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -82,4 +84,10 @@ protected static CompletableFuture> getLanguageServers( beforeStartingServerFilter, afterStartingServerFilter); } + + protected static void updateTextDocumentUri(@NotNull TextDocumentIdentifier textDocument, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer) { + textDocument.setUri(FileUriSupport.getFileUri(file.getVirtualFile(), languageServer.getClientFeatures()).toASCIIString()); + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPGoToAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPGoToAction.java index c2437af34..6bcaf4523 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPGoToAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/AbstractLSPGoToAction.java @@ -29,6 +29,7 @@ import com.redhat.devtools.lsp4ij.client.indexing.ProjectIndexingManager; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import com.redhat.devtools.lsp4ij.usages.LSPUsagesManager; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; @@ -82,21 +83,24 @@ public void actionPerformed(@NotNull AnActionEvent e) { ProgressManager.getInstance().run(new Task.Backgroundable(project, getProgressTitle(psiFile, offset), true) { @Override public void run(@NotNull ProgressIndicator indicator) { - List locations = null; + List locations = null; try { - CompletableFuture> locationsFuture = getLocations(psiFile, document, editor, offset); + CompletableFuture> locationsFuture = getLocations(psiFile, document, editor, offset); if (isDoneNormally(locationsFuture)) { // textDocument/(declaration|implementation|references|typeDefinition) has been collected correctly locations = locationsFuture.getNow(null); } } finally { - final List resultLocations = locations != null ? locations : Collections.emptyList(); + final List resultLocations = locations != null ? locations : Collections.emptyList(); DataContext dataContext = e.getDataContext(); // Call "Find Usages" in popup mode. ApplicationManager.getApplication() .invokeLater(() -> - LSPUsagesManager.getInstance(project).findShowUsagesInPopup(resultLocations, usageType, dataContext, null) + LSPUsagesManager.getInstance(project).findShowUsagesInPopup(resultLocations, + usageType, + dataContext, + null) ); } } @@ -148,10 +152,10 @@ private boolean canSupportAction(@NotNull AnActionEvent e) { * @param offset the offset. * @return the LSP {@link Location} list result of the execution of the LSP GoTo feature. */ - protected abstract CompletableFuture> getLocations(@NotNull PsiFile psiFile, - @NotNull Document document, - @NotNull Editor editor, - int offset); + protected abstract CompletableFuture> getLocations(@NotNull PsiFile psiFile, + @NotNull Document document, + @NotNull Editor editor, + int offset); /** * Returns true if the action is supported by the client features of the language server and false otherwise. diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/LSPPsiElementFactory.java b/src/main/java/com/redhat/devtools/lsp4ij/features/LSPPsiElementFactory.java index 52969b918..3fb5e9983 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/LSPPsiElementFactory.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/LSPPsiElementFactory.java @@ -18,6 +18,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; import org.eclipse.lsp4j.Range; @@ -50,8 +51,10 @@ public interface LSPPsiElementFactory { * @return an instance of {@link LSPPsiElement} from the given LSP location and null otherwise. */ @Nullable - public static LSPPsiElement toPsiElement(@NotNull Location location, @NotNull Project project) { - return toPsiElement(location, project, DEFAULT); + public static LSPPsiElement toPsiElement(@NotNull Location location, + @Nullable FileUriSupport fileUriSupport, + @NotNull Project project) { + return toPsiElement(location, fileUriSupport, project, DEFAULT); } /** @@ -64,13 +67,14 @@ public static LSPPsiElement toPsiElement(@NotNull Location location, @NotNull Pr */ @Nullable public static T toPsiElement(@NotNull Location location, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project, @NotNull LSPPsiElementFactory factory) { if (ApplicationManager.getApplication().isReadAccessAllowed()) { - return doToPsiElement(location.getUri(), location.getRange(), project, factory); + return doToPsiElement(location.getUri(), location.getRange(), fileUriSupport, project, factory); } return ReadAction.compute(() -> { - return doToPsiElement(location.getUri(), location.getRange(), project, factory); + return doToPsiElement(location.getUri(), location.getRange(), fileUriSupport, project, factory); }); } @@ -82,8 +86,9 @@ public static T toPsiElement(@NotNull Location locatio * @return an instance of {@link LSPPsiElement} from the given LSP location link and null otherwise. */ public static LSPPsiElement toPsiElement(@NotNull LocationLink location, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project) { - return toPsiElement(location, project, DEFAULT); + return toPsiElement(location, fileUriSupport, project, DEFAULT); } /** @@ -96,25 +101,27 @@ public static LSPPsiElement toPsiElement(@NotNull LocationLink location, */ @Nullable public static T toPsiElement(@NotNull LocationLink location, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project, @NotNull LSPPsiElementFactory factory) { if (ApplicationManager.getApplication().isReadAccessAllowed()) { - return doToPsiElement(location.getTargetUri(), location.getTargetRange(), project, factory); + return doToPsiElement(location.getTargetUri(), location.getTargetRange(), fileUriSupport, project, factory); } return ReadAction.compute(() -> { - return doToPsiElement(location.getTargetUri(), location.getTargetRange(), project, factory); + return doToPsiElement(location.getTargetUri(), location.getTargetRange(), fileUriSupport, project, factory); }); } @Nullable private static T doToPsiElement(@Nullable String uri, @Nullable Range range, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project, @NotNull LSPPsiElementFactory factory) { if (uri == null || range == null) { return null; } - VirtualFile file = LSPIJUtils.findResourceFor(uri); + VirtualFile file = FileUriSupport.findFileByUri(uri, fileUriSupport); if (file == null) { return null; } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyIncomingCallsSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyIncomingCallsSupport.java index 44bef7313..5b8a4070e 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyIncomingCallsSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyIncomingCallsSupport.java @@ -66,7 +66,7 @@ protected CompletableFuture> doLoad(CallHierarchyInc // Collect list of callHierarchy/incomingCalls future for each language servers List>> callHierarchyPerServerFutures = languageServers .stream() - .map(languageServer -> getCallHierarchyIncomingCalls(params, languageServer, file, cancellationSupport)) + .map(languageServer -> getCallHierarchyIncomingCalls(params, languageServer, cancellationSupport)) .toList(); // Merge list of callHierarchy/incomingCalls future in one future which return the list of call hierarchy items @@ -76,7 +76,6 @@ protected CompletableFuture> doLoad(CallHierarchyInc private static CompletableFuture> getCallHierarchyIncomingCalls(@NotNull CallHierarchyIncomingCallsParams params, @NotNull LanguageServerItem languageServer, - @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyOutgoingCallsSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyOutgoingCallsSupport.java index f2d422573..7a89f5302 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyOutgoingCallsSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyOutgoingCallsSupport.java @@ -66,7 +66,7 @@ protected CompletableFuture> doLoad(CallHierarchyOut // Collect list of callHierarchy/outgoingCalls future for each language servers List>> callHierarchyPerServerFutures = languageServers .stream() - .map(languageServer -> getCallHierarchyOutgoingCalls(params, languageServer, file, cancellationSupport)) + .map(languageServer -> getCallHierarchyOutgoingCalls(params, languageServer, cancellationSupport)) .toList(); // Merge list of callHierarchy/outgoingCalls future in one future which return the list of call hierarchy items @@ -76,7 +76,6 @@ protected CompletableFuture> doLoad(CallHierarchyOut private static CompletableFuture> getCallHierarchyOutgoingCalls(@NotNull CallHierarchyOutgoingCallsParams params, @NotNull LanguageServerItem languageServer, - @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyTreeStructureBase.java b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyTreeStructureBase.java index bc0128846..7985258b2 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyTreeStructureBase.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyTreeStructureBase.java @@ -55,7 +55,11 @@ public LSPCallHierarchyTreeStructureBase(@NotNull Project project, @NotNull PsiE } @Override - protected void buildRoot(@NotNull HierarchyNodeDescriptor descriptor, PsiFile psiFile, Document document, int offset, List descriptors) { + protected void buildRoot(@NotNull HierarchyNodeDescriptor descriptor, + @NotNull PsiFile psiFile, + @NotNull Document document, + int offset, + @NotNull List descriptors) { // Consume LSP 'textDocument/prepareCallHierarchy' request LSPPrepareCallHierarchySupport prepareCallHierarchySupport = LSPFileSupport.getSupport(psiFile).getPrepareCallHierarchySupport(); var params = new LSPCallHierarchyPrepareParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); @@ -82,7 +86,10 @@ protected void fillChildren(@NotNull HierarchyNodeDescriptor descriptor, if (items != null) { for (var item : items) { var callHierarchyItem = item.callHierarchyItem(); - PsiElement element = createPsiElement(callHierarchyItem.getUri(), callHierarchyItem.getRange(), callHierarchyItem.getName()); + PsiElement element = createPsiElement(callHierarchyItem.getUri(), + callHierarchyItem.getRange(), + callHierarchyItem.getName(), + item.languageServer().getClientFeatures()); if (element != null) { descriptors.add(createHierarchyNodeDescriptor(myProject, descriptor, element, callHierarchyItem)); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPPrepareCallHierarchySupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPPrepareCallHierarchySupport.java index ae95f81df..2e1b056e3 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPPrepareCallHierarchySupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPPrepareCallHierarchySupport.java @@ -86,6 +86,8 @@ private static CompletableFuture> getCallHierarchies @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .prepareCallHierarchy(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/intention/LSPIntentionCodeActionSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/intention/LSPIntentionCodeActionSupport.java index bfe381e41..e1b6c3af7 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/intention/LSPIntentionCodeActionSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/intention/LSPIntentionCodeActionSupport.java @@ -78,7 +78,7 @@ protected CompletableFuture> doLoad(@NotNull CodeActionPara // Collect list of textDocument/codeAction future for each language servers List>> codeActionPerServerFutures = languageServers .stream() - .map(languageServer -> getCodeActionsFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getCodeActionsFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/codeAction future in one future which return the list of code actions @@ -87,8 +87,11 @@ protected CompletableFuture> doLoad(@NotNull CodeActionPara } private static CompletableFuture> getCodeActionsFor(@NotNull CodeActionParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .codeAction(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_CODE_ACTION) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java index 47f1040d5..3786da29a 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java @@ -85,6 +85,8 @@ private static CompletableFuture> getCodeLensesFor(@NotNull C @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .codeLens(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_CODE_LENS) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java index cd17d90e3..859038416 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java @@ -65,7 +65,7 @@ protected CompletableFuture> doLoad(@NotNull DocumentColorParams // Collect list of textDocument/documentColor future for each language servers List>> colorInformationPerServerFutures = languageServers .stream() - .map(languageServer -> getColorsFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getColorsFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/documentColor future in one future which return the list of color information @@ -74,8 +74,12 @@ protected CompletableFuture> doLoad(@NotNull DocumentColorParams } private static CompletableFuture> getColorsFor(@NotNull DocumentColorParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .documentColor(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_COLOR) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionSupport.java index d7f54d1b2..d15d2e736 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/completion/LSPCompletionSupport.java @@ -85,6 +85,8 @@ private static CompletableFuture> getCompletionsFor(@NotNul @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); params.setContext(createCompletionContext(params, file, languageServer)); return cancellationSupport.execute(languageServer .getTextDocumentService() diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPDeclarationSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPDeclarationSupport.java index 4fd43e128..91d13ffc9 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPDeclarationSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPDeclarationSupport.java @@ -17,7 +17,7 @@ import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -25,12 +25,12 @@ /** * LSP declaration support which collect: - * + * *
      *
    • textDocument/declaration
    • *
    */ -public class LSPDeclarationSupport extends AbstractLSPDocumentFeatureSupport> { +public class LSPDeclarationSupport extends AbstractLSPDocumentFeatureSupport> { private Integer previousOffset; @@ -38,7 +38,7 @@ public LSPDeclarationSupport(@NotNull PsiFile file) { super(file); } - public CompletableFuture> getDeclarations(LSPDeclarationParams params) { + public CompletableFuture> getDeclarations(LSPDeclarationParams params) { int offset = params.getOffset(); if (previousOffset != null && !previousOffset.equals(offset)) { super.cancel(); @@ -48,17 +48,17 @@ public CompletableFuture> getDeclarations(LSPDeclarationParams pa } @Override - protected CompletableFuture> doLoad(LSPDeclarationParams params, CancellationSupport cancellationSupport) { + protected CompletableFuture> doLoad(LSPDeclarationParams params, CancellationSupport cancellationSupport) { PsiFile file = super.getFile(); return collectDeclarations(file, params, cancellationSupport); } - private static @NotNull CompletableFuture> collectDeclarations(@NotNull PsiFile file, - @NotNull LSPDeclarationParams params, - @NotNull CancellationSupport cancellationSupport) { + private static @NotNull CompletableFuture> collectDeclarations(@NotNull PsiFile file, + @NotNull LSPDeclarationParams params, + @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, - f -> f.getDeclarationFeature().isEnabled(file), - f -> f.getDeclarationFeature().isSupported(file)) + f -> f.getDeclarationFeature().isEnabled(file), + f -> f.getDeclarationFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have declaration capability @@ -67,9 +67,9 @@ protected CompletableFuture> doLoad(LSPDeclarationParams params, } // Collect list of textDocument/declaration future for each language servers - List>> declarationsPerServerFutures = languageServers + List>> declarationsPerServerFutures = languageServers .stream() - .map(languageServer -> getDeclarationFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getDeclarationFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/declaration future in one future which return the list of declaration ranges @@ -77,12 +77,15 @@ protected CompletableFuture> doLoad(LSPDeclarationParams params, }); } - private static CompletableFuture> getDeclarationFor(LSPDeclarationParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getDeclarationFor(@NotNull LSPDeclarationParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .declaration(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DECLARATION) - .thenApplyAsync(LSPIJUtils::getLocations); + .thenApplyAsync(locations -> LSPIJUtils.getLocations(locations, languageServer)); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java index 3c47b6d12..2f6650034 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java @@ -20,6 +20,7 @@ import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -44,13 +45,13 @@ public LSPGoToDeclarationAction() { } @Override - protected CompletableFuture> getLocations(@NotNull PsiFile psiFile, + protected CompletableFuture> getLocations(@NotNull PsiFile psiFile, @NotNull Document document, @NotNull Editor editor, int offset) { LSPDeclarationSupport declarationSupport = LSPFileSupport.getSupport(psiFile).getDeclarationSupport(); var params = new LSPDeclarationParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> declarationsFuture = declarationSupport.getDeclarations(params); + CompletableFuture> declarationsFuture = declarationSupport.getDeclarations(params); try { waitUntilDone(declarationsFuture, psiFile); } catch (ProcessCanceledException ex) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticHandler.java index 161c86e39..ef6c00281 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticHandler.java @@ -25,6 +25,7 @@ import com.redhat.devtools.lsp4ij.LSPVirtualFileData; import com.redhat.devtools.lsp4ij.LanguageServerWrapper; import com.redhat.devtools.lsp4ij.client.CoalesceByKey; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.jetbrains.annotations.NotNull; @@ -74,7 +75,7 @@ private void updateDiagnostics(@NotNull PublishDiagnosticsParams params, @NotNul if (project.isDisposed()) { return; } - VirtualFile file = LSPIJUtils.findResourceFor(params.getUri()); + VirtualFile file = FileUriSupport.findFileByUri(params.getUri(), languageServerWrapper.getClientFeatures()); if (file == null) { return; } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkGotoDeclarationHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkGotoDeclarationHandler.java index 1b15fa664..293c035c3 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkGotoDeclarationHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkGotoDeclarationHandler.java @@ -26,6 +26,7 @@ import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServersRegistry; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import org.eclipse.lsp4j.DocumentLink; import org.eclipse.lsp4j.DocumentLinkParams; import org.jetbrains.annotations.Nullable; @@ -90,13 +91,14 @@ public class LSPDocumentLinkGotoDeclarationHandler implements GotoDeclarationHan // 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); + FileUriSupport fileUriSupport = documentLinkData.languageServer().getClientFeatures(); + VirtualFile targetFile = FileUriSupport.findFileByUri(target, fileUriSupport ); 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. - LSPIJUtils.openInEditor(target, null, true, true, project); + LSPIJUtils.openInEditor(target, null, true, true, fileUriSupport, project); // Return an empty response here. // If user accepts to create the file, the open is done after the creation of the file. return PsiElement.EMPTY_ARRAY; diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkSupport.java index c8fd67e7d..770914acc 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentLink/LSPDocumentLinkSupport.java @@ -64,7 +64,7 @@ protected CompletableFuture> doLoad(@NotNull DocumentLink // Collect list of textDocument/documentLink future for each language servers List>> linkInformationPerServerFutures = languageServers .stream() - .map(languageServer -> getDocumentLinksFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getDocumentLinksFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/documentLink future in one future which return the list of document link @@ -73,8 +73,11 @@ protected CompletableFuture> doLoad(@NotNull DocumentLink } private static CompletableFuture> getDocumentLinksFor(@NotNull DocumentLinkParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .documentLink(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_LINK) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolSupport.java index b8c43db74..c7635a124 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentSymbol/LSPDocumentSymbolSupport.java @@ -64,7 +64,7 @@ protected CompletableFuture> doLoad(DocumentSymbolParam // Collect list of textDocument/documentSymbol future for each language servers List>> documentSymbolInformationPerServerFutures = languageServers .stream() - .map(languageServer -> getDocumentSymbolsFor(params, languageServer, file, cancellationSupport)) + .map(languageServer -> getDocumentSymbolsFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/documentSymbol future in one future which return the list of document link @@ -73,9 +73,11 @@ protected CompletableFuture> doLoad(DocumentSymbolParam } private static CompletableFuture> getDocumentSymbolsFor(@NotNull DocumentSymbolParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, - @NotNull PsiFile psiFile, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .documentSymbol(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_SYMBOL) @@ -89,9 +91,9 @@ private static CompletableFuture> getDocumentSymbolsFor .map(symbol -> { if (symbol.isLeft()) { var si = symbol.getLeft(); - return new DocumentSymbolData(convertToDocumentSymbol(si), psiFile, languageServer); + return new DocumentSymbolData(convertToDocumentSymbol(si), file, languageServer); } else { - return new DocumentSymbolData(symbol.getRight(), psiFile, languageServer); + return new DocumentSymbolData(symbol.getRight(), file, languageServer); } }) .toList(); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationLinkHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationLinkHandler.java index cde81e66e..992a36349 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationLinkHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationLinkHandler.java @@ -15,6 +15,7 @@ import com.intellij.platform.backend.documentation.DocumentationTarget; import com.intellij.platform.backend.documentation.LinkResolveResult; import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +34,8 @@ public class LSPDocumentationLinkHandler implements DocumentationLinkHandler { ApplicationManager.getApplication() .executeOnPooledThread(() -> { var file = lspTarget.getFile(); - LSPIJUtils.openInEditor(url, null, true, true, file.getProject()); + FileUriSupport fileUriSupport = lspTarget.getLanguageServer().getClientFeatures();; + LSPIJUtils.openInEditor(url, null, true, true, fileUriSupport, file.getProject()); }); return LinkResolveResult.resolvedTarget(target); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationTarget.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationTarget.java index 781024887..354e56fb3 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationTarget.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPDocumentationTarget.java @@ -67,4 +67,8 @@ public Pointer createPointer() { public PsiFile getFile() { return file; } + + public LanguageServerItem getLanguageServer() { + return languageServer; + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPHoverSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPHoverSupport.java index 063a982f9..10bb49ffe 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPHoverSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/documentation/LSPHoverSupport.java @@ -70,7 +70,7 @@ protected CompletableFuture> doLoad(HoverParams params, Cancella // Collect list of textDocument/hover future for each language servers List> hoverPerServerFutures = languageServers .stream() - .map(languageServer -> getHoverFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getHoverFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/hover future in one future which return the list of highlights @@ -95,8 +95,11 @@ protected CompletableFuture> doLoad(HoverParams params, Cancella } private static CompletableFuture<@Nullable HoverData> getHoverFor(@NotNull HoverParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .hover(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_HOVER) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/foldingRange/LSPFoldingRangeSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/foldingRange/LSPFoldingRangeSupport.java index 0372afc02..7c722782b 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/foldingRange/LSPFoldingRangeSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/foldingRange/LSPFoldingRangeSupport.java @@ -65,7 +65,7 @@ protected CompletableFuture> doLoad(FoldingRangeRequestParams // Collect list of textDocument/foldingRange future for each language servers List>> foldingRangesPerServerFutures = languageServers .stream() - .map(languageServer -> getFoldingRangesFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getFoldingRangesFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/foldingRange future in one future which return the list of folding ranges @@ -73,9 +73,12 @@ protected CompletableFuture> doLoad(FoldingRangeRequestParams }); } - private static CompletableFuture> getFoldingRangesFor(FoldingRangeRequestParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getFoldingRangesFor(@NotNull FoldingRangeRequestParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .foldingRange(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_FOLDING_RANGE) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java index c29d7e1a9..69531446f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java @@ -19,6 +19,7 @@ import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LSPRequestConstants; import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; @@ -110,14 +111,14 @@ protected CompletableFuture> doLoad(LSPFormattingParams if (isRangeFormatting && languageServer.isDocumentRangeFormattingSupported()) { // Range formatting - DocumentRangeFormattingParams lspParams = createDocumentRangeFormattingParams(params.tabSize(), params.insertSpaces(), params.textRange(), params.document()); + DocumentRangeFormattingParams lspParams = createDocumentRangeFormattingParams(params.tabSize(), params.insertSpaces(), params.textRange(), params.document(), languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .rangeFormatting(lspParams), languageServer, LSPRequestConstants.TEXT_DOCUMENT_RANGE_FORMATTING); } // Full document formatting - DocumentFormattingParams lspParams = createDocumentFormattingParams(params.tabSize(), params.insertSpaces()); + DocumentFormattingParams lspParams = createDocumentFormattingParams(params.tabSize(), params.insertSpaces(), languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .formatting(lspParams), languageServer, LSPRequestConstants.TEXT_DOCUMENT_FORMATTING); @@ -139,9 +140,11 @@ private static LanguageServerItem getFormattingLanguageServer(List> doLoad(DocumentHighlightPar // Collect list of textDocument/highlights future for each language servers List>> highlightsPerServerFutures = languageServers .stream() - .map(languageServer -> getHighlightsFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getHighlightsFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/highlights future in one future which return the list of highlights @@ -80,8 +80,11 @@ protected CompletableFuture> doLoad(DocumentHighlightPar } private static CompletableFuture> getHighlightsFor(@NotNull DocumentHighlightParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .documentHighlight(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java index 1ccdd1a65..1d6daa2b0 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java @@ -20,7 +20,7 @@ import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,10 +44,10 @@ public LSPGoToImplementationAction() { } @Override - protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { + protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { LSPImplementationSupport implementationSupport = LSPFileSupport.getSupport(psiFile).getImplementationSupport(); var params = new LSPImplementationParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> implementationsFuture = implementationSupport.getImplementations(params); + CompletableFuture> implementationsFuture = implementationSupport.getImplementations(params); try { waitUntilDone(implementationsFuture, psiFile); } catch (ProcessCanceledException ex) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPImplementationSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPImplementationSupport.java index 3795cc134..0a27d98bd 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPImplementationSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPImplementationSupport.java @@ -17,7 +17,7 @@ import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -30,7 +30,7 @@ *
  • textDocument/implementation
  • * */ -public class LSPImplementationSupport extends AbstractLSPDocumentFeatureSupport> { +public class LSPImplementationSupport extends AbstractLSPDocumentFeatureSupport> { private Integer previousOffset; @@ -38,7 +38,7 @@ public LSPImplementationSupport(@NotNull PsiFile file) { super(file); } - public CompletableFuture> getImplementations(LSPImplementationParams params) { + public CompletableFuture> getImplementations(LSPImplementationParams params) { int offset = params.getOffset(); if (previousOffset != null && !previousOffset.equals(offset)) { super.cancel(); @@ -48,12 +48,12 @@ public CompletableFuture> getImplementations(LSPImplementationPar } @Override - protected CompletableFuture> doLoad(LSPImplementationParams params, CancellationSupport cancellationSupport) { + protected CompletableFuture> doLoad(LSPImplementationParams params, CancellationSupport cancellationSupport) { PsiFile file = super.getFile(); return collectImplementations(file, params, cancellationSupport); } - private static @NotNull CompletableFuture> collectImplementations(@NotNull PsiFile file, + private static @NotNull CompletableFuture> collectImplementations(@NotNull PsiFile file, @NotNull LSPImplementationParams params, @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, @@ -67,9 +67,9 @@ protected CompletableFuture> doLoad(LSPImplementationParams param } // Collect list of textDocument/implementation future for each language servers - List>> implementationsPerServerFutures = languageServers + List>> implementationsPerServerFutures = languageServers .stream() - .map(languageServer -> getImplementationFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getImplementationFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/implementation future in one future which return the list of implementation ranges @@ -77,12 +77,15 @@ protected CompletableFuture> doLoad(LSPImplementationParams param }); } - private static CompletableFuture> getImplementationFor(LSPImplementationParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getImplementationFor(@NotNull LSPImplementationParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .implementation(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_IMPLEMENTATION) - .thenApplyAsync(LSPIJUtils::getLocations); + .thenApplyAsync(locations -> LSPIJUtils.getLocations(locations, languageServer)); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java index 8d259a5af..4937db43d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java @@ -79,6 +79,8 @@ private static CompletableFuture> getInlayHintsFor(@NotNull @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .inlayHint(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_INLAY_HINT) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPDefinitionSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPDefinitionSupport.java index b31a74e19..950cc2b99 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPDefinitionSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPDefinitionSupport.java @@ -17,7 +17,7 @@ import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -30,7 +30,7 @@ *
  • textDocument/definition
  • * */ -public class LSPDefinitionSupport extends AbstractLSPDocumentFeatureSupport> { +public class LSPDefinitionSupport extends AbstractLSPDocumentFeatureSupport> { private Integer previousOffset; @@ -38,7 +38,7 @@ public LSPDefinitionSupport(@NotNull PsiFile file) { super(file); } - public CompletableFuture> getDefinitions(LSPDefinitionParams params) { + public CompletableFuture> getDefinitions(LSPDefinitionParams params) { int offset = params.getOffset(); if (previousOffset != null && !previousOffset.equals(offset)) { super.cancel(); @@ -48,17 +48,17 @@ public CompletableFuture> getDefinitions(LSPDefinitionParams para } @Override - protected CompletableFuture> doLoad(LSPDefinitionParams params, CancellationSupport cancellationSupport) { + protected CompletableFuture> doLoad(LSPDefinitionParams params, CancellationSupport cancellationSupport) { PsiFile file = super.getFile(); return collectDefinitions(file, params, cancellationSupport); } - private static @NotNull CompletableFuture> collectDefinitions(@NotNull PsiFile psiFile, + private static @NotNull CompletableFuture> collectDefinitions(@NotNull PsiFile file, @NotNull LSPDefinitionParams params, @NotNull CancellationSupport cancellationSupport) { - return getLanguageServers(psiFile, - f -> f.getDefinitionFeature().isEnabled(psiFile), - f -> f.getDefinitionFeature().isSupported(psiFile)) + return getLanguageServers(file, + f -> f.getDefinitionFeature().isEnabled(file), + f -> f.getDefinitionFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have definition capability @@ -67,9 +67,9 @@ protected CompletableFuture> doLoad(LSPDefinitionParams params, C } // Collect list of textDocument/definition future for each language servers - List>> definitionsPerServerFutures = languageServers + List>> definitionsPerServerFutures = languageServers .stream() - .map(languageServer -> getDefinitionFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getDefinitionFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/definition future in one future which return the list of definition ranges @@ -77,12 +77,15 @@ protected CompletableFuture> doLoad(LSPDefinitionParams params, C }); } - private static CompletableFuture> getDefinitionFor(LSPDefinitionParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getDefinitionFor(@NotNull LSPDefinitionParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .definition(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DECLARATION) - .thenApplyAsync(LSPIJUtils::getLocations); + .thenApplyAsync(locations -> LSPIJUtils.getLocations(locations, languageServer)); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPGotoDeclarationHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPGotoDeclarationHandler.java index 23ecd523d..f8b065ac4 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPGotoDeclarationHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/navigation/LSPGotoDeclarationHandler.java @@ -21,7 +21,7 @@ import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServersRegistry; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,7 +63,7 @@ public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement // Consume LSP 'textDocument/definition' request LSPDefinitionSupport definitionSupport = LSPFileSupport.getSupport(psiFile).getDefinitionSupport(); var params = new LSPDefinitionParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> definitionsFuture = definitionSupport.getDefinitions(params); + CompletableFuture> definitionsFuture = definitionSupport.getDefinitions(params); try { waitUntilDone(definitionsFuture, psiFile); } catch (ProcessCanceledException ex) { @@ -77,12 +77,12 @@ public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement } if (isDoneNormally(definitionsFuture)) { - // textDocument/implementations has been collected correctly - List locations = definitionsFuture.getNow(null); + // textDocument/definition has been collected correctly + List locations = definitionsFuture.getNow(null); if (locations != null) { return locations .stream() - .map(location -> toPsiElement(location, project)) + .map(location -> toPsiElement(location.location(), location.languageServer().getClientFeatures(), project)) .filter(Objects::nonNull) .toArray(PsiElement[]::new); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/refactoring/LSPRenamePsiElementProcessor.java b/src/main/java/com/redhat/devtools/lsp4ij/features/refactoring/LSPRenamePsiElementProcessor.java index bc7c681d4..f1e559986 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/refactoring/LSPRenamePsiElementProcessor.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/refactoring/LSPRenamePsiElementProcessor.java @@ -22,7 +22,11 @@ import com.intellij.refactoring.rename.RenamePsiElementProcessor; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; -import com.redhat.devtools.lsp4ij.*; +import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.LSPRequestConstants; +import com.redhat.devtools.lsp4ij.LanguageServersRegistry; +import com.redhat.devtools.lsp4ij.LanguageServiceAccessor; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.features.LSPPsiElement; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import org.eclipse.lsp4j.FileRename; @@ -100,7 +104,8 @@ public void prepareRenaming(@NotNull PsiElement element, // Wait until the future of LSP 'workspace/willRenameFiles' request // is finished and stop the wait if there are some ProcessCanceledException. waitUntilDone(willRenameFilesFuture, file); - } catch (ProcessCanceledException ignore) {//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility + } catch ( + ProcessCanceledException ignore) {//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility //TODO delete block when minimum required version is 2024.2 } catch (CancellationException ignore) { } catch (ExecutionException e) { @@ -115,6 +120,7 @@ public void prepareRenaming(@NotNull PsiElement element, LSPRenameFilesContextHolder.set(new LSPRenameFilesContext(params, Collections.singletonList(editData.languageServer()), file)); var edit = editData.edit(); + var fileUriSupport = editData.languageServer().getClientFeatures(); var documentChanges = edit.getDocumentChanges(); if (documentChanges != null) { for (var documentChange : documentChanges) { @@ -122,7 +128,7 @@ public void prepareRenaming(@NotNull PsiElement element, var ed = documentChange.getLeft(); String uri = ed.getTextDocument().getUri(); List textEdits = ed.getEdits(); - addPsiElementRename(uri, textEdits, allRenames, project); + addPsiElementRename(uri, textEdits, allRenames, fileUriSupport, project); } } } @@ -132,7 +138,7 @@ public void prepareRenaming(@NotNull PsiElement element, changes.entrySet().forEach(entry -> { String uri = entry.getKey(); List textEdits = entry.getValue(); - addPsiElementRename(uri, textEdits, allRenames, project); + addPsiElementRename(uri, textEdits, allRenames, fileUriSupport, project); }); } } @@ -142,8 +148,9 @@ public void prepareRenaming(@NotNull PsiElement element, private static void addPsiElementRename(@NotNull String uri, @NotNull List textEdits, @NotNull Map allRenames, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project) { - VirtualFile file = LSPIJUtils.findResourceFor(uri); + VirtualFile file = FileUriSupport.findFileByUri(uri, fileUriSupport); if (file != null) { Document document = LSPIJUtils.getDocument(file); PsiFile psiFile = LSPIJUtils.getPsiFile(file, project); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java index d94f0af5c..d951c534d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java @@ -20,7 +20,7 @@ import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,10 +44,10 @@ public LSPGoToReferenceAction() { } @Override - protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { + protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { LSPReferenceSupport referenceSupport = LSPFileSupport.getSupport(psiFile).getReferenceSupport(); var params = new LSPReferenceParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> referencesFuture = referenceSupport.getReferences(params); + CompletableFuture> referencesFuture = referenceSupport.getReferences(params); try { waitUntilDone(referencesFuture, psiFile); } catch (ProcessCanceledException ex) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPReferenceSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPReferenceSupport.java index 67a35240c..d0ecd6525 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPReferenceSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPReferenceSupport.java @@ -16,6 +16,7 @@ import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; @@ -30,7 +31,7 @@ *
  • textDocument/reference
  • * */ -public class LSPReferenceSupport extends AbstractLSPDocumentFeatureSupport> { +public class LSPReferenceSupport extends AbstractLSPDocumentFeatureSupport> { private Integer previousOffset; @@ -38,7 +39,7 @@ public LSPReferenceSupport(@NotNull PsiFile file) { super(file); } - public CompletableFuture> getReferences(LSPReferenceParams params) { + public CompletableFuture> getReferences(LSPReferenceParams params) { int offset = params.getOffset(); if (previousOffset != null && !previousOffset.equals(offset)) { super.cancel(); @@ -48,12 +49,12 @@ public CompletableFuture> getReferences(LSPReferenceParams params } @Override - protected CompletableFuture> doLoad(LSPReferenceParams params, CancellationSupport cancellationSupport) { + protected CompletableFuture> doLoad(LSPReferenceParams params, CancellationSupport cancellationSupport) { PsiFile file = super.getFile(); return collectReferences(file, params, cancellationSupport); } - private static @NotNull CompletableFuture> collectReferences(@NotNull PsiFile file, + private static @NotNull CompletableFuture> collectReferences(@NotNull PsiFile file, @NotNull LSPReferenceParams params, @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, @@ -67,9 +68,9 @@ protected CompletableFuture> doLoad(LSPReferenceParams params, Ca } // Collect list of textDocument/reference future for each language servers - List>> referencesPerServerFutures = languageServers + List>> referencesPerServerFutures = languageServers .stream() - .map(languageServer -> getReferenceFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getReferenceFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/reference future in one future which return the list of reference ranges @@ -77,9 +78,12 @@ protected CompletableFuture> doLoad(LSPReferenceParams params, Ca }); } - private static CompletableFuture> getReferenceFor(LSPReferenceParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getReferenceFor(@NotNull LSPReferenceParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .references(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_REFERENCES) @@ -90,7 +94,7 @@ private static CompletableFuture> getReferenceFor(LSPReferencePar } return locations .stream() - .map(l -> new Location(l.getUri(), l.getRange())) + .map(l -> new LocationData(new Location(l.getUri(), l.getRange()), languageServer)) .toList(); }); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPPrepareRenameSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPPrepareRenameSupport.java index 9c3e54e11..0d8bd2fbe 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPPrepareRenameSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPPrepareRenameSupport.java @@ -78,7 +78,7 @@ protected CompletableFuture> doLoad(@NotNull LSPPr for (var languageServer : languageServers) { CompletableFuture> future = null; if (languageServer.isPrepareRenameSupported()) { - future = getPrepareRenamesFor(params, defaultPrepareRenameResult, languageServer, cancellationSupport); + future = getPrepareRenamesFor(params, file, defaultPrepareRenameResult, languageServer, cancellationSupport); } else { var result = defaultPrepareRenameResult.apply(languageServer); if (result != null) { @@ -97,9 +97,12 @@ protected CompletableFuture> doLoad(@NotNull LSPPr } private static CompletableFuture> getPrepareRenamesFor(@NotNull PrepareRenameParams params, + @NotNull PsiFile file, @NotNull DefaultPrepareRenameResultProvider defaultPrepareRenameResultProvider, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .prepareRename(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_PREPARE_RENAME, diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameSupport.java index b775770a9..2811d2fd7 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameSupport.java @@ -52,15 +52,16 @@ public CompletableFuture> getRename(@NotNull LSPRenamePa @Override protected CompletableFuture> doLoad(@NotNull LSPRenameParams params, @NotNull CancellationSupport cancellationSupport) { - return getRename(params, cancellationSupport); + return getRename(params, getFile(), cancellationSupport); } private static @NotNull CompletableFuture> getRename(@NotNull LSPRenameParams params, + @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { // Collect list of textDocument/rename future for each language servers List>> renamePerServerFutures = params.getLanguageServers() .stream() - .map(languageServer -> getRenameFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getRenameFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/rename future in one future which return the list of workspace edit @@ -68,8 +69,11 @@ protected CompletableFuture> doLoad(@NotNull LSPRenamePa } private static CompletableFuture> getRenameFor(@NotNull RenameParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .rename(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_RENAME, diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/selectionRange/LSPSelectionRangeSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/selectionRange/LSPSelectionRangeSupport.java index 2b5b79e0c..30163b48a 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/selectionRange/LSPSelectionRangeSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/selectionRange/LSPSelectionRangeSupport.java @@ -71,7 +71,7 @@ protected CompletableFuture> doLoad(LSPSelectionRangeParams // Collect list of textDocument/selectionRange future for each language servers List>> selectionRangesPerServerFutures = languageServers .stream() - .map(languageServer -> getSelectionRangesFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getSelectionRangesFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/selectionRange future in one future which return the list of selection ranges @@ -79,9 +79,12 @@ protected CompletableFuture> doLoad(LSPSelectionRangeParams }); } - private static CompletableFuture> getSelectionRangesFor(LSPSelectionRangeParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getSelectionRangesFor(@NotNull LSPSelectionRangeParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .selectionRange(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_SELECTION_RANGE) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPSemanticTokensSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPSemanticTokensSupport.java index 8da403a07..32fd7f45e 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPSemanticTokensSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPSemanticTokensSupport.java @@ -64,7 +64,7 @@ protected CompletableFuture doLoad(SemanticTokensParams para // Collect list of textDocument/semanticTokens future for each language servers List> semanticTokensPerServerFutures = languageServers .stream() - .map(languageServer -> getSemanticTokensFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getSemanticTokensFor(params, file, languageServer, cancellationSupport)) .filter(Objects::nonNull) .toList(); @@ -73,9 +73,12 @@ protected CompletableFuture doLoad(SemanticTokensParams para }); } - private static CompletableFuture getSemanticTokensFor(SemanticTokensParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture getSemanticTokensFor(@NotNull SemanticTokensParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .semanticTokensFull(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/signatureHelp/LSPSignatureHelpSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/signatureHelp/LSPSignatureHelpSupport.java index 0f7f0ffe4..8370bb33a 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/signatureHelp/LSPSignatureHelpSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/signatureHelp/LSPSignatureHelpSupport.java @@ -59,13 +59,16 @@ protected CompletableFuture doLoad(SignatureHelpParams params, Ca // Get signature help for the first language server LanguageServerItem languageServer = languageServers.get(0); - return getSignatureHelpFor(params, languageServer, cancellationSupport); + return getSignatureHelpFor(params, file, languageServer, cancellationSupport); }); } private static CompletableFuture getSignatureHelpFor(@NotNull SignatureHelpParams params, + @NotNull PsiFile file, @NotNull LanguageServerItem languageServer, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .signatureHelp(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_SIGNATURE_HELP); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java index edc02aa6e..0dd7b1619 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java @@ -20,7 +20,7 @@ import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,10 +44,10 @@ public LSPGoToTypeDefinitionAction() { } @Override - protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { + protected CompletableFuture> getLocations(PsiFile psiFile, Document document, Editor editor, int offset) { LSPTypeDefinitionSupport typeDefinitionSupport = LSPFileSupport.getSupport(psiFile).getTypeDefinitionSupport(); var params = new LSPTypeDefinitionParams(LSPIJUtils.toTextDocumentIdentifier(psiFile.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> typeDefinitionsFuture = typeDefinitionSupport.getTypeDefinitions(params); + CompletableFuture> typeDefinitionsFuture = typeDefinitionSupport.getTypeDefinitions(params); try { waitUntilDone(typeDefinitionsFuture, psiFile); } catch (ProcessCanceledException ex) { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPTypeDefinitionSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPTypeDefinitionSupport.java index e800532da..13ba173a4 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPTypeDefinitionSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPTypeDefinitionSupport.java @@ -17,7 +17,7 @@ import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -30,7 +30,7 @@ *
  • textDocument/typeDefinition
  • * */ -public class LSPTypeDefinitionSupport extends AbstractLSPDocumentFeatureSupport> { +public class LSPTypeDefinitionSupport extends AbstractLSPDocumentFeatureSupport> { private Integer previousOffset; @@ -38,7 +38,7 @@ public LSPTypeDefinitionSupport(@NotNull PsiFile file) { super(file); } - public CompletableFuture> getTypeDefinitions(LSPTypeDefinitionParams params) { + public CompletableFuture> getTypeDefinitions(LSPTypeDefinitionParams params) { int offset = params.getOffset(); if (previousOffset != null && !previousOffset.equals(offset)) { super.cancel(); @@ -48,12 +48,12 @@ public CompletableFuture> getTypeDefinitions(LSPTypeDefinitionPar } @Override - protected CompletableFuture> doLoad(LSPTypeDefinitionParams params, CancellationSupport cancellationSupport) { + protected CompletableFuture> doLoad(LSPTypeDefinitionParams params, CancellationSupport cancellationSupport) { PsiFile file = super.getFile(); return collectTypeDefinitions(file, params, cancellationSupport); } - private static @NotNull CompletableFuture> collectTypeDefinitions(@NotNull PsiFile file, + private static @NotNull CompletableFuture> collectTypeDefinitions(@NotNull PsiFile file, @NotNull LSPTypeDefinitionParams params, @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, @@ -67,9 +67,9 @@ protected CompletableFuture> doLoad(LSPTypeDefinitionParams param } // Collect list of textDocument/typeDefinition future for each language servers - List>> typeDefinitionsPerServerFutures = languageServers + List>> typeDefinitionsPerServerFutures = languageServers .stream() - .map(languageServer -> getTypeDefinitionFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getTypeDefinitionFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/typeDefinition future in one future which return the list of typeDefinition ranges @@ -77,12 +77,15 @@ protected CompletableFuture> doLoad(LSPTypeDefinitionParams param }); } - private static CompletableFuture> getTypeDefinitionFor(LSPTypeDefinitionParams params, - LanguageServerItem languageServer, - CancellationSupport cancellationSupport) { + private static CompletableFuture> getTypeDefinitionFor(@NotNull LSPTypeDefinitionParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .typeDefinition(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_TYPE_DEFINITION) - .thenApplyAsync(LSPIJUtils::getLocations); + .thenApplyAsync(locations -> LSPIJUtils.getLocations(locations, languageServer)); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPPrepareTypeHierarchySupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPPrepareTypeHierarchySupport.java index eb1b04c89..d142ec95d 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPPrepareTypeHierarchySupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPPrepareTypeHierarchySupport.java @@ -61,8 +61,8 @@ protected CompletableFuture> doLoad(TypeHierarchyPre @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, - f -> f.getTypeHierarchyFeature().isEnabled(file), - f -> f.getTypeHierarchyFeature().isSupported(file)) + f -> f.getTypeHierarchyFeature().isEnabled(file), + f -> f.getTypeHierarchyFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have type hierarchy capability @@ -86,6 +86,8 @@ private static CompletableFuture> getTypeHierarchies @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { + // Update textDocument Uri with custom file Uri if needed + updateTextDocumentUri(params.getTextDocument(), file, languageServer); return cancellationSupport.execute(languageServer .getTextDocumentService() .prepareTypeHierarchy(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySubtypesSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySubtypesSupport.java index df7e133c9..0a149c58a 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySubtypesSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySubtypesSupport.java @@ -54,8 +54,8 @@ protected CompletableFuture> doLoad(TypeHierarchySub @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, - f -> f.getTypeHierarchyFeature().isEnabled(file), - f -> f.getTypeHierarchyFeature().isSupported(file)) + f -> f.getTypeHierarchyFeature().isEnabled(file), + f -> f.getTypeHierarchyFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have type hierarchy capability @@ -66,7 +66,7 @@ protected CompletableFuture> doLoad(TypeHierarchySub // Collect list of typeHierarchy/subtypes future for each language servers List>> typeHierarchyPerServerFutures = languageServers .stream() - .map(languageServer -> getTypeHierarchiesFor(params, languageServer, file, cancellationSupport)) + .map(languageServer -> getTypeHierarchiesFor(params, languageServer, cancellationSupport)) .toList(); // Merge list of typeHierarchy/subtypes future in one future which return the list of type hierarchy items @@ -76,7 +76,6 @@ protected CompletableFuture> doLoad(TypeHierarchySub private static CompletableFuture> getTypeHierarchiesFor(@NotNull TypeHierarchySubtypesParams params, @NotNull LanguageServerItem languageServer, - @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer .getTextDocumentService() diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySupertypesSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySupertypesSupport.java index b5c961d1b..317b25cb6 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySupertypesSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchySupertypesSupport.java @@ -50,12 +50,12 @@ protected CompletableFuture> doLoad(TypeHierarchySup } private static @NotNull CompletableFuture> getTypeHierarchySupertypes(@NotNull PsiFile file, - @NotNull TypeHierarchySupertypesParams params, - @NotNull CancellationSupport cancellationSupport) { + @NotNull TypeHierarchySupertypesParams params, + @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, - f -> f.getTypeHierarchyFeature().isEnabled(file), - f -> f.getTypeHierarchyFeature().isSupported(file)) + f -> f.getTypeHierarchyFeature().isEnabled(file), + f -> f.getTypeHierarchyFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have type hierarchy capability @@ -66,7 +66,7 @@ protected CompletableFuture> doLoad(TypeHierarchySup // Collect list of typeHierarchy/supertypes future for each language servers List>> typeHierarchyPerServerFutures = languageServers .stream() - .map(languageServer -> getTypeHierarchiesFor(params, languageServer, file, cancellationSupport)) + .map(languageServer -> getTypeHierarchiesFor(params, languageServer, cancellationSupport)) .toList(); // Merge list of typeHierarchy/supertypes future in one future which return the list of type hierarchy items @@ -76,7 +76,6 @@ protected CompletableFuture> doLoad(TypeHierarchySup private static CompletableFuture> getTypeHierarchiesFor(@NotNull TypeHierarchySupertypesParams params, @NotNull LanguageServerItem languageServer, - @NotNull PsiFile file, @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer .getTextDocumentService() diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchyTreeStructureBase.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchyTreeStructureBase.java index 3b49eaf65..a2c6898f1 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchyTreeStructureBase.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchyTreeStructureBase.java @@ -86,7 +86,7 @@ protected void fillChildren(@NotNull HierarchyNodeDescriptor descriptor, if (items != null) { for (var item : items) { var typeHierarchyItem = item.typeHierarchyItem(); - PsiElement element = createPsiElement(typeHierarchyItem.getUri(), typeHierarchyItem.getRange(), typeHierarchyItem.getName()); + PsiElement element = createPsiElement(typeHierarchyItem.getUri(), typeHierarchyItem.getRange(), typeHierarchyItem.getName(), item.languageServer().getClientFeatures()); if (element != null) { descriptors.add(createHierarchyNodeDescriptor(myProject, descriptor, element, typeHierarchyItem)); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceImplementationsSearch.java b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceImplementationsSearch.java index 7be8ed8b1..dbb8bb16b 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceImplementationsSearch.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceImplementationsSearch.java @@ -29,7 +29,7 @@ import com.redhat.devtools.lsp4ij.features.implementation.LSPImplementationParams; import com.redhat.devtools.lsp4ij.features.implementation.LSPImplementationSupport; import com.redhat.devtools.lsp4ij.ui.LSP4IJUiUtils; -import org.eclipse.lsp4j.Location; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,7 +95,7 @@ public void processQuery(DefinitionsScopedSearch.@NotNull SearchParameters query offset ); LSPImplementationSupport implementationSupport = LSPFileSupport.getSupport(file).getImplementationSupport(); - CompletableFuture> implementationsFuture = implementationSupport.getImplementations(params); + CompletableFuture> implementationsFuture = implementationSupport.getImplementations(params); try { waitUntilDone(implementationsFuture, file); } catch (ProcessCanceledException ex) { @@ -109,14 +109,14 @@ public void processQuery(DefinitionsScopedSearch.@NotNull SearchParameters query } if (isDoneNormally(implementationsFuture)) { - List implementations = implementationsFuture.getNow(null); + List implementations = implementationsFuture.getNow(null); if (ContainerUtil.isEmpty(implementations)) { // No implementations found LSP4IJUiUtils.showErrorHint(file, CodeInsightBundle.message("goto.implementation.notFound")); } else { // textDocument/implementations has been collected correctly - for (Location implementation : implementations) { - if (!consumer.process(LSPPsiElementFactory.toPsiElement(implementation, project))) { + for (LocationData implementation : implementations) { + if (!consumer.process(LSPPsiElementFactory.toPsiElement(implementation.location(), implementation.languageServer().getClientFeatures(), project))) { return; } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceSymbolSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceSymbolSupport.java index 5010d5a09..d1fb7275b 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceSymbolSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceSymbolSupport.java @@ -13,6 +13,7 @@ import com.intellij.openapi.project.Project; import com.redhat.devtools.lsp4ij.LSPRequestConstants; import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.features.AbstractLSPWorkspaceFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; @@ -91,14 +92,14 @@ private static CompletableFuture> getWorkspaceSymbolFo for (var si : s) { if (params.accept(si)) { items.add(new WorkspaceSymbolData( - si.getName(), si.getKind(), si.getLocation(), project)); + si.getName(), si.getKind(), si.getLocation(), languageServer.getClientFeatures(), project)); } } } else if (symbols.isRight()) { List ws = symbols.getRight(); for (var si : ws) { if (params.accept(si)) { - WorkspaceSymbolData item = createItem(si, project); + WorkspaceSymbolData item = createItem(si, languageServer.getClientFeatures(),project); items.add(item); } } @@ -107,14 +108,16 @@ private static CompletableFuture> getWorkspaceSymbolFo }); } - private static WorkspaceSymbolData createItem(WorkspaceSymbol si, Project project) { + private static WorkspaceSymbolData createItem(WorkspaceSymbol si, + FileUriSupport fileUriSupport, + Project project) { String name = si.getName(); SymbolKind symbolKind = si.getKind(); if (si.getLocation().isLeft()) { return new WorkspaceSymbolData( - name, symbolKind, si.getLocation().getLeft(), project); + name, symbolKind, si.getLocation().getLeft(), fileUriSupport, project); } return new WorkspaceSymbolData( - name, symbolKind, si.getLocation().getRight().getUri(), null, project); + name, symbolKind, si.getLocation().getRight().getUri(), null, fileUriSupport, project); } } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceTypeDeclarationProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceTypeDeclarationProvider.java index 2021953ff..06743444c 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceTypeDeclarationProvider.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/LSPWorkspaceTypeDeclarationProvider.java @@ -29,6 +29,7 @@ import com.redhat.devtools.lsp4ij.features.typeDefinition.LSPTypeDefinitionParams; import com.redhat.devtools.lsp4ij.features.typeDefinition.LSPTypeDefinitionSupport; import com.redhat.devtools.lsp4ij.ui.LSP4IJUiUtils; +import com.redhat.devtools.lsp4ij.usages.LocationData; import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -89,7 +90,7 @@ public class LSPWorkspaceTypeDeclarationProvider implements TypeDeclarationPlace LSPTypeDefinitionSupport typeDefinitionSupport = LSPFileSupport.getSupport(file).getTypeDefinitionSupport(); var params = new LSPTypeDefinitionParams(LSPIJUtils.toTextDocumentIdentifier(file.getVirtualFile()), LSPIJUtils.toPosition(offset, document), offset); - CompletableFuture> typeDefinitionsFuture = typeDefinitionSupport.getTypeDefinitions(params); + CompletableFuture> typeDefinitionsFuture = typeDefinitionSupport.getTypeDefinitions(params); try { waitUntilDone(typeDefinitionsFuture, file); } catch (ProcessCanceledException ex) { @@ -103,15 +104,16 @@ public class LSPWorkspaceTypeDeclarationProvider implements TypeDeclarationPlace } if (isDoneNormally(typeDefinitionsFuture)) { - List typeDefinitions = typeDefinitionsFuture.getNow(null); + List typeDefinitions = typeDefinitionsFuture.getNow(null); if (ContainerUtil.isEmpty(typeDefinitions)) { // No type declarations found LSP4IJUiUtils.showErrorHint(file, LanguageServerBundle.message("goto.typeDeclaration.notFound")); } else { // textDocument/typeDefinition has been collected correctly List typeDefinitionElements = new ArrayList<>(typeDefinitions.size()); - for (Location typeDefinition : typeDefinitions) { - ContainerUtil.addIfNotNull(typeDefinitionElements, LSPPsiElementFactory.toPsiElement(typeDefinition, project)); + for (LocationData typeDefinition : typeDefinitions) { + ContainerUtil.addIfNotNull(typeDefinitionElements, + LSPPsiElementFactory.toPsiElement(typeDefinition.location(), typeDefinition.languageServer().getClientFeatures(), project)); } if (!ContainerUtil.isEmpty(typeDefinitionElements)) { return typeDefinitionElements.toArray(PsiElement.EMPTY_ARRAY); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/WorkspaceSymbolData.java b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/WorkspaceSymbolData.java index adf3fa947..a5df27eb9 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/WorkspaceSymbolData.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/workspaceSymbol/WorkspaceSymbolData.java @@ -18,6 +18,7 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.ui.IconMapper; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; @@ -40,6 +41,7 @@ public class WorkspaceSymbolData implements NavigationItem { private final Project project; private final VirtualFile file; private final LSPItemPresentation presentation; + private final FileUriSupport fileUriSupport; private record LSPItemPresentation(String name, SymbolKind symbolKind, String locationString) implements ItemPresentation { @@ -64,16 +66,26 @@ public String name() { } - public WorkspaceSymbolData(String name, SymbolKind symbolKind, Location location, Project project) { - this(name, symbolKind, location.getUri(), location.getRange().getStart(), project); + public WorkspaceSymbolData(String name, + SymbolKind symbolKind, + Location location, + FileUriSupport fileUriSupport, + Project project) { + this(name, symbolKind, location.getUri(), location.getRange().getStart(), fileUriSupport, project); } - public WorkspaceSymbolData(String name, SymbolKind symbolKind, String fileUri, Position position, Project project) { + public WorkspaceSymbolData(String name, + SymbolKind symbolKind, + String fileUri, + Position position, + FileUriSupport fileUriSupport, + Project project) { this.symbolKind = symbolKind; this.fileUri = fileUri; this.position = position; this.project = project; - this.file = LSPIJUtils.findResourceFor(fileUri); + this.file = FileUriSupport.findFileByUri(fileUri, fileUriSupport); + this.fileUriSupport = fileUriSupport; String locationString = file != null ? getLocationString(project, file) : fileUri; this.presentation = new LSPItemPresentation(name, symbolKind, locationString); } @@ -98,7 +110,7 @@ public VirtualFile getFile() { @Override public void navigate(boolean requestFocus) { - LSPIJUtils.openInEditor(fileUri, position, requestFocus, project); + LSPIJUtils.openInEditor(fileUri, position, requestFocus, false, fileUriSupport, project); } @Override diff --git a/src/main/java/com/redhat/devtools/lsp4ij/hint/LSPNavigationLinkHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/hint/LSPNavigationLinkHandler.java index 51921cfd5..40517b624 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/hint/LSPNavigationLinkHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/hint/LSPNavigationLinkHandler.java @@ -41,7 +41,7 @@ public class LSPNavigationLinkHandler extends TooltipLinkHandler { @Override public boolean handleLink(@NotNull String fileUrl, @NotNull Editor editor) { - return LSPIJUtils.openInEditor(fileUrl, null, true, true, editor.getProject()); + return LSPIJUtils.openInEditor(fileUrl, null, true, true, null, editor.getProject()); } /** diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSearcher.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSearcher.java index f37899059..0aae7dd74 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSearcher.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSearcher.java @@ -57,7 +57,7 @@ public void processElementUsages(@NotNull PsiElement element, @NotNull Processor if (elt.getLSPReferences() != null) { elt.getLSPReferences() .forEach(ref -> { - var psiElement = LSPUsagesManager.toPsiElement(ref, LSPUsagePsiElement.UsageKind.references, element.getProject()); + var psiElement = LSPUsagesManager.toPsiElement(ref.location(), ref.languageServer().getClientFeatures(), LSPUsagePsiElement.UsageKind.references, element.getProject()); if (psiElement != null) { processor.process(ReadAction.compute(() -> new UsageInfo2UsageAdapter(new UsageInfo(psiElement)))); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java index 523d52112..efd856945 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java @@ -14,6 +14,8 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LSPRequestConstants; +import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport; import com.redhat.devtools.lsp4ij.internal.CancellationSupport; import com.redhat.devtools.lsp4ij.internal.CompletableFutures; @@ -80,51 +82,57 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara var clientFeature = ls.getClientFeatures(); // Collect declarations if (clientFeature.getDeclarationFeature().isDeclarationSupported(file)) { + updateTextDocumentUri(declarationParams.getTextDocument(), file, ls); allFutures.add( + // Update textDocument Uri with custom file Uri if needed cancellationSupport.execute(ls .getTextDocumentService() .declaration(declarationParams), ls, LSPRequestConstants.TEXT_DOCUMENT_DECLARATION) - .handle(reportUsages(project, LSPUsagePsiElement.UsageKind.declarations)) + .handle(reportUsages(ls, project, LSPUsagePsiElement.UsageKind.declarations)) ); } // Collect definitions if (clientFeature.getDefinitionFeature().isDefinitionSupported(file)) { + updateTextDocumentUri(definitionParams.getTextDocument(), file, ls); allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() .definition(definitionParams), ls, LSPRequestConstants.TEXT_DOCUMENT_DEFINITION) - .handle(reportUsages(project, LSPUsagePsiElement.UsageKind.definitions)) + .handle(reportUsages(ls, project, LSPUsagePsiElement.UsageKind.definitions)) ); } // Collect type definitions if (clientFeature.getTypeDefinitionFeature().isTypeDefinitionSupported(file)) { + updateTextDocumentUri(typeDefinitionParams.getTextDocument(), file, ls); allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() .typeDefinition(typeDefinitionParams), ls, LSPRequestConstants.TEXT_DOCUMENT_TYPE_DEFINITION) - .handle(reportUsages(project, LSPUsagePsiElement.UsageKind.typeDefinitions)) + .handle(reportUsages(ls, project, LSPUsagePsiElement.UsageKind.typeDefinitions)) ); } // Collect references if (clientFeature.getReferencesFeature().isReferencesSupported(file)) { + updateTextDocumentUri(referenceParams.getTextDocument(), file, ls); allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() .references(referenceParams), ls, LSPRequestConstants.TEXT_DOCUMENT_REFERENCES) - .handle(reportUsages2(project, LSPUsagePsiElement.UsageKind.references)) + .handle(reportUsages2(ls, project, LSPUsagePsiElement.UsageKind.references)) ); } // Collect implementation if (clientFeature.getImplementationFeature().isImplementationSupported(file)) { + updateTextDocumentUri(implementationParams.getTextDocument(), file, ls); allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() .implementation(implementationParams), ls, LSPRequestConstants.TEXT_DOCUMENT_IMPLEMENTATION) - .handle(reportUsages(project, LSPUsagePsiElement.UsageKind.implementations)) + .handle(reportUsages(ls, project, LSPUsagePsiElement.UsageKind.implementations)) ); } @@ -136,19 +144,19 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara } private static BiFunction, Throwable, ? extends List> reportUsages2( - Project project, + LanguageServerItem ls, Project project, LSPUsagePsiElement.UsageKind usageKind) { return (locations, error) -> { if (error != null) { return Collections.emptyList(); } - return createUsages(locations, usageKind, project); + return createUsages(locations, ls.getClientFeatures(), usageKind, project); }; } @NotNull private static BiFunction, List>, Throwable, List> reportUsages( - @NotNull Project project, + LanguageServerItem ls, @NotNull Project project, @NotNull LSPUsagePsiElement.UsageKind usageKind) { return (locations, error) -> { if (error != null) { @@ -157,11 +165,12 @@ private static BiFunction, List createUsages(@Nullable List locations, + @Nullable FileUriSupport fileUriSupport, @NotNull LSPUsagePsiElement.UsageKind usageKind, @NotNull Project project) { if (locations == null || locations.isEmpty()) { @@ -169,24 +178,26 @@ private static List createUsages(@Nullable List LSPUsagesManager.toPsiElement(location, usageKind, project)) + .map(location -> LSPUsagesManager.toPsiElement(location, fileUriSupport, usageKind, project)) .filter(Objects::nonNull) .toList(); } private static List createUsages(@Nullable Either, List> locations, + @Nullable FileUriSupport fileUriSupport, @Nullable LSPUsagePsiElement.UsageKind usageKind, @Nullable Project project) { if (locations == null) { return Collections.emptyList(); } if (locations.isLeft()) { - return createUsages(locations.getLeft(), usageKind, project); + return createUsages(locations.getLeft(), fileUriSupport, usageKind, project); } - return createUsagesFromLocationLinks(locations.getRight(), usageKind, project); + return createUsagesFromLocationLinks(locations.getRight(), fileUriSupport, usageKind, project); } private static List createUsagesFromLocationLinks(@Nullable List locations, + @Nullable FileUriSupport fileUriSupport, @NotNull LSPUsagePsiElement.UsageKind usageKind, @NotNull Project project) { if (locations == null || locations.isEmpty()) { @@ -194,7 +205,7 @@ private static List createUsagesFromLocationLinks(@Nullable } return locations .stream() - .map(location -> LSPUsagesManager.toPsiElement(location, usageKind, project)) + .map(location -> LSPUsagesManager.toPsiElement(location, fileUriSupport, usageKind, project)) .filter(Objects::nonNull) .toList(); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageTriggeredPsiElement.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageTriggeredPsiElement.java index 9261f7e4c..affbbffbf 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageTriggeredPsiElement.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageTriggeredPsiElement.java @@ -13,7 +13,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.features.LSPPsiElement; -import org.eclipse.lsp4j.Location; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,18 +25,18 @@ */ public class LSPUsageTriggeredPsiElement extends LSPPsiElement { - private List references; + private List references; public LSPUsageTriggeredPsiElement(@NotNull PsiFile file, @NotNull TextRange textRange) { super(file, textRange); } @Nullable - public List getLSPReferences() { + public List getLSPReferences() { return references; } - public void setLSPReferences(List references) { + public void setLSPReferences(List references) { this.references = references; } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsagesManager.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsagesManager.java index 671118e55..7061e3dbf 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsagesManager.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsagesManager.java @@ -22,6 +22,7 @@ import com.intellij.ui.awt.RelativePoint; import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServerBundle; +import com.redhat.devtools.lsp4ij.client.features.FileUriSupport; import com.redhat.devtools.lsp4ij.features.LSPPsiElementFactory; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.LocationLink; @@ -54,7 +55,7 @@ private LSPUsagesManager(@NotNull Project project) { // Show references, implementation, etc in Popup - public void findShowUsagesInPopup(@NotNull List locations, + public void findShowUsagesInPopup(@NotNull List locations, @NotNull LSPUsageType usageType, @NotNull DataContext dataContext, @Nullable MouseEvent event) { @@ -65,28 +66,29 @@ public void findShowUsagesInPopup(@NotNull List locations, } case 1: { // On response, open editor with the given location - Location ref = locations.get(0); - openLocation(ref, project); + LocationData ref = locations.get(0); + openLocation(ref.location(), ref.languageServer().getClientFeatures(), project); break; } default: { // Open locations in a Popup @Nullable Editor editor = dataContext.getData(CommonDataKeys.EDITOR); - Location ref = locations.get(0); - LSPUsageTriggeredPsiElement element = toUsageTriggeredPsiElement(ref, project); - if(element != null) { + LocationData ref = locations.get(0); + LSPUsageTriggeredPsiElement element = toUsageTriggeredPsiElement(ref.location(), ref.languageServer().getClientFeatures(), project); + if (element != null) { element.setLSPReferences(locations); - GotoDeclarationAction.startFindUsages(editor, element.getProject(), element, event == null ? null : new RelativePoint(event)); } + GotoDeclarationAction.startFindUsages(editor, element.getProject(), element, event == null ? null : new RelativePoint(event)); } } } @Nullable public static LSPUsagePsiElement toPsiElement(@NotNull Location location, + @Nullable FileUriSupport fileUriSupport, @NotNull LSPUsagePsiElement.UsageKind kind, @NotNull Project project) { - LSPUsagePsiElement element = LSPPsiElementFactory.toPsiElement(location, project, USAGE_ELEMENT_FACTORY); + LSPUsagePsiElement element = LSPPsiElementFactory.toPsiElement(location, fileUriSupport, project, USAGE_ELEMENT_FACTORY); if (element != null) { element.setKind(kind); } @@ -95,9 +97,10 @@ public static LSPUsagePsiElement toPsiElement(@NotNull Location location, @Nullable public static LSPUsagePsiElement toPsiElement(@NotNull LocationLink location, + @Nullable FileUriSupport fileUriSupport, @NotNull LSPUsagePsiElement.UsageKind kind, @NotNull Project project) { - LSPUsagePsiElement element = LSPPsiElementFactory.toPsiElement(location, project, USAGE_ELEMENT_FACTORY); + LSPUsagePsiElement element = LSPPsiElementFactory.toPsiElement(location, fileUriSupport, project, USAGE_ELEMENT_FACTORY); if (element != null) { element.setKind(kind); } @@ -106,8 +109,9 @@ public static LSPUsagePsiElement toPsiElement(@NotNull LocationLink location, @Nullable public static LSPUsageTriggeredPsiElement toUsageTriggeredPsiElement(@NotNull Location location, + @Nullable FileUriSupport fileUriSupport, @NotNull Project project) { - return LSPPsiElementFactory.toPsiElement(location, project, USAGE_TRIGGERED_ELEMENT_FACTORY); + return LSPPsiElementFactory.toPsiElement(location, fileUriSupport, project, USAGE_TRIGGERED_ELEMENT_FACTORY); } private void showNoUsage(@NotNull LSPUsageType usageType, DataContext dataContext) { @@ -139,7 +143,9 @@ private String getNoUsageMessageKey(LSPUsageType usageType) { return null; } - private static void openLocation(@NotNull Location location, @NotNull Project project) { - ApplicationManager.getApplication().invokeLater(() -> LSPIJUtils.openInEditor(location, project)); + private static void openLocation(@NotNull Location location, + @Nullable FileUriSupport fileUriSupport, + @NotNull Project project) { + ApplicationManager.getApplication().invokeLater(() -> LSPIJUtils.openInEditor(location, fileUriSupport, project)); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LocationData.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LocationData.java new file mode 100644 index 000000000..3647e98a8 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LocationData.java @@ -0,0 +1,7 @@ +package com.redhat.devtools.lsp4ij.usages; + +import com.redhat.devtools.lsp4ij.LanguageServerItem; +import org.eclipse.lsp4j.Location; + +public record LocationData(Location location, LanguageServerItem languageServer) { +}