forked from redhat-developer/lsp4ij
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support for
textDocument/semanticTokens
Fixes redhat-developer#238 Signed-off-by: azerr <[email protected]>
- Loading branch information
1 parent
6f183e1
commit 8d5c2b0
Showing
10 changed files
with
507 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
.../java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPRainbowColorSettingsPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package com.redhat.devtools.lsp4ij.features.semanticTokens; | ||
|
||
import com.intellij.lang.Language; | ||
import com.intellij.openapi.editor.colors.TextAttributesKey; | ||
import com.intellij.openapi.fileTypes.PlainTextLanguage; | ||
import com.intellij.openapi.fileTypes.SyntaxHighlighter; | ||
import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; | ||
import com.intellij.openapi.options.colors.AttributesDescriptor; | ||
import com.intellij.openapi.options.colors.ColorDescriptor; | ||
import com.intellij.openapi.options.colors.RainbowColorSettingsPage; | ||
import com.intellij.openapi.util.NlsContexts; | ||
import com.intellij.psi.codeStyle.DisplayPriority; | ||
import com.intellij.psi.codeStyle.DisplayPrioritySortable; | ||
import org.jetbrains.annotations.NonNls; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import javax.swing.*; | ||
import java.util.Map; | ||
|
||
public class LSPRainbowColorSettingsPage implements RainbowColorSettingsPage, DisplayPrioritySortable { | ||
@Override | ||
public boolean isRainbowType(TextAttributesKey type) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public @Nullable Language getLanguage() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public @Nullable Icon getIcon() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public @NotNull SyntaxHighlighter getHighlighter() { | ||
return SyntaxHighlighterFactory.getSyntaxHighlighter(PlainTextLanguage.INSTANCE, null, null); | ||
} | ||
|
||
@Override | ||
public @NonNls @NotNull String getDemoText() { | ||
return "LSP ..."; | ||
} | ||
|
||
@Override | ||
public @Nullable Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public AttributesDescriptor @NotNull [] getAttributeDescriptors() { | ||
return new AttributesDescriptor[0]; | ||
} | ||
|
||
@Override | ||
public ColorDescriptor @NotNull [] getColorDescriptors() { | ||
return ColorDescriptor.EMPTY_ARRAY; | ||
} | ||
|
||
@Override | ||
public @NotNull @NlsContexts.ConfigurableName String getDisplayName() { | ||
return "LSP"; | ||
} | ||
|
||
@Override | ||
public DisplayPriority getPriority() { | ||
return DisplayPriority.LANGUAGE_SETTINGS; | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPRainbowVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package com.redhat.devtools.lsp4ij.features.semanticTokens; | ||
|
||
import com.intellij.codeInsight.daemon.RainbowVisitor; | ||
import com.intellij.codeInsight.daemon.impl.HighlightVisitor; | ||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder; | ||
import com.intellij.openapi.editor.colors.TextAttributesScheme; | ||
import com.intellij.openapi.progress.ProcessCanceledException; | ||
import com.intellij.psi.PsiElement; | ||
import com.intellij.psi.PsiFile; | ||
import com.redhat.devtools.lsp4ij.LSPFileSupport; | ||
import com.redhat.devtools.lsp4ij.LSPIJUtils; | ||
import com.redhat.devtools.lsp4ij.LanguageServersRegistry; | ||
import org.eclipse.lsp4j.SemanticTokensParams; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.concurrent.CancellationException; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutionException; | ||
|
||
import static com.redhat.devtools.lsp4ij.internal.CompletableFutures.isDoneNormally; | ||
import static com.redhat.devtools.lsp4ij.internal.CompletableFutures.waitUntilDone; | ||
|
||
public class LSPRainbowVisitor extends RainbowVisitor { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(LSPRainbowVisitor.class); | ||
private TextAttributesScheme colorScheme; | ||
|
||
@Override | ||
public boolean suitableForFile(@NotNull PsiFile file) { | ||
return LanguageServersRegistry.getInstance().isFileSupported(file); | ||
} | ||
|
||
@Override | ||
public boolean analyze(@NotNull PsiFile file, boolean updateWholeFile, @NotNull HighlightInfoHolder holder, @NotNull Runnable action) { | ||
this.colorScheme = holder.getColorsScheme(); | ||
return super.analyze(file, updateWholeFile, holder, action); | ||
} | ||
|
||
@Override | ||
public void visit(@NotNull PsiElement element) { | ||
// Consume LSP 'textDocument/semanticTokens' request | ||
PsiFile file = element.getContainingFile(); | ||
if (!LanguageServersRegistry.getInstance().isFileSupported(file)) { | ||
return; | ||
} | ||
LSPSemanticTokensSupport semanticTokensSupport = LSPFileSupport.getSupport(file).getSemanticTokensSupport(); | ||
var params = new SemanticTokensParams(LSPIJUtils.toTextDocumentIdentifier(file.getVirtualFile())); | ||
CompletableFuture<SemanticTokensData> semanticTokensFuture = semanticTokensSupport.getSemanticTokens(params); | ||
try { | ||
waitUntilDone(semanticTokensFuture, file); | ||
} catch (ProcessCanceledException e) {//Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility | ||
//TODO delete block when minimum required version is 2024.2 | ||
semanticTokensSupport.cancel(); | ||
return; | ||
} catch (CancellationException e) { | ||
// cancel the LSP requests textDocument/foldingRanges | ||
semanticTokensSupport.cancel(); | ||
return; | ||
} catch (ExecutionException e) { | ||
LOGGER.error("Error while consuming LSP 'textDocument/semanticTokens' request", e); | ||
return; | ||
} | ||
|
||
if (isDoneNormally(semanticTokensFuture)) { | ||
// textDocument/foldingRanges has been collected correctly, create list of IJ FoldingDescriptor from LSP FoldingRange list | ||
SemanticTokensData semanticTokens = semanticTokensFuture.getNow(null); | ||
if (semanticTokens != null) { | ||
var document = LSPIJUtils.getDocument(file.getVirtualFile()); | ||
if (document == null) { | ||
return; | ||
} | ||
semanticTokens.highlight(super.getHighlighter(), document, info -> super.addInfo(info)); | ||
} | ||
|
||
} | ||
} | ||
|
||
@Override | ||
public @NotNull HighlightVisitor clone() { | ||
return new LSPRainbowVisitor(); | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
...ain/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LSPSemanticTokensSupport.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/******************************************************************************* | ||
* 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 implementation | ||
******************************************************************************/ | ||
package com.redhat.devtools.lsp4ij.features.semanticTokens; | ||
|
||
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.AbstractLSPFeatureSupport; | ||
import com.redhat.devtools.lsp4ij.internal.CancellationSupport; | ||
import org.eclipse.lsp4j.SemanticTokensLegend; | ||
import org.eclipse.lsp4j.SemanticTokensParams; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
/** | ||
* LSP semanticTokens support which loads and caches semantic tokens by consuming: | ||
* | ||
* <ul> | ||
* <li>LSP 'textDocument/semanticTokens' requests</li> | ||
* </ul> | ||
*/ | ||
public class LSPSemanticTokensSupport extends AbstractLSPFeatureSupport<SemanticTokensParams, SemanticTokensData> { | ||
|
||
public LSPSemanticTokensSupport(@NotNull PsiFile file) { | ||
super(file); | ||
} | ||
|
||
public CompletableFuture<SemanticTokensData> getSemanticTokens(SemanticTokensParams params) { | ||
return super.getFeatureData(params); | ||
} | ||
|
||
@Override | ||
protected CompletableFuture<SemanticTokensData> doLoad(SemanticTokensParams params, CancellationSupport cancellationSupport) { | ||
PsiFile file = super.getFile(); | ||
return getSemanticTokens(file.getVirtualFile(), file.getProject(), params, cancellationSupport); | ||
} | ||
|
||
private static @NotNull CompletableFuture<SemanticTokensData> getSemanticTokens(@NotNull VirtualFile file, | ||
@NotNull Project project, | ||
@NotNull SemanticTokensParams params, | ||
@NotNull CancellationSupport cancellationSupport) { | ||
|
||
return LanguageServiceAccessor.getInstance(project) | ||
.getLanguageServers(file, LanguageServerItem::isSemanticTokensSupported) | ||
.thenComposeAsync(languageServers -> { | ||
// Here languageServers is the list of language servers which matches the given file | ||
// and which have folding range capability | ||
if (languageServers.isEmpty()) { | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
// Collect list of textDocument/semanticTokens future for each language servers | ||
List<CompletableFuture<SemanticTokensData>> semanticTokensPerServerFutures = languageServers | ||
.stream() | ||
.map(languageServer -> getSemanticTokensFor(params, languageServer, cancellationSupport)) | ||
.filter(Objects::nonNull) | ||
.toList(); | ||
|
||
// Merge list of textDocument/foldingRange future in one future which return the list of folding ranges | ||
return semanticTokensPerServerFutures.get(0); //CompletableFutures.mergeInOneFuture(semanticTokensPerServerFutures, cancellationSupport); | ||
}); | ||
} | ||
|
||
private static CompletableFuture<SemanticTokensData> getSemanticTokensFor(SemanticTokensParams params, | ||
LanguageServerItem languageServer, | ||
CancellationSupport cancellationSupport) { | ||
return cancellationSupport.execute(languageServer | ||
.getTextDocumentService() | ||
.semanticTokensFull(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL) | ||
.thenApplyAsync(semanticTokens -> { | ||
if (semanticTokens == null) { | ||
// textDocument/semanticTokensFull may return null | ||
return null; | ||
} | ||
return new SemanticTokensData(semanticTokens, getLegend(languageServer)); | ||
}); | ||
} | ||
|
||
@Nullable | ||
private static SemanticTokensLegend getLegend(LanguageServerItem languageServer) { | ||
var serverCapabilities = languageServer.getServerCapabilities(); | ||
return serverCapabilities.getSemanticTokensProvider() != null ? serverCapabilities.getSemanticTokensProvider().getLegend() : null; | ||
} | ||
|
||
} |
Oops, something went wrong.