Skip to content

Commit

Permalink
Semantic highlighting (#92)
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Sherman <[email protected]>
  • Loading branch information
bentsherman authored Jan 10, 2025
1 parent 96e73bc commit dd1e9b7
Show file tree
Hide file tree
Showing 13 changed files with 637 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The following language features are currently supported:
- formatting
- hover hints
- rename
- semantic highlighting
- DAG preview for workflows

## Requirements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@

import static nextflow.config.parser.ConfigParser.*;
import static nextflow.script.parser.PositionConfigureUtils.ast;
import static nextflow.script.parser.PositionConfigureUtils.tokenPosition;
import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
import static org.codehaus.groovy.ast.tools.GeneralUtils.*;

Expand Down Expand Up @@ -1173,6 +1174,7 @@ private Parameter formalParameter(FormalParameterContext ctx) {
: null;
var result = ast( param(type, name, defaultValue), ctx );
checkInvalidVarName(name, result);
result.putNodeMetaData("_START_NAME", tokenPosition(ctx.identifier()));
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,17 @@ public static <T extends ASTNode> T ast(T astNode, ASTNode start, ASTNode stop)

return astNode;
}

/**
* Get the zero-based start position (line, character) of a token.
*
* @param ctx
*/
public static TokenPosition tokenPosition(ParserRuleContext ctx) {
var token = ctx.getStart();
return new TokenPosition(
token.getLine() - 1,
token.getCharPositionInLine()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import org.codehaus.groovy.syntax.Types;

import static nextflow.script.parser.PositionConfigureUtils.ast;
import static nextflow.script.parser.PositionConfigureUtils.tokenPosition;
import static nextflow.script.parser.ScriptParser.*;
import static nextflow.script.ast.ASTHelpers.*;
import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
Expand Down Expand Up @@ -339,7 +340,11 @@ private IncludeNode includeDeclaration(IncludeDeclarationContext ctx) {
.map(it -> {
var name = it.name.getText();
var alias = it.alias != null ? it.alias.getText() : null;
return ast( new IncludeVariable(name, alias), it );
var result = new IncludeVariable(name, alias);
result.putNodeMetaData("_START_NAME", tokenPosition(it.name));
if( it.alias != null )
result.putNodeMetaData("_START_ALIAS", tokenPosition(it.alias));
return ast( result, it );
})
.collect(Collectors.toList());

Expand Down Expand Up @@ -1525,6 +1530,7 @@ private Parameter formalParameter(FormalParameterContext ctx) {
: null;
var result = ast( param(type, name, defaultValue), ctx );
checkInvalidVarName(name, result);
result.putNodeMetaData("_START_NAME", tokenPosition(ctx.identifier()));
return result;
}

Expand Down
25 changes: 25 additions & 0 deletions modules/compiler/src/main/java/script/parser/TokenPosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2013-2025, Seqera Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nextflow.script.parser;

/**
*
* @author Ben Sherman <[email protected]>
*/
public record TokenPosition(
int line, // 0-based
int character // 0-based
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import nextflow.lsp.file.PathUtils;
import nextflow.lsp.util.Logger;
import nextflow.lsp.services.LanguageService;
import nextflow.lsp.services.SemanticTokensVisitor;
import nextflow.lsp.services.config.ConfigService;
import nextflow.lsp.services.script.ScriptService;
import nextflow.script.formatter.FormattingOptions;
Expand Down Expand Up @@ -76,6 +77,10 @@
import org.eclipse.lsp4j.ProgressParams;
import org.eclipse.lsp4j.ReferenceParams;
import org.eclipse.lsp4j.RenameParams;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensLegend;
import org.eclipse.lsp4j.SemanticTokensParams;
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.SetTraceParams;
import org.eclipse.lsp4j.SymbolInformation;
Expand Down Expand Up @@ -164,6 +169,14 @@ public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
serverCapabilities.setExecuteCommandProvider(executeCommandOptions);
serverCapabilities.setHoverProvider(true);
serverCapabilities.setReferencesProvider(true);
var semanticTokensOptions = new SemanticTokensWithRegistrationOptions(
new SemanticTokensLegend(
SemanticTokensVisitor.TOKEN_TYPES,
Collections.emptyList()
),
true,
false);
serverCapabilities.setSemanticTokensProvider(semanticTokensOptions);
serverCapabilities.setRenameProvider(true);
serverCapabilities.setWorkspaceSymbolProvider(true);

Expand Down Expand Up @@ -408,6 +421,19 @@ public CompletableFuture<WorkspaceEdit> rename(RenameParams params) {
});
}

@Override
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
return CompletableFutures.computeAsync((cancelChecker) -> {
cancelChecker.checkCanceled();
var uri = params.getTextDocument().getUri();
log.debug("textDocument/semanticTokens/full " + relativePath(uri));
var service = getLanguageService(uri);
if( service == null )
return null;
return service.semanticTokensFull(params);
});
}

// --- WorkspaceService

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.ReferenceParams;
import org.eclipse.lsp4j.RenameParams;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.WorkspaceEdit;
Expand Down Expand Up @@ -116,6 +118,7 @@ public LanguageService() {
protected LinkProvider getLinkProvider() { return null; }
protected ReferenceProvider getReferenceProvider() { return null; }
protected RenameProvider getRenameProvider() { return null; }
protected SemanticTokensProvider getSemanticTokensProvider() { return null; }
protected SymbolProvider getSymbolProvider() { return null; }

private volatile boolean initialized;
Expand Down Expand Up @@ -295,6 +298,15 @@ public WorkspaceEdit rename(RenameParams params) {
return provider.rename(params.getTextDocument(), params.getPosition(), params.getNewName());
}

public SemanticTokens semanticTokensFull(SemanticTokensParams params) {
var provider = getSemanticTokensProvider();
if( provider == null )
return null;

awaitUpdate();
return provider.semanticTokensFull(params.getTextDocument());
}

public List<? extends WorkspaceSymbol> symbol(WorkspaceSymbolParams params) {
var provider = getSymbolProvider();
if( provider == null )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024, Seqera Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nextflow.lsp.services;

import java.util.List;

import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.TextDocumentIdentifier;

public interface SemanticTokensProvider {

SemanticTokens semanticTokensFull(TextDocumentIdentifier textDocument);

}
Loading

0 comments on commit dd1e9b7

Please sign in to comment.