Skip to content

Commit

Permalink
feat: Support for TypeHierachy + CallHierarchy
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Nov 23, 2024
1 parent e5199d6 commit 429bfa1
Show file tree
Hide file tree
Showing 40 changed files with 2,509 additions and 54 deletions.
66 changes: 59 additions & 7 deletions docs/LSPSupport.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ Current state of [Language Features]( https://microsoft.github.io/language-serve
*[textDocument/typeDefinition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_typeDefinition) (see [implementation details](#type-definition))
*[textDocument/implementation](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation) (see [implementation details](#implementation))
*[textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) (see [implementation details](#references))
*[textDocument/prepareCallHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy).
*[textDocument/incomingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls).
*[textDocument/outgoingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_outgoingCalls).
*[textDocument/prepareTypeHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareTypeHierarchy).
*[typeHierarchy/supertypes](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#typeHierarchy_supertypes).
*[textDocument/prepareCallHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy) (see [implementation details](#call-hierarchy))
*[textDocument/incomingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls).
*[textDocument/outgoingCalls](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_outgoingCalls).
*[textDocument/prepareTypeHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareTypeHierarchy) (see [implementation details](#type-hierarchy))
*[typeHierarchy/subtypes](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#typeHierarchy_subtypes).
*[typeHierarchy/supertypes](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#typeHierarchy_supertypes).
*[textDocument/foldingRange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange) (see [implementation details](#folding-range))
*[textDocument/selectionRange](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange).
*[textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) (see [implementation details](#document-symbol))
Expand Down Expand Up @@ -561,9 +562,60 @@ Here is an example with [TypeScript Language Server](./user-defined-ls/typescrip
which opens the standard `File Structure` with `Ctrl+F12` / `Cmd+F12`
(also available with the `Navigate / File Structure` menu) to display TypeScript functions as symbols and navigate them easily:

![textDocument/documentSymbol](./images/lsp-support/textDocument_documentSymbol.gif)
### Call Hierarchy

You can too open the `Structure` view with `View / Tool Windows / Structure` menu:
[textDocument/prepareCallHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy) is implemented with
the `callHierarchyProvider` extension point. By default, LSP4IJ registers the `callHierarchyProvider` with
[LSPCallHierarchyProvider](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/features/callHierarchy/LSPCallHierarchyProvider.java) class for `TEXT` and `textmate` languages:

```xml
<!-- LSP textDocument/callHierarchy request support -->
<callHierarchyProvider
language="TEXT"
implementationClass="com.redhat.devtools.lsp4ij.features.callHierarchy.LSPCallHierarchyProvider" />
<callHierarchyProvider
language="textmate"
implementationClass="com.redhat.devtools.lsp4ij.features.callHierarchy.LSPCallHierarchyProvider" />
```

If you use another language, you will have to declare `callHierarchyProvider` with your language:

```xml
<callHierarchyProvider
language="YourLanguage"
implementationClass="com.redhat.devtools.lsp4ij.features.callHierarchy.LSPCallHierarchyProvider" />
```

After set the cursor position in a file, you can process Call Hierarchy with the `Navigate / Call Hierarchy` menu (or Ctr+Alt+H).
Here is a call hierarchy example with [Go Language Server](./user-defined-ls/gopls.md):

![textDocument/callHierarchy](./images/lsp-support/textDocument_callHierarchy.gif)

### Type Hierarchy

[textDocument/prepareTypeHierarchy](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareTypeHierarchy) is implemented with
the `typeHierarchyProvider` extension point. By default, LSP4IJ registers the `typeHierarchyProvider` with
[LSPTypeHierarchyProvider](https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/features/typeHierarchy/LSPTypeHierarchyProvider.java) class for `TEXT` and `textmate` languages:

```xml
<!-- LSP textDocument/typeHierarchy request support -->
<typeHierarchyProvider
language="TEXT"
implementationClass="com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPTypeHierarchyProvider" />
<typeHierarchyProvider
language="textmate"
implementationClass="com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPTypeHierarchyProvider" />
```

If you use another language, you will have to declare `typeHierarchyProvider` with your language:

```xml
<typeHierarchyProvider
language="YourLanguage"
implementationClass="com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPTypeHierarchyProvider" />
```

After set the cursor position in a file, you can process Type Hierarchy with the `Navigate / Type Hierarchy` menu (or Ctr+Alt+H).

### Workspace Symbol

Expand Down
80 changes: 80 additions & 0 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPFileSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.psi.PsiFile;
import com.redhat.devtools.lsp4ij.features.callHierarchy.LSPCallHierarchyIncomingCallsSupport;
import com.redhat.devtools.lsp4ij.features.callHierarchy.LSPCallHierarchyOutgoingCallsSupport;
import com.redhat.devtools.lsp4ij.features.callHierarchy.LSPPrepareCallHierarchySupport;
import com.redhat.devtools.lsp4ij.features.codeAction.intention.LSPIntentionCodeActionSupport;
import com.redhat.devtools.lsp4ij.features.codeLens.LSPCodeLensSupport;
import com.redhat.devtools.lsp4ij.features.color.LSPColorSupport;
Expand All @@ -34,6 +37,9 @@
import com.redhat.devtools.lsp4ij.features.semanticTokens.LSPSemanticTokensSupport;
import com.redhat.devtools.lsp4ij.features.signatureHelp.LSPSignatureHelpSupport;
import com.redhat.devtools.lsp4ij.features.typeDefinition.LSPTypeDefinitionSupport;
import com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPPrepareTypeHierarchySupport;
import com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPTypeHierarchySubtypesSupport;
import com.redhat.devtools.lsp4ij.features.typeHierarchy.LSPTypeHierarchySupertypesSupport;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -87,6 +93,14 @@ public class LSPFileSupport extends UserDataHolderBase implements Disposable {

private final LSPDocumentSymbolSupport documentSymbolSupport;

private final LSPPrepareCallHierarchySupport prepareCallHierarchySupport;
private final LSPCallHierarchyIncomingCallsSupport callHierarchyIncomingCallsSupport;
private final LSPCallHierarchyOutgoingCallsSupport callHierarchyOutgoingCallsSupport;

private final LSPPrepareTypeHierarchySupport prepareTypeHierarchySupport;
private final LSPTypeHierarchySubtypesSupport typeHierarchySubtypesSupport;
private final LSPTypeHierarchySupertypesSupport typeHierarchySupertypesSupport;

private LSPFileSupport(@NotNull PsiFile file) {
this.file = file;
this.codeLensSupport = new LSPCodeLensSupport(file);
Expand All @@ -109,6 +123,12 @@ private LSPFileSupport(@NotNull PsiFile file) {
this.typeDefinitionSupport = new LSPTypeDefinitionSupport(file);
this.semanticTokensSupport = new LSPSemanticTokensSupport(file);
this.documentSymbolSupport = new LSPDocumentSymbolSupport(file);
this.prepareCallHierarchySupport = new LSPPrepareCallHierarchySupport(file);
this.callHierarchyIncomingCallsSupport = new LSPCallHierarchyIncomingCallsSupport(file);
this.callHierarchyOutgoingCallsSupport = new LSPCallHierarchyOutgoingCallsSupport(file);
this.prepareTypeHierarchySupport = new LSPPrepareTypeHierarchySupport(file);
this.typeHierarchySubtypesSupport = new LSPTypeHierarchySubtypesSupport(file);
this.typeHierarchySupertypesSupport = new LSPTypeHierarchySupertypesSupport(file);
file.putUserData(LSP_FILE_SUPPORT_KEY, this);
}

Expand All @@ -135,6 +155,12 @@ public void dispose() {
getTypeDefinitionSupport().cancel();
getSemanticTokensSupport().cancel();
getDocumentSymbolSupport().cancel();
getPrepareCallHierarchySupport().cancel();
getCallHierarchyIncomingCallsSupport().cancel();
getCallHierarchyOutgoingCallsSupport().cancel();
getPrepareTypeHierarchySupport().cancel();
getTypeHierarchySubtypesSupport().cancel();
getTypeHierarchySupertypesSupport().cancel();
var map = getUserMap();
for (var key : map.getKeys()) {
var value = map.get(key);
Expand Down Expand Up @@ -324,6 +350,60 @@ public LSPDocumentSymbolSupport getDocumentSymbolSupport() {
return documentSymbolSupport;
}

/**
* Returns the LSP prepare call hierarchy support.
*
* @return the LSP prepare call hierarchy support.
*/
public LSPPrepareCallHierarchySupport getPrepareCallHierarchySupport() {
return prepareCallHierarchySupport;
}

/**
* Returns the LSP call hierarchy incoming calls support.
*
* @return the LSP call hierarchy incoming calls support.
*/
public LSPCallHierarchyIncomingCallsSupport getCallHierarchyIncomingCallsSupport() {
return callHierarchyIncomingCallsSupport;
}

/**
* Returns the LSP prepare call hierarchy outgoing calls support.
*
* @return the LSP prepare call hierarchy outgoing calls support.
*/
public LSPCallHierarchyOutgoingCallsSupport getCallHierarchyOutgoingCallsSupport() {
return callHierarchyOutgoingCallsSupport;
}

/**
* Returns the LSP prepare type hierarchy support.
*
* @return the LSP prepare type hierarchy support.
*/
public LSPPrepareTypeHierarchySupport getPrepareTypeHierarchySupport() {
return prepareTypeHierarchySupport;
}

/**
* Returns the LSP type hierarchy subtypes support.
*
* @return the LSP type hierarchy subtypes support.
*/
public LSPTypeHierarchySubtypesSupport getTypeHierarchySubtypesSupport() {
return typeHierarchySubtypesSupport;
}

/**
* Returns the LSP type hierarchy supertypes support.
*
* @return the LSP type hierarchy supertypes support.
*/
public LSPTypeHierarchySupertypesSupport getTypeHierarchySupertypesSupport() {
return typeHierarchySupertypesSupport;
}

/**
* Return the existing LSP file support for the given Psi file, or create a new one if necessary.
*
Expand Down
17 changes: 11 additions & 6 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPRequestConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class LSPRequestConstants {
public static final String WORKSPACE_WILL_CREATE_FILES = "workspace/willCreateFiles";
public static final String WORKSPACE_WILL_DELETE_FILES = "workspace/willDeleteFiles";
public static final String WORKSPACE_WILL_RENAME_FILES = "workspace/willRenameFiles";
public static final String WORKSPACE_SYMBOL = "workspace/symbol";

// textDocument/* LSP requests

Expand All @@ -31,10 +32,10 @@ public class LSPRequestConstants {
public static final String TEXT_DOCUMENT_TYPE_DEFINITION = "textDocument/typeDefinition";
public static final String TEXT_DOCUMENT_CODE_ACTION = "textDocument/codeAction";
public static final String TEXT_DOCUMENT_CODE_LENS = "textDocument/codeLens";
public static final String TEXT_DOCUMENT_RESOLVE_CODE_LENS = "textDocument/resolveCodelens";
public static final String CODE_LENS_RESOLVE = "codelens/resolve";
public static final String TEXT_DOCUMENT_HOVER = "textDocument/hover";
public static final String TEXT_DOCUMENT_INLAY_HINT = "textDocument/inlayHint";
public static final String TEXT_DOCUMENT_RESOLVE_INLAY_HINT = "textDocument/resolveInlayHint";
public static final String INLAY_HINT_RESOLVE = "inlayHint/resolve";
public static final String TEXT_DOCUMENT_DOCUMENT_COLOR = "textDocument/documentColor";
public static final String TEXT_DOCUMENT_COMPLETION = "textDocument/completion";
public static final String TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT = "textDocument/documentHighlight";
Expand All @@ -45,11 +46,15 @@ public class LSPRequestConstants {
public static final String TEXT_DOCUMENT_SIGNATURE_HELP = "textDocument/signatureHelp";
public static final String TEXT_DOCUMENT_PREPARE_RENAME = "textDocument/prepareRename";
public static final String TEXT_DOCUMENT_RENAME = "textDocument/rename";
public static final String TEXT_DOCUMENT_DOCUMENT_COMPLETION = "textDocument/completion";
public static final String TEXT_DOCUMENT_DOCUMENT_SYMBOL = "textDocument/documentSymbol";

// workspace/* LSP requests
public static final String WORKSPACE_SYMBOL = "workspace/symbol";
public static final String TEXT_DOCUMENT_CALL_HIERARCHY = "textDocument/callHierarchy";
public static final String TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY = "textDocument/prepareCallHierarchy";
public static final String CALL_HIERARCHY_INCOMING_CALLS = "callHierarchy/incomingCalls";
public static final String CALL_HIERARCHY_OUTGOING_CALLS = "callHierarchy/outgoingCalls";
public static final String TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY = "textDocument/prepareTypeHierarchy";
public static final String TYPE_HIERARCHY_SUB_TYPES = "typeHierarchy/subtypes";
public static final String TYPE_HIERARCHY_SUPER_TYPES = "typeHierarchy/supertypes";
public static final String TEXT_DOCUMENT_TYPE_HIERARCHY = "textDocument/typeHierarchy";

private LSPRequestConstants() {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*******************************************************************************
* 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 implementation
******************************************************************************/
package com.redhat.devtools.lsp4ij.client.features;

import com.intellij.psi.PsiFile;
import com.redhat.devtools.lsp4ij.server.capabilities.CallHierarchyCapabilityRegistry;
import org.eclipse.lsp4j.CallHierarchyItem;
import org.eclipse.lsp4j.ServerCapabilities;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* LSP call hierarchy feature.
*/
@ApiStatus.Experimental
public class LSPCallHierarchyFeature extends AbstractLSPDocumentFeature {

private CallHierarchyCapabilityRegistry callHierarchyCapabilityRegistry;

@Override
public boolean isSupported(@NotNull PsiFile file) {
return isCallHierarchySupported(file);
}

/**
* Returns true if the file associated with a language server can support call hierarchy and false otherwise.
*
* @param file the file.
* @return true if the file associated with a language server can support call hierarchy and false otherwise.
*/
public boolean isCallHierarchySupported(@NotNull PsiFile file) {
return getCallHierarchyCapabilityRegistry().isCallHierarchySupported(file);
}

public CallHierarchyCapabilityRegistry getCallHierarchyCapabilityRegistry() {
if (callHierarchyCapabilityRegistry == null) {
initCallHierarchyCapabilityRegistry();
}
return callHierarchyCapabilityRegistry;
}

private synchronized void initCallHierarchyCapabilityRegistry() {
if (callHierarchyCapabilityRegistry != null) {
return;
}
var clientFeatures = getClientFeatures();
callHierarchyCapabilityRegistry = new CallHierarchyCapabilityRegistry(clientFeatures);
callHierarchyCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync());
}

@Override
public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) {
if (callHierarchyCapabilityRegistry != null) {
callHierarchyCapabilityRegistry.setServerCapabilities(serverCapabilities);
}
}

/**
* Returns the callHierarchyItem text from the LSP callHierarchyItem and null otherwise (to ignore the LSP CallHierarchyItem).
*
* @param callHierarchyItem the LSP callHierarchyItem
* @return the callHierarchyItem text from the LSP callHierarchyItem and null otherwise (to ignore the LSP CallHierarchyItem).
*/
@Nullable
public String getText(@NotNull CallHierarchyItem callHierarchyItem) {
return callHierarchyItem.getName();
}

}
Loading

0 comments on commit 429bfa1

Please sign in to comment.