Skip to content

Commit

Permalink
feat: improve document symbol + add doc
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Sep 24, 2024
1 parent 10aee5d commit 3761477
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 171 deletions.
44 changes: 41 additions & 3 deletions docs/LSPSupport.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Current state of [Language Features]( https://microsoft.github.io/language-serve
*[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).
* [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) (see [implementation details](#document-symbol))
*[textDocument/semanticTokens (experimental)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) (see [implementation details](#semantic-tokens))
*[textDocument/inlineValue](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlineValue).
*[workspace/inlineValue/refresh](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_inlineValue_refresh).
Expand Down Expand Up @@ -337,7 +337,7 @@ you can use `order="first"` to gain priority:
id="YourID"
language="YourLanguage"
order="first"
implementationClass="YourClass"/>
implementationClass="com.redhat.devtools.lsp4ij.features.signatureHelp.LSPParameterInfoHandler"/>
```

Here is an example with the [TypeScript Language Server](./user-defined-ls/typescript-language-server.md) showing signature help:
Expand Down Expand Up @@ -527,10 +527,48 @@ If you need other mapping:
* if you think it is a generic mapping, please create a contribution to define a new `SemanticTokensHighlightingColors` constants
* if the mapping is specific to your language, use the [semanticTokensColorsProvider extension point](./DeveloperGuide.md#semantic-tokens-colors-provider) to define your own provider and mapping.

### Document Symbol

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

```xml
<!-- LSP textDocument/documentSymbol -->
<lang.psiStructureViewFactory
id="LSPDocumentSymbolStructureViewFactoryForText"
language="TEXT"
implementationClass="com.redhat.devtools.lsp4ij.features.documentSymbol.LSPDocumentSymbolStructureViewFactory"/>
<lang.psiStructureViewFactory
id="LSPDocumentSymbolStructureViewFactoryForTextMate"
language="textmate"
implementationClass="com.redhat.devtools.lsp4ij.features.documentSymbol.LSPDocumentSymbolStructureViewFactory"/>
```

If you use another language, you will have to declare `lang.psiStructureViewFactory` with your language.
If `lang.psiStructureViewFactory` for the language is already defined by another plugin or the IDE,
you can use `order="first"` to gain priority:

```xml
<lang.psiStructureViewFactory
id="YourID"
language="YourLanguage"
order="first"
implementationClass="com.redhat.devtools.lsp4ij.features.documentSymbol.LSPDocumentSymbolStructureViewFactory"/>
```

Here is an example with [TypeScript Language Server](./user-defined-ls/typescript-language-server.md)
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)

You can too open the `Structure` view with `View / Tool Windows / Structure` menu:

### Workspace Symbol

[workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) is implemented with the `gotoSymbolContributor` extension point.

Here is an example with the [MicroProfile language server](https://github.com/eclipse/lsp4mp/tree/master/microprofile.ls) collecting JAX-RS endpoints:

![workspace/symbol](./images/lsp-support/workspace_symbol.gif)
![workspace/symbol](./images/lsp-support/workspace_symbol.gif)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
******************************************************************************/
package com.redhat.devtools.lsp4ij.features.documentSymbol;

import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.FakePsiElement;
import com.redhat.devtools.lsp4ij.LSPIJUtils;
import com.redhat.devtools.lsp4ij.ui.IconMapper;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.SymbolKind;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -23,35 +25,81 @@

/**
* LSP document symbol data.
*
* @param documentSymbol the LSP document symbol
*/
record DocumentSymbolData(@NotNull DocumentSymbol documentSymbol) {
class DocumentSymbolData extends FakePsiElement {

private record LSPItemPresentation(String name, SymbolKind symbolKind, String locationString) implements ItemPresentation {
private static final DocumentSymbolData[] EMPTY_ARRAY = new DocumentSymbolData[0];

public String name() {
return name;
}
private final @NotNull DocumentSymbol documentSymbol;
private final @NotNull PsiFile psiFile;
private final DocumentSymbolData parent;
private DocumentSymbolData[] cachedChildren;

@Override
public @NlsSafe @Nullable String getPresentableText() {
return name();
}
public DocumentSymbolData(@NotNull DocumentSymbol documentSymbol,
@NotNull PsiFile psiFile) {
this(documentSymbol, psiFile,null);
}

@Override
public @Nullable Icon getIcon(boolean unused) {
return IconMapper.getIcon(symbolKind);
}
public DocumentSymbolData(@NotNull DocumentSymbol documentSymbol,
@NotNull PsiFile psiFile,
@Nullable DocumentSymbolData parent) {
this.documentSymbol = documentSymbol;
this.psiFile = psiFile;
this.parent = parent;
}

@Override
public @NlsSafe @Nullable String getLocationString() {
return locationString;
}
private @NotNull DocumentSymbol getDocumentSymbol() {
return documentSymbol;
}

@Override
public @Nullable String getPresentableText() {
return documentSymbol.getName();
}

@Override
public @Nullable Icon getIcon(boolean unused) {
return IconMapper.getIcon(documentSymbol.getKind());
}

@Override
public @Nullable String getLocationString() {
return documentSymbol.getDetail();
}

@Override
public void navigate(boolean requestFocus) {
var selectionRange = getDocumentSymbol().getSelectionRange();
LSPIJUtils.openInEditor(psiFile.getVirtualFile(), selectionRange.getStart(), psiFile.getProject());
}

@Override
public boolean canNavigate() {
var selectionRange = getDocumentSymbol().getSelectionRange();
return selectionRange != null && selectionRange.getStart() != null;
}

@Override
public PsiElement /* PsiFile || DocumentSymbolData */ getParent() {
return parent != null ? parent : psiFile;
}

@Override
public DocumentSymbolData @NotNull [] getChildren() {
var children = getDocumentSymbol().getChildren();
if (children == null || children.isEmpty()) {
return DocumentSymbolData.EMPTY_ARRAY;
}
if (cachedChildren == null) {
cachedChildren = children.stream()
.map(child -> new DocumentSymbolData(child, psiFile, this))
.toArray(DocumentSymbolData[]::new);
}
return cachedChildren;
}

public ItemPresentation getPresentation() {
return new LSPItemPresentation(documentSymbol.getName(), documentSymbol.getKind(), documentSymbol.getDetail());
@Override
public @NotNull Project getProject() {
return psiFile.getProject();
}
}
Loading

0 comments on commit 3761477

Please sign in to comment.