Skip to content

Commit

Permalink
feat: Open references if definition result has the same range than
Browse files Browse the repository at this point in the history
clicked offset

Fixes redhat-developer#531

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Sep 21, 2024
1 parent eca7ca6 commit 639a767
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 66 deletions.
15 changes: 15 additions & 0 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPFileSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.redhat.devtools.lsp4ij.features.color.LSPColorSupport;
import com.redhat.devtools.lsp4ij.features.completion.LSPCompletionSupport;
import com.redhat.devtools.lsp4ij.features.declaration.LSPDeclarationSupport;
import com.redhat.devtools.lsp4ij.features.definition.LSPDefinitionSupport;
import com.redhat.devtools.lsp4ij.features.documentLink.LSPDocumentLinkSupport;
import com.redhat.devtools.lsp4ij.features.documentation.LSPHoverSupport;
import com.redhat.devtools.lsp4ij.features.foldingRange.LSPFoldingRangeSupport;
Expand All @@ -43,6 +44,7 @@
public class LSPFileSupport extends UserDataHolderBase implements Disposable {

private static final Key<LSPFileSupport> LSP_FILE_SUPPORT_KEY = Key.create("lsp.file.support");

private final PsiFile file;

private final LSPCodeLensSupport codeLensSupport;
Expand Down Expand Up @@ -75,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 @@ -99,6 +103,7 @@ private LSPFileSupport(@NotNull PsiFile file) {
this.implementationSupport = new LSPImplementationSupport(file);
this.referenceSupport = new LSPReferenceSupport(file);
this.declarationSupport = new LSPDeclarationSupport(file);
this.definitionSupport = new LSPDefinitionSupport(file);
this.typeDefinitionSupport = new LSPTypeDefinitionSupport(file);
this.semanticTokensSupport = new LSPSemanticTokensSupport(file);
file.putUserData(LSP_FILE_SUPPORT_KEY, this);
Expand All @@ -123,6 +128,7 @@ public void dispose() {
getCompletionSupport().cancel();
getImplementationSupport().cancel();
getReferenceSupport().cancel();
getDefinitionSupport().cancel();
getDeclarationSupport().cancel();
getTypeDefinitionSupport().cancel();
getSemanticTokensSupport().cancel();
Expand Down Expand Up @@ -270,6 +276,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
@@ -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 definition
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.definition;

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.definition;

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 collectTypeDefinitions(file.getVirtualFile(), file.getProject(), params, cancellationSupport);
}

private static @NotNull CompletableFuture<List<Location>> collectTypeDefinitions(@NotNull VirtualFile file,
@NotNull Project project,
@NotNull LSPDefinitionParams params,
@NotNull CancellationSupport cancellationSupport) {
return LanguageServiceAccessor.getInstance(project)
.getLanguageServers(file, LanguageServerItem::isTypeDefinitionSupported)
.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 -> getTypeDefinitionFor(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>> getTypeDefinitionFor(LSPDefinitionParams params,
LanguageServerItem languageServer,
CancellationSupport cancellationSupport) {
return cancellationSupport.execute(languageServer
.getTextDocumentService()
.definition(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_DEFINITION)
.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();
});
}

}
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(LSPImplementationParams param
@NotNull Project project,
@NotNull LSPImplementationParams params,
@NotNull CancellationSupport cancellationSupport) {
var textDocumentIdentifier = LSPIJUtils.toTextDocumentIdentifier(file);
return LanguageServiceAccessor.getInstance(project)
.getLanguageServers(file, LanguageServerItem::isImplementationSupported)
.thenComposeAsync(languageServers -> {
Expand Down
Loading

0 comments on commit 639a767

Please sign in to comment.