Skip to content

Commit

Permalink
fix: CodeLens are not displayed in IDEA EAP 2023-3 EAP
Browse files Browse the repository at this point in the history
Fixes #1204

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Oct 8, 2023
1 parent 5cc54ca commit ec2b71d
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@
******************************************************************************/
package com.redhat.devtools.intellij.lsp4ij;

import com.intellij.codeInsight.hints.ChangeListener;
import com.intellij.codeInsight.hints.ImmediateConfigurable;
import com.intellij.codeInsight.hints.InlayHintsProvider;
import com.intellij.codeInsight.hints.NoSettings;
import com.intellij.codeInsight.hints.SettingsKey;
import com.intellij.codeInsight.hints.*;
import com.intellij.ide.DataManager;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.ActionManager;
Expand All @@ -24,6 +20,12 @@
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.layout.LCFlags;
import com.intellij.ui.layout.LayoutKt;
import com.redhat.devtools.intellij.lsp4ij.commands.CommandExecutor;
Expand All @@ -36,6 +38,12 @@

public abstract class AbstractLSPInlayProvider implements InlayHintsProvider<NoSettings> {

private final Key<InlayHintsSink> sinkKey;

protected AbstractLSPInlayProvider(Key<InlayHintsSink> sinkKey) {
this.sinkKey = sinkKey;
}

private SettingsKey<NoSettings> key = new SettingsKey<>("LSP.hints");

@Override
Expand Down Expand Up @@ -90,11 +98,39 @@ protected void executeClientCommand(Component source, Command command) {
if (command != null) {
AnAction action = ActionManager.getInstance().getAction(command.getCommand());
if (action != null) {
DataContext context = SimpleDataContext.getSimpleContext(CommandExecutor.LSP_COMMAND, command, DataManager.getInstance().getDataContext(source));
DataContext context = SimpleDataContext.getSimpleContext(CommandExecutor.LSP_COMMAND, command, DataManager.getInstance().getDataContext(source));
action.actionPerformed(new AnActionEvent(null, context,
ActionPlaces.UNKNOWN, new Presentation(),
ActionManager.getInstance(), 0));
}
}
}

/**
* Returns the virtual file where inlay hint must be added and null otherwise.
*
* @param psiFile the psi file.
* @param editor the editor.
* @param inlayHintsSink the inlay hints sink.
* @return the virtual file where inlay hint must be added and null otherwise.
*/
protected @Nullable VirtualFile getFile(@NotNull PsiFile psiFile, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) {
Project project = psiFile.getProject();
if (project.isDisposed()) {
// The project has been closed, don't collect inlay hints.
return null;
}
// Before IJ 2023-3, FactoryInlayHintsCollector#collect(PsiElement element.. is called once time with PsiFile as element.
// Since IJ 2023-3, FactoryInlayHintsCollector#collect(PsiElement element.. is called several times for each tokens of the PsiFile
// which causes the problem of codelens/inlay hint which are not displayed because there are too many call of LSP request codelens/inlayhint which are cancelled.
// With IJ 2023-3 we need to collect LSP CodeLens/InlayHint just for the first call. To implement this idea, we store the instance InlayHintsSink,
// and we forbid the compute of inlay hint if InlayHintsSink is already filled.
InlayHintsSink sink = editor.getUserData(sinkKey);
if (sink == inlayHintsSink) {
// LSP CodeLens/InlayHint has already be done for teh file, ignore it.
return null;
}
editor.putUserData(sinkKey, inlayHintsSink);
return LSPIJUtils.getFile(psiFile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
Expand All @@ -40,11 +41,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.Component;
import java.awt.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.CompletableFuture;
Expand All @@ -58,6 +58,12 @@
public class LSPCodelensInlayProvider extends AbstractLSPInlayProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(LSPCodelensInlayProvider.class);

private static final Key<InlayHintsSink> SINK_KEY = new Key<>(LSPCodelensInlayProvider.class.getName());

public LSPCodelensInlayProvider() {
super(SINK_KEY);
}

@Nullable
@Override
public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile,
Expand All @@ -67,38 +73,36 @@ public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile,
return new FactoryInlayHintsCollector(editor) {
@Override
public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) {
Project project = psiElement.getProject();
if (project.isDisposed()) {
// The project has been closed, don't collect code lenses.
VirtualFile file = getFile(psiFile, editor, inlayHintsSink);
if (file == null) {
// Codelens must not be collected
return false;
}
Document document = editor.getDocument();
final CancellationSupport cancellationSupport = new CancellationSupport();
try {
VirtualFile file = LSPIJUtils.getFile(psiElement);
URI docURI = LSPIJUtils.toUri(file);
if (docURI != null) {
CodeLensParams param = new CodeLensParams(new TextDocumentIdentifier(docURI.toString()));
BlockingDeque<Pair<CodeLens, LanguageServer>> pairs = new LinkedBlockingDeque<>();
URI fileUri = LSPIJUtils.toUri(file);
CodeLensParams param = new CodeLensParams(new TextDocumentIdentifier(fileUri.toASCIIString()));
BlockingDeque<Pair<CodeLens, LanguageServer>> pairs = new LinkedBlockingDeque<>();

CompletableFuture<Void> future = collect(file, project, param, pairs, cancellationSupport);
List<Pair<Integer, Pair<CodeLens, LanguageServer>>> codeLenses = createCodeLenses(document, pairs, future, cancellationSupport);
codeLenses.stream()
.collect(Collectors.groupingBy(p -> p.first))
.forEach((offset, list) ->
inlayHintsSink.addBlockElement(
offset,
true,
true,
0,
toPresentation(editor, offset, list, getFactory()))
);
}
CompletableFuture<Void> future = collect(file, psiFile.getProject(), param, pairs, cancellationSupport);
List<Pair<Integer, Pair<CodeLens, LanguageServer>>> codeLenses = createCodeLenses(document, pairs, future, cancellationSupport);
codeLenses.stream()
.collect(Collectors.groupingBy(p -> p.first))
.forEach((offset, list) ->
inlayHintsSink.addBlockElement(
offset,
true,
true,
0,
toPresentation(editor, offset, list, getFactory()))
);
} catch (ProcessCanceledException e) {
// Cancel all LSP requests
cancellationSupport.cancel();
throw e;
} catch (InterruptedException e) {
// Cancel all LSP requests
cancellationSupport.cancel();
LOGGER.warn(e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
}
Expand All @@ -109,22 +113,17 @@ public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @
private List<Pair<Integer, Pair<CodeLens, LanguageServer>>> createCodeLenses(Document document, BlockingDeque<Pair<CodeLens, LanguageServer>> pairs, CompletableFuture<Void> future, CancellationSupport cancellationSupport) throws InterruptedException {
List<Pair<Integer, Pair<CodeLens, LanguageServer>>> codelenses = new ArrayList<>();
while (!future.isDone() || !pairs.isEmpty()) {
try {
ProgressManager.checkCanceled();
Pair<CodeLens, LanguageServer> pair = pairs.poll(25, TimeUnit.MILLISECONDS);
if (pair != null) {
int offset = LSPIJUtils.toOffset(pair.getFirst().getRange().getStart(), document);
codelenses.add(Pair.create(offset, pair));
}
} catch (ProcessCanceledException e) {
cancellationSupport.cancel();
throw e;
ProgressManager.checkCanceled();
Pair<CodeLens, LanguageServer> pair = pairs.poll(25, TimeUnit.MILLISECONDS);
if (pair != null) {
int offset = LSPIJUtils.toOffset(pair.getFirst().getRange().getStart(), document);
codelenses.add(Pair.create(offset, pair));
}
}
return codelenses;
}

private CompletableFuture<Void> collect(VirtualFile file, Project project, CodeLensParams param, BlockingDeque<Pair<CodeLens, LanguageServer>> pairs, CancellationSupport cancellationSupport) {
private @NotNull CompletableFuture<Void> collect(@NotNull VirtualFile file, @NotNull Project project, @NotNull CodeLensParams param, @NotNull BlockingDeque<Pair<CodeLens, LanguageServer>> pairs, @NotNull CancellationSupport cancellationSupport) {
return LanguageServiceAccessor.getInstance(project)
.getLanguageServers(file, capabilities -> capabilities.getCodeLensProvider() != null)
.thenComposeAsync(languageServers ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
Expand Down Expand Up @@ -55,6 +56,12 @@
public class LSPInlayHintInlayProvider extends AbstractLSPInlayProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(LSPInlayHintInlayProvider.class);

private static final Key<InlayHintsSink> SINK_KEY = new Key<>(LSPInlayHintInlayProvider.class.getName());

public LSPInlayHintInlayProvider() {
super(SINK_KEY);
}

@Nullable
@Override
public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile,
Expand All @@ -64,21 +71,17 @@ public InlayHintsCollector getCollectorFor(@NotNull PsiFile psiFile,
return new FactoryInlayHintsCollector(editor) {
@Override
public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @NotNull InlayHintsSink inlayHintsSink) {
Project project = psiElement.getProject();
if (project.isDisposed()) {
// The project has been closed, don't collect inlay hints.
VirtualFile file = getFile(psiFile, editor, inlayHintsSink);
if (file == null) {
// InlayHint must not be collected
return false;
}
Document document = editor.getDocument();
final CancellationSupport cancellationSupport = new CancellationSupport();
try {
VirtualFile file = LSPIJUtils.getFile(psiFile);
if (file == null) {
return false;
}
URI docURI = LSPIJUtils.toUri(file);
URI fileUri = LSPIJUtils.toUri(file);
Range viewPortRange = getViewPortRange(editor);
InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(docURI.toString()), viewPortRange);
InlayHintParams param = new InlayHintParams(new TextDocumentIdentifier(fileUri.toASCIIString()), viewPortRange);
BlockingDeque<Pair<InlayHint, LanguageServer>> pairs = new LinkedBlockingDeque<>();

CompletableFuture<Void> future = collect(psiElement.getProject(), file, param, pairs, cancellationSupport);
Expand All @@ -90,8 +93,9 @@ public boolean collect(@NotNull PsiElement psiElement, @NotNull Editor editor, @
} catch (ProcessCanceledException e) {
// Cancel all LSP requests
cancellationSupport.cancel();
throw e;
} catch (InterruptedException e) {
// Cancel all LSP requests
cancellationSupport.cancel();
LOGGER.warn(e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
}
Expand Down

0 comments on commit ec2b71d

Please sign in to comment.