Skip to content

Commit

Permalink
perf: Unreasonnable amount of textDocument/definition requests sent on
Browse files Browse the repository at this point in the history
Ctrl/Cmd

Fixes #20

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Sep 30, 2024
1 parent bb808da commit 5da7f64
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 80 deletions.
13 changes: 13 additions & 0 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPFileSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.redhat.devtools.lsp4ij.features.highlight.LSPHighlightSupport;
import com.redhat.devtools.lsp4ij.features.implementation.LSPImplementationSupport;
import com.redhat.devtools.lsp4ij.features.inlayhint.LSPInlayHintsSupport;
import com.redhat.devtools.lsp4ij.features.navigation.LSPDefinitionSupport;
import com.redhat.devtools.lsp4ij.features.references.LSPReferenceSupport;
import com.redhat.devtools.lsp4ij.features.rename.LSPPrepareRenameSupport;
import com.redhat.devtools.lsp4ij.features.rename.LSPRenameSupport;
Expand Down Expand Up @@ -76,6 +77,8 @@ public class LSPFileSupport extends UserDataHolderBase implements Disposable {

private final LSPReferenceSupport referenceSupport;

private final LSPDefinitionSupport definitionSupport;

private final LSPDeclarationSupport declarationSupport;

private final LSPTypeDefinitionSupport typeDefinitionSupport;
Expand All @@ -101,6 +104,7 @@ private LSPFileSupport(@NotNull PsiFile file) {
this.completionSupport = new LSPCompletionSupport(file);
this.implementationSupport = new LSPImplementationSupport(file);
this.referenceSupport = new LSPReferenceSupport(file);
this.definitionSupport = new LSPDefinitionSupport(file);
this.declarationSupport = new LSPDeclarationSupport(file);
this.typeDefinitionSupport = new LSPTypeDefinitionSupport(file);
this.semanticTokensSupport = new LSPSemanticTokensSupport(file);
Expand Down Expand Up @@ -275,6 +279,15 @@ public LSPReferenceSupport getReferenceSupport() {
return referenceSupport;
}

/**
* Returns the LSP definition support.
*
* @return the LSP definition support.
*/
public LSPDefinitionSupport getDefinitionSupport() {
return definitionSupport;
}

/**
* Returns the LSP declaration support.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
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.LanguageServiceAccessor;
Expand Down Expand Up @@ -61,7 +60,6 @@ protected CompletableFuture<List<Location>> doLoad(LSPDeclarationParams params,
@NotNull Project project,
@NotNull LSPDeclarationParams params,
@NotNull CancellationSupport cancellationSupport) {
var textDocumentIdentifier = LSPIJUtils.toTextDocumentIdentifier(file);
return LanguageServiceAccessor.getInstance(project)
.getLanguageServers(file, LanguageServerItem::isDeclarationSupported)
.thenComposeAsync(languageServers -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
if (document == null) {
return Collections.emptyList();
}
var params = new LSPHoverParams(LSPIJUtils.toTextDocumentIdentifier(file), LSPIJUtils.toPosition(offset, document), offset);
LSPHoverSupport hoverSupport = LSPFileSupport.getSupport(psiFile).getHoverSupport();
CompletableFuture<List<Hover>> hoverFuture = hoverSupport.getHover(offset, document);
CompletableFuture<List<Hover>> hoverFuture = hoverSupport.getHover(params);

try {
waitUntilDone(hoverFuture, psiFile);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* 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 https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and declaration
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.documentation;

import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;

/**
* LSP hover parameters which hosts the offset where hover has been triggered.
*/
public class LSPHoverParams extends HoverParams {

// Use transient to avoid serializing the fields when GSON will be processed
private transient final int offset;

public LSPHoverParams(TextDocumentIdentifier textDocument, Position position, int offset) {
super.setTextDocument(textDocument);
super.setPosition(position);
this.offset = offset;
}

public int getOffset() {
return offset;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.documentation;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
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.LanguageServiceAccessor;
Expand Down Expand Up @@ -44,13 +42,12 @@ public LSPHoverSupport(@NotNull PsiFile file) {
super(file);
}

public CompletableFuture<List<Hover>> getHover(int offset, Document document) {
if (previousOffset != null && previousOffset != offset) {
// Cancel previous hover (without setting previousOffset to null)
cancel();
public CompletableFuture<List<Hover>> getHover(LSPHoverParams params) {
int offset = params.getOffset();
if (previousOffset != null && !previousOffset.equals(offset)) {
super.cancel();
}
previousOffset = offset;
HoverParams params = LSPIJUtils.toHoverParams(offset, document);
return super.getFeatureData(params);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* 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 https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and declaration
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.highlight;

import org.eclipse.lsp4j.DocumentHighlightParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;

/**
* LSP documentHighlight parameters which hosts the offset where documentHighlight has been triggered.
*/
public class LSPDocumentHighlightParams extends DocumentHighlightParams {

// Use transient to avoid serializing the fields when GSON will be processed
private transient final int offset;

public LSPDocumentHighlightParams(TextDocumentIdentifier textDocument, Position position, int offset) {
super.setTextDocument(textDocument);
super.setPosition(position);
this.offset = offset;
}

public int getOffset() {
return offset;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,16 @@
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.highlight;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
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.LanguageServiceAccessor;
import com.redhat.devtools.lsp4ij.features.AbstractLSPDocumentFeatureSupport;
import com.redhat.devtools.lsp4ij.internal.CancellationSupport;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightParams;
import org.eclipse.lsp4j.Position;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
Expand All @@ -46,16 +43,12 @@ public LSPHighlightSupport(@NotNull PsiFile file) {
super(file);
}

public CompletableFuture<List<DocumentHighlight>> getHighlights(int offset,
@NotNull Document document,
@NotNull VirtualFile file) {
if (previousOffset != null && previousOffset != offset) {
// Cancel previous documentHighlight (without setting previousOffset to null)
cancel();
public CompletableFuture<List<DocumentHighlight>> getHighlights(LSPDocumentHighlightParams params) {
int offset = params.getOffset();
if (previousOffset != null && !previousOffset.equals(offset)) {
super.cancel();
}
previousOffset = offset;
Position position = LSPIJUtils.toPosition(offset, document);
DocumentHighlightParams params = new DocumentHighlightParams(LSPIJUtils.toTextDocumentIdentifier(file), position);
return super.getFeatureData(params);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ private List<LSPHighlightPsiElement> getTargets(Editor editor, PsiFile psiFile)
int offset = TargetElementUtil.adjustOffset(psiFile, document, editor.getCaretModel().getOffset());

// Consume LSP 'textDocument/documentHighlight' request
var params = new LSPDocumentHighlightParams(LSPIJUtils.toTextDocumentIdentifier(file), LSPIJUtils.toPosition(offset, document), offset);
LSPHighlightSupport highlightSupport = LSPFileSupport.getSupport(psiFile).getHighlightSupport();
CompletableFuture<List<DocumentHighlight>> highlightFuture = highlightSupport.getHighlights(offset, document, file);
CompletableFuture<List<DocumentHighlight>> highlightFuture = highlightSupport.getHighlights(params);
try {
waitUntilDone(highlightFuture, psiFile);
} catch (ProcessCanceledException e) {//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* 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 https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and declaration
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.navigation;

import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;

/**
* LSP definition parameters which hosts the offset where definition has been triggered.
*/
public class LSPDefinitionParams extends DefinitionParams {

// Use transient to avoid serializing the fields when GSON will be processed
private transient final int offset;

public LSPDefinitionParams(TextDocumentIdentifier textDocument, Position position, int offset) {
super.setTextDocument(textDocument);
super.setPosition(position);
this.offset = offset;
}

public int getOffset() {
return offset;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*******************************************************************************
* 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 definition
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.navigation;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.redhat.devtools.lsp4ij.LSPRequestConstants;
import com.redhat.devtools.lsp4ij.LanguageServerItem;
import com.redhat.devtools.lsp4ij.LanguageServiceAccessor;
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 org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
* LSP definition support which collect:
*
* <ul>
* <li>textDocument/definition</li>
* </ul>
*/
public class LSPDefinitionSupport extends AbstractLSPDocumentFeatureSupport<LSPDefinitionParams, List<Location>> {

private Integer previousOffset;

public LSPDefinitionSupport(@NotNull PsiFile file) {
super(file);
}

public CompletableFuture<List<Location>> getDefinitions(LSPDefinitionParams params) {
int offset = params.getOffset();
if (previousOffset != null && !previousOffset.equals(offset)) {
super.cancel();
}
previousOffset = offset;
return super.getFeatureData(params);
}

@Override
protected CompletableFuture<List<Location>> doLoad(LSPDefinitionParams params, CancellationSupport cancellationSupport) {
PsiFile file = super.getFile();
return collectDefinitions(file.getVirtualFile(), file.getProject(), params, cancellationSupport);
}

private static @NotNull CompletableFuture<List<Location>> collectDefinitions(@NotNull VirtualFile file,
@NotNull Project project,
@NotNull LSPDefinitionParams params,
@NotNull CancellationSupport cancellationSupport) {
return LanguageServiceAccessor.getInstance(project)
.getLanguageServers(file, LanguageServerItem::isDefinitionSupported)
.thenComposeAsync(languageServers -> {
// Here languageServers is the list of language servers which matches the given file
// and which have definition capability
if (languageServers.isEmpty()) {
return CompletableFuture.completedFuture(null);
}

// Collect list of textDocument/definition future for each language servers
List<CompletableFuture<List<Location>>> definitionsPerServerFutures = languageServers
.stream()
.map(languageServer -> getDefinitionFor(params, languageServer, cancellationSupport))
.toList();

// Merge list of textDocument/definition future in one future which return the list of definition ranges
return CompletableFutures.mergeInOneFuture(definitionsPerServerFutures, cancellationSupport);
});
}

private static CompletableFuture<List<Location>> getDefinitionFor(LSPDefinitionParams params,
LanguageServerItem languageServer,
CancellationSupport cancellationSupport) {
return cancellationSupport.execute(languageServer
.getTextDocumentService()
.definition(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DECLARATION)
.thenApplyAsync(locations -> {
if (locations == null) {
// textDocument/definition may return null
return Collections.emptyList();
}
if (locations.isLeft()) {
return locations.getLeft()
.stream()
.map(l -> new Location(l.getUri(), l.getRange()))
.toList();

}
return locations.getRight()
.stream()
.map(l -> new Location(l.getTargetUri(), l.getTargetRange()))
.toList();
});
}

}
Loading

0 comments on commit 5da7f64

Please sign in to comment.