Temporarily bring JDT LS semantic tokens into Boot LS to avoid conflicts
This commit is contained in:
@@ -32,21 +32,16 @@ import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.HoverParams;
|
||||
import org.eclipse.lsp4j.InlayHint;
|
||||
import org.eclipse.lsp4j.InlayHintParams;
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensDelta;
|
||||
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensRangeParams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SemanticTokensLegend;
|
||||
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokenData;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.CodeActionHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.CodeLensHandler;
|
||||
@@ -65,9 +60,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class CompositeLanguageServerComponents implements LanguageServerComponents {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CompositeLanguageServerComponents.class);
|
||||
|
||||
|
||||
public static class Builder {
|
||||
private Map<LanguageId, List<LanguageServerComponents>> componentsByLanguageId = new HashMap<>();
|
||||
|
||||
@@ -219,15 +212,12 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
|
||||
|
||||
List<SemanticTokensHandler> semanticTokenHandlers = componentsByLanguageId.values().stream().flatMap(l -> l.stream()).map(c -> c.getSemanticTokensHandler()).filter(o -> o.isPresent()).map(o -> o.get()).collect(Collectors.toList());
|
||||
List<SemanticTokensWithRegistrationOptions> listCapabilities = semanticTokenHandlers.stream().map(sth -> sth.getCapability()).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
for (int i = 1; i < listCapabilities.size(); i++) {
|
||||
SemanticTokensWithRegistrationOptions first = listCapabilities.get(0);
|
||||
SemanticTokensWithRegistrationOptions current = listCapabilities.get(i);
|
||||
if (!Objects.equals(first.getFull(), current.getFull())
|
||||
|| !Objects.equals(first.getLegend(), current.getLegend())
|
||||
|| !Objects.equals(first.getRange(), current.getRange())) {
|
||||
throw new IllegalStateException("Incompatible Semantic Token registrations for composite language server components");
|
||||
}
|
||||
}
|
||||
|
||||
SemanticTokensLegend legend = new SemanticTokensLegend(
|
||||
listCapabilities.stream().flatMap(cap -> cap.getLegend().getTokenTypes().stream()).distinct().collect(Collectors.toList()),
|
||||
listCapabilities.stream().flatMap(cap -> cap.getLegend().getTokenModifiers().stream()).distinct().collect(Collectors.toList())
|
||||
);
|
||||
|
||||
this.semanticTokensHanlder = semanticTokenHandlers.isEmpty() ? null : new SemanticTokensHandler() {
|
||||
|
||||
@Override
|
||||
@@ -236,32 +226,24 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
|
||||
SemanticTokensWithRegistrationOptions capabilities = new SemanticTokensWithRegistrationOptions();
|
||||
capabilities.setDocumentSelector(listCapabilities.stream().map(c -> c.getDocumentSelector()).flatMap(l -> l.stream()).collect(Collectors.toList()));
|
||||
if (!listCapabilities.isEmpty()) {
|
||||
SemanticTokensWithRegistrationOptions first = listCapabilities.get(0);
|
||||
capabilities.setFull(first.getFull());
|
||||
capabilities.setLegend(first.getLegend());
|
||||
capabilities.setRange(first.getRange());
|
||||
capabilities.setFull(true);
|
||||
capabilities.setLegend(legend);
|
||||
capabilities.setRange(false);
|
||||
}
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticTokens semanticTokensFull(SemanticTokensParams params, CancelChecker cancelChecker) {
|
||||
return findHandler(params.getTextDocument()).map(sth -> sth.semanticTokensFull(params, cancelChecker)).orElse(SemanticTokensHandler.super.semanticTokensFull(params, cancelChecker));
|
||||
public List<SemanticTokenData> semanticTokensFull(TextDocument doc, CancelChecker cancelChecker) {
|
||||
return findHandler(doc).map(sth -> sth.semanticTokensFull(doc, cancelChecker)).orElse(SemanticTokensHandler.super.semanticTokensFull(doc, cancelChecker));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<SemanticTokens, SemanticTokensDelta> semanticTokensFullDelta(
|
||||
SemanticTokensDeltaParams params, CancelChecker cancelChecker) {
|
||||
return findHandler(params.getTextDocument()).map(sth -> sth.semanticTokensFullDelta(params, cancelChecker)).orElse(SemanticTokensHandler.super.semanticTokensFullDelta(params, cancelChecker));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticTokens semanticTokensRange(SemanticTokensRangeParams params, CancelChecker cancelChecker) {
|
||||
return findHandler(params.getTextDocument()).map(sth -> sth.semanticTokensRange(params, cancelChecker)).orElse(SemanticTokensHandler.super.semanticTokensRange(params, cancelChecker));
|
||||
public List<SemanticTokenData> semanticTokensRange(TextDocument doc, Range range, CancelChecker cancelChecker) {
|
||||
return findHandler(doc).map(sth -> sth.semanticTokensRange(doc, range, cancelChecker)).orElse(SemanticTokensHandler.super.semanticTokensRange(doc, range, cancelChecker));
|
||||
}
|
||||
|
||||
private Optional<SemanticTokensHandler> findHandler(TextDocumentIdentifier docId) {
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docId.getUri());
|
||||
private Optional<SemanticTokensHandler> findHandler(TextDocument doc) {
|
||||
// Only opened docs ideally should get requests for semantic token to highlight
|
||||
if (doc != null) {
|
||||
LanguageId language = doc.getLanguageId();
|
||||
@@ -269,8 +251,6 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
|
||||
if (subComponents != null) {
|
||||
return subComponents.stream().filter(sc -> sc.getSemanticTokensHandler().isPresent()).map(sc -> sc.getSemanticTokensHandler().get()).findFirst();
|
||||
}
|
||||
} else {
|
||||
log.error("Received Semantic Tokens request for a non opened document: %s".formatted(docId.getUri()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -10,28 +10,22 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.semantic.tokens;
|
||||
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensDelta;
|
||||
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensRangeParams;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
public interface SemanticTokensHandler {
|
||||
|
||||
SemanticTokensWithRegistrationOptions getCapability();
|
||||
|
||||
default SemanticTokens semanticTokensFull(SemanticTokensParams params, CancelChecker cancelChecker) {
|
||||
default List<SemanticTokenData> semanticTokensFull(TextDocument doc, CancelChecker cancelChecker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default Either<SemanticTokens, SemanticTokensDelta> semanticTokensFullDelta(SemanticTokensDeltaParams params, CancelChecker cancelChecker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default SemanticTokens semanticTokensRange(SemanticTokensRangeParams params, CancelChecker cancelChecker) {
|
||||
default List<SemanticTokenData> semanticTokensRange(TextDocument doc, Range range, CancelChecker cancelChecker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,15 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.lsp4j.SemanticTokensLegend;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
public class SemanticTokensUtils {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(SemanticTokensUtils.class);
|
||||
|
||||
private static int getSemanticTokenTypeIndex(SemanticTokensLegend legend, String tokenType) {
|
||||
return legend.getTokenTypes().indexOf(tokenType);
|
||||
}
|
||||
@@ -59,5 +65,35 @@ public class SemanticTokensUtils {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static List<Integer> mapTokensDataToLsp(TextDocument doc, SemanticTokensLegend legend,
|
||||
List<SemanticTokenData> tokensData) {
|
||||
|
||||
// Sort tokens by start offset
|
||||
Collections.sort(tokensData);
|
||||
|
||||
List<Integer> data = new ArrayList<>(tokensData.size() * 5);
|
||||
|
||||
// Encode relative positions for tokens
|
||||
int previousLine = 0;
|
||||
int previousColumn = 0;
|
||||
for (SemanticTokenData tokenData : tokensData) {
|
||||
try {
|
||||
int currentLine = doc.getLineOfOffset(tokenData.start());
|
||||
int currentColumn = tokenData.start() - doc.getLineOffset(currentLine);
|
||||
data.add(currentLine - previousLine);
|
||||
data.add(currentLine == previousLine ? currentColumn - previousColumn : currentColumn);
|
||||
data.add(tokenData.end() - tokenData.start());
|
||||
data.add(SemanticTokensUtils.getSemanticTokenTypeIndex(legend, tokenData.type()));
|
||||
data.add(SemanticTokensUtils.getSemanticTokenModifiersFlags(legend, tokenData.modifiers()));
|
||||
previousLine = currentLine;
|
||||
previousColumn = currentColumn;
|
||||
} catch (BadLocationException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,8 +60,6 @@ import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.ReferenceParams;
|
||||
import org.eclipse.lsp4j.RenameParams;
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensDelta;
|
||||
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensRangeParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
|
||||
@@ -88,6 +86,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.semantic.tokens.SemanticTokensUtils;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
@@ -458,30 +457,34 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
|
||||
if (semanticTokensHandler == null) {
|
||||
return CompletableFuture.completedFuture(new SemanticTokens());
|
||||
} else {
|
||||
return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelChecker -> semanticTokensHandler.semanticTokensFull(params, cancelChecker));
|
||||
if (semanticTokensHandler != null) {
|
||||
TextDocument doc = getLatestSnapshot(params.getTextDocument().getUri());
|
||||
if (doc != null) {
|
||||
return CompletableFutures.computeAsync(/*messageWorkerThreadPool,*/ cancelChecker -> semanticTokensHandler.semanticTokensFull(doc, cancelChecker)).thenApply(std -> {
|
||||
if (std != null && !std.isEmpty()) {
|
||||
return new SemanticTokens(SemanticTokensUtils.mapTokensDataToLsp(doc, semanticTokensHandler.getCapability().getLegend(), std));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<SemanticTokens, SemanticTokensDelta>> semanticTokensFullDelta(
|
||||
SemanticTokensDeltaParams params) {
|
||||
if (semanticTokensHandler == null) {
|
||||
return CompletableFuture.completedFuture(Either.forLeft(new SemanticTokens()));
|
||||
} else {
|
||||
return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelChecker -> semanticTokensHandler.semanticTokensFullDelta(params, cancelChecker));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SemanticTokens> semanticTokensRange(SemanticTokensRangeParams params) {
|
||||
if (semanticTokensHandler == null) {
|
||||
return CompletableFuture.completedFuture(new SemanticTokens());
|
||||
} else {
|
||||
return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelChecker -> semanticTokensHandler.semanticTokensRange(params, cancelChecker));
|
||||
if (semanticTokensHandler != null) {
|
||||
TextDocument doc = getLatestSnapshot(params.getTextDocument().getUri());
|
||||
if (doc != null) {
|
||||
return CompletableFutures.computeAsync(/*messageWorkerThreadPool,*/ cancelChecker -> semanticTokensHandler.semanticTokensRange(doc, params.getRange(), cancelChecker)).thenApply(std -> {
|
||||
if (std != null && !std.isEmpty()) {
|
||||
return new SemanticTokens(SemanticTokensUtils.mapTokensDataToLsp(doc, semanticTokensHandler.getCapability().getLegend(), std));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private List<Either<Command, CodeAction>> computeCodeActions(CancelChecker cancelToken, CodeActionCapabilities capabilities, TextDocument doc, CodeActionParams params) {
|
||||
|
||||
Reference in New Issue
Block a user