diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java index a3d21d5b5..f27f605b8 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/operations/inlayhint/LSPInlayHintInlayProvider.java @@ -63,43 +63,59 @@ public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile, return new FactoryInlayHintsCollector(editor) { @Override public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) { + Document document = editor.getDocument(); try { - URI docURI = LSPIJUtils.toUri(editor.getDocument()); - if (docURI != null) { - Range viewPortRange = getViewPortRange(editor); - InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(docURI.toString()), viewPortRange); - BlockingDeque> pairs = new LinkedBlockingDeque<>(); - List>> inlayhints = new ArrayList<>(); - CompletableFuture future = LanguageServiceAccessor.getInstance(psiElement.getProject()) - .getLanguageServers(editor.getDocument(), capabilities -> capabilities.getInlayHintProvider() != null) - .thenComposeAsync(languageServers -> CompletableFuture.allOf(languageServers.stream() - .map(languageServer -> languageServer.getSecond().getTextDocumentService().inlayHint(param) - .thenAcceptAsync(inlayHints -> { - // textDocument/codeLens may return null - if (inlayHints != null) { - inlayHints.stream().filter(Objects::nonNull) - .forEach(inlayHint -> pairs.add(new Pair(inlayHint, languageServer.getSecond()))); - } - })) - .toArray(CompletableFuture[]::new))); - while (!future.isDone() || !pairs.isEmpty()) { - ProgressManager.checkCanceled(); - Pair pair = pairs.poll(25, TimeUnit.MILLISECONDS); - if (pair != null) { - int offset = LSPIJUtils.toOffset(pair.getFirst().getPosition(), editor.getDocument()); - inlayhints.add(Pair.create(offset, pair)); - } - } - Map>>> elements = inlayhints.stream().collect(Collectors.groupingBy(p -> p.first)); - elements.forEach((offset, list) -> inlayHintsSink.addInlineElement(offset, false, - toPresentation(editor, offset, list, getFactory()), false)); + URI docURI = LSPIJUtils.toUri(document); + if (docURI == null) { + return false; } + Range viewPortRange = getViewPortRange(editor); + InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(docURI.toString()), viewPortRange); + BlockingDeque> pairs = new LinkedBlockingDeque<>(); + CompletableFuture future = collect(psiElement.getProject(), document, param, pairs); + List>> inlayHints = createInlayHints(document, pairs, future); + Map>>> elements = inlayHints.stream().collect(Collectors.groupingBy(p -> p.first)); + elements.forEach((offset, list) -> + inlayHintsSink.addInlineElement(offset, false, toPresentation(editor, list, getFactory()), false)); } catch (InterruptedException e) { LOGGER.warn(e.getLocalizedMessage(), e); Thread.currentThread().interrupt(); } return false; } + + @NotNull + private List>> createInlayHints( + @NotNull Document document, + BlockingDeque> pairs, + CompletableFuture future) + throws InterruptedException { + List>> inlayHints = new ArrayList<>(); + while (!future.isDone() || !pairs.isEmpty()) { + ProgressManager.checkCanceled(); + Pair pair = pairs.poll(25, TimeUnit.MILLISECONDS); + if (pair != null) { + int offset = LSPIJUtils.toOffset(pair.getFirst().getPosition(), document); + inlayHints.add(Pair.create(offset, pair)); + } + } + return inlayHints; + } + + private CompletableFuture collect(@NotNull Project project, @NotNull Document document, InlayHintParams param, BlockingDeque> pairs) { + return LanguageServiceAccessor.getInstance(project) + .getLanguageServers(document, capabilities -> capabilities.getInlayHintProvider() != null) + .thenComposeAsync(languageServers -> CompletableFuture.allOf(languageServers.stream() + .map(languageServer -> languageServer.getSecond().getTextDocumentService().inlayHint(param) + .thenAcceptAsync(inlayHints -> { + // textDocument/codeLens may return null + if (inlayHints != null) { + inlayHints.stream().filter(Objects::nonNull) + .forEach(inlayHint -> pairs.add(new Pair(inlayHint, languageServer.getSecond()))); + } + })) + .toArray(CompletableFuture[]::new))); + } }; } @@ -111,7 +127,7 @@ private static Range getViewPortRange(Editor editor) { return new Range(start, end); } - private InlayPresentation toPresentation(Editor editor, int offset, + private InlayPresentation toPresentation(Editor editor, List>> elements, PresentationFactory factory) { List presentations = new ArrayList<>(); @@ -122,17 +138,7 @@ private InlayPresentation toPresentation(Editor editor, int offset, } else { int index = 0; for (InlayHintLabelPart part : label.getRight()) { - InlayPresentation text = factory.smallText(part.getValue()); - if (!hasCommand(part)) { - // No command, create a simple text inlay hint - presentations.add(text); - } else { - // InlayHintLabelPart defines a Command, create a clickable inlay hint - int finalIndex = index; - text = factory.referenceOnHover(text, (event, translated) -> { - executeClientCommand(p.second.second, p.second.first, finalIndex, (Component) event.getSource(), editor.getProject()); - }); - } + InlayPresentation text = createInlayPresentation(editor.getProject(), factory, presentations, p, index, part); if (part.getTooltip() != null && part.getTooltip().isLeft()) { text = factory.withTooltip(part.getTooltip().getLeft(), text); } @@ -144,6 +150,22 @@ private InlayPresentation toPresentation(Editor editor, int offset, return factory.roundWithBackground(new SequencePresentation(presentations)); } + @NotNull + private InlayPresentation createInlayPresentation(Project project, PresentationFactory factory, List presentations, Pair> p, int index, InlayHintLabelPart part) { + InlayPresentation text = factory.smallText(part.getValue()); + if (!hasCommand(part)) { + // No command, create a simple text inlay hint + presentations.add(text); + } else { + // InlayHintLabelPart defines a Command, create a clickable inlay hint + int finalIndex = index; + text = factory.referenceOnHover(text, (event, translated) -> { + executeClientCommand(p.second.second, p.second.first, finalIndex, (Component) event.getSource(), project); + }); + } + return text; + } + private static boolean hasCommand(InlayHintLabelPart part) { Command command = part.getCommand(); return (command != null && command.getCommand() != null && !command.getCommand().isEmpty());