From f07ef8c6fd907fc5598df64dda6656ecaa701e37 Mon Sep 17 00:00:00 2001 From: azerr Date: Wed, 18 Oct 2023 11:34:18 +0200 Subject: [PATCH] fix: Argument for @NotNull parameter 'file' of com/intellij/openapi/vfs/VfsUtilCore.virtualToIoFile must not be null Fixes #1228 Signed-off-by: azerr --- .../devtools/intellij/lsp4ij/LSPIJUtils.java | 53 +++++++++++----- .../completion/LSPCompletionContributor.java | 15 +++-- .../LSPDocumentLinkAnnotator.java | 10 +-- .../LSPHighlightUsagesHandlerFactory.java | 11 ++-- .../navigation/LSPGotoDeclarationHandler.java | 62 ++++++++++--------- 5 files changed, 91 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java index 4ef821d58..de1788c25 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LSPIJUtils.java @@ -87,10 +87,10 @@ public static Language getFileLanguage(@Nonnull VirtualFile file, Project projec } private static T toTextDocumentPositionParamsCommon(T param, int offset, Document document) { - URI uri = toUri(document); Position start = toPosition(offset, document); param.setPosition(start); TextDocumentIdentifier id = new TextDocumentIdentifier(); + URI uri = toUri(document); if (uri != null) { id.setUri(uri.toASCIIString()); } @@ -106,7 +106,19 @@ public static HoverParams toHoverParams(int offset, Document document) { return toTextDocumentPositionParamsCommon(new HoverParams(), offset, document); } - public static URI toUri(File file) { + + /** + * Returns the Uri of the virtual file corresponding to the specified document. + * + * @param document the document for which the virtual file is requested. + * @return the Uri of the file, or null if the document wasn't created from a virtual file. + */ + public static @Nullable URI toUri(@NotNull Document document) { + VirtualFile file = getFile(document); + return file != null ? toUri(file) : null; + } + + public static @NotNull URI toUri(@NotNull File file) { // URI scheme specified by language server protocol and LSP try { return new URI("file", "", file.getAbsoluteFile().toURI().getPath(), null); //$NON-NLS-1$ //$NON-NLS-2$ @@ -116,19 +128,21 @@ public static URI toUri(File file) { } } - public static URI toUri(PsiFile file) { - return toUri(file.getVirtualFile()); + public static @Nullable URI toUri(@NotNull PsiFile psiFile) { + VirtualFile file = getFile(psiFile); + return file != null ? toUri(file) : null; } - public static URI toUri(VirtualFile file) { + public static @NotNull URI toUri(@NotNull VirtualFile file) { return toUri(VfsUtilCore.virtualToIoFile(file)); } - public static String toUriAsString(PsiFile file) { - return toUriAsString(file.getVirtualFile()); + public static @Nullable String toUriAsString(@NotNull PsiFile psFile) { + VirtualFile file = psFile.getVirtualFile(); + return file != null ? toUriAsString(file) : null; } - public static String toUriAsString(VirtualFile file) { + public static @NotNull String toUriAsString(@NotNull VirtualFile file) { String protocol = file.getFileSystem() != null ? file.getFileSystem().getProtocol() : null; if (JAR_PROTOCOL.equals(protocol) || JRT_PROTOCOL.equals(protocol)) { return VfsUtilCore.convertToURL(file.getUrl()).toExternalForm(); @@ -136,15 +150,24 @@ public static String toUriAsString(VirtualFile file) { return toUri(VfsUtilCore.virtualToIoFile(file)).toASCIIString(); } - public static URI toUri(Document document) { - VirtualFile file = getFile(document); - return file != null ? toUri(file) : null; - } - - public static @Nullable VirtualFile getFile(Document document) { - return FileDocumentManager.getInstance().getFile(document); + /** + * Returns the virtual file corresponding to the specified document. + * + * @param document the document for which the virtual file is requested. + * @return the file, or null if the document wasn't created from a virtual file. + */ + public static @Nullable VirtualFile getFile(@NotNull Document document) { + if (ApplicationManager.getApplication().isReadAccessAllowed()) { + return FileDocumentManager.getInstance().getFile(document); + } + return ReadAction.compute(() -> FileDocumentManager.getInstance().getFile(document)); } + /** + * Returns the virtual file corresponding to the PSI file. + * + * @return the virtual file, or {@code null} if the file exists only in memory. + */ public static @Nullable VirtualFile getFile(@NotNull PsiElement element) { PsiFile psFile = element.getContainingFile(); return psFile != null ? psFile.getVirtualFile() : null; diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java index 5f92fd671..b462ad97a 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/completion/LSPCompletionContributor.java @@ -50,18 +50,23 @@ public class LSPCompletionContributor extends CompletionContributor { @Override public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { - Document document = parameters.getEditor().getDocument(); - Editor editor = parameters.getEditor(); PsiFile psiFile = parameters.getOriginalFile(); + VirtualFile file = psiFile.getVirtualFile(); + if (file == null) { + return; + } + + Editor editor = parameters.getEditor(); + Document document = editor.getDocument(); Project project = psiFile.getProject(); int offset = parameters.getOffset(); - VirtualFile file = psiFile.getVirtualFile(); URI uri = LSPIJUtils.toUri(file); + ProgressManager.checkCanceled(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(project, file); + CompletableFuture> completionLanguageServersFuture = initiateLanguageServers(file, project); cancellationSupport.execute(completionLanguageServersFuture); ProgressManager.checkCanceled(); @@ -171,7 +176,7 @@ private static LookupElement createErrorProposal(int offset, Exception ex) { return LookupElementBuilder.create("Error while computing completion", ""); } - private static CompletableFuture> initiateLanguageServers(Project project, VirtualFile file) { + private static CompletableFuture> initiateLanguageServers(@NotNull VirtualFile file, @NotNull Project project) { return LanguageServiceAccessor.getInstance(project).getLanguageServers(file, capabilities -> { CompletionOptions provider = capabilities.getCompletionProvider(); diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java index 3f0bd3df9..1fdfe5553 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/documentLink/LSPDocumentLinkAnnotator.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; @@ -55,10 +56,6 @@ public class LSPDocumentLinkAnnotator extends ExternalAnnotator collectInformation(@NotNull PsiFile psiFile, @NotNull Editor editor, boolean hasErrors) { - URI uri = LSPIJUtils.toUri(editor.getDocument()); - if (uri == null) { - return null; - } Document document = editor.getDocument(); VirtualFile file = LSPIJUtils.getFile(document); if (file == null) { @@ -68,10 +65,13 @@ public List collectInformation(@NotNull PsiFile psiFile, @No final CancellationSupport cancellationSupport = new CancellationSupport(); try { ProgressManager.checkCanceled(); + + URI uri = LSPIJUtils.toUri(file); DocumentLinkParams params = new DocumentLinkParams(LSPIJUtils.toTextDocumentIdentifier(uri)); + Project project = editor.getProject(); BlockingDeque, LanguageServerWrapper>> documentLinks = new LinkedBlockingDeque<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()).getLanguageServers(file, + CompletableFuture future = LanguageServiceAccessor.getInstance(project).getLanguageServers(file, capabilities -> capabilities.getDocumentLinkProvider() != null) .thenAcceptAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java index 13a46f75a..db7666550 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/highlight/LSPHighlightUsagesHandlerFactory.java @@ -55,21 +55,22 @@ private List getTargets(Editor editor, PsiFile psiFile) if (file == null) { return Collections.emptyList(); } - URI uri = LSPIJUtils.toUri(file); List elements = new ArrayList<>(); final CancellationSupport cancellationSupport = new CancellationSupport(); try { - int offset = TargetElementUtil.adjustOffset(psiFile, editor.getDocument(), editor.getCaretModel().getOffset()); Document document = editor.getDocument(); + int offset = TargetElementUtil.adjustOffset(psiFile, document, editor.getCaretModel().getOffset()); Position position = LSPIJUtils.toPosition(offset, document); ProgressManager.checkCanceled(); - TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri.toString()); + + String uri = LSPIJUtils.toUriAsString(file); + TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri); DocumentHighlightParams params = new DocumentHighlightParams(identifier, position); BlockingDeque highlights = new LinkedBlockingDeque<>(); CompletableFuture future = LanguageServiceAccessor.getInstance(editor.getProject()) - .getLanguageServers(psiFile.getVirtualFile(), capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) + .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDocumentHighlightProvider())) .thenAcceptAsync(languageServers -> cancellationSupport.execute(CompletableFuture.allOf(languageServers.stream() .map(languageServer -> cancellationSupport.execute(languageServer.getServer().getTextDocumentService().documentHighlight(params))) @@ -97,4 +98,4 @@ private List getTargets(Editor editor, PsiFile psiFile) return elements; } -} +} \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java index 4d1c40614..a4ef8cfd0 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/navigation/LSPGotoDeclarationHandler.java @@ -51,39 +51,41 @@ public class LSPGotoDeclarationHandler implements GotoDeclarationHandler { @Nullable @Override public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) { - URI uri = LSPIJUtils.toUri(editor.getDocument()); - if (uri != null) { - VirtualFile file = LSPIJUtils.getFile(sourceElement); - DefinitionParams params = new DefinitionParams(LSPIJUtils.toTextDocumentIdentifier(uri), LSPIJUtils.toPosition(offset, editor.getDocument())); - Set targets = new HashSet<>(); - final CancellationSupport cancellationSupport = new CancellationSupport(); - try { - LanguageServiceAccessor.getInstance(editor.getProject()) - .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())) - .thenComposeAsync(languageServers -> - cancellationSupport.execute( - CompletableFuture.allOf( - languageServers - .stream() - .map(server -> - cancellationSupport.execute(server.getServer().getTextDocumentService().definition(params)) - .thenAcceptAsync(definitions -> targets.addAll(toElements(editor.getProject(), definitions)))) - .toArray(CompletableFuture[]::new)))) - .get(1_000, TimeUnit.MILLISECONDS); - } catch (ResponseErrorException | ExecutionException | CancellationException e) { - // do not report error if the server has cancelled the request - if (!CancellationUtil.isRequestCancelledException(e)) { - LOGGER.warn(e.getLocalizedMessage(), e); - } - } catch (TimeoutException e) { - LOGGER.warn(e.getLocalizedMessage(), e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + VirtualFile file = LSPIJUtils.getFile(sourceElement); + if (file == null) { + return PsiElement.EMPTY_ARRAY; + } + URI uri = LSPIJUtils.toUri(file); + Document document = editor.getDocument(); + DefinitionParams params = new DefinitionParams(LSPIJUtils.toTextDocumentIdentifier(uri), LSPIJUtils.toPosition(offset, document)); + Set targets = new HashSet<>(); + final CancellationSupport cancellationSupport = new CancellationSupport(); + try { + Project project = editor.getProject(); + LanguageServiceAccessor.getInstance(project) + .getLanguageServers(file, capabilities -> LSPIJUtils.hasCapability(capabilities.getDefinitionProvider())) + .thenComposeAsync(languageServers -> + cancellationSupport.execute( + CompletableFuture.allOf( + languageServers + .stream() + .map(server -> + cancellationSupport.execute(server.getServer().getTextDocumentService().definition(params)) + .thenAcceptAsync(definitions -> targets.addAll(toElements(project, definitions)))) + .toArray(CompletableFuture[]::new)))) + .get(1_000, TimeUnit.MILLISECONDS); + } catch (ResponseErrorException | ExecutionException | CancellationException e) { + // do not report error if the server has cancelled the request + if (!CancellationUtil.isRequestCancelledException(e)) { LOGGER.warn(e.getLocalizedMessage(), e); } - return targets.toArray(new PsiElement[targets.size()]); + } catch (TimeoutException e) { + LOGGER.warn(e.getLocalizedMessage(), e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.warn(e.getLocalizedMessage(), e); } - return new PsiElement[0]; + return targets.toArray(new PsiElement[targets.size()]); } private List toElements(Project project, Either, List> definitions) {