Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ test {

configurations {
nextflowRuntime
specImplementation.extendsFrom(nextflowRuntime)
}

dependencies {
Expand All @@ -57,6 +56,7 @@ dependencies {
testImplementation ('org.objenesis:objenesis:3.4')
testImplementation ('net.bytebuddy:byte-buddy:1.14.17')
testImplementation ('org.spockframework:spock-core:2.3-groovy-4.0') { exclude group: 'org.apache.groovy' }
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

sourceSets {
Expand All @@ -67,6 +67,10 @@ sourceSets {
}
}

configurations {
specImplementation.extendsFrom(nextflowRuntime)
}

task buildSpec(type: JavaExec) {
description = 'Build spec file of core definitions'
group = 'build'
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
26 changes: 14 additions & 12 deletions src/main/java/nextflow/lsp/NextflowLanguageServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ public static void main(String[] args) {
private LanguageClient client = null;

private Map<String, String> workspaceRoots = new HashMap<>();
private Map<String, LanguageService> scriptServices = new HashMap<>();
private Map<String, LanguageService> configServices = new HashMap<>();
private Map<String, ConfigService> configServices = new HashMap<>();
private Map<String, ScriptService> scriptServices = new HashMap<>();

private LanguageServerConfiguration configuration = LanguageServerConfiguration.defaults();

Expand Down Expand Up @@ -461,6 +461,7 @@ public void didChangeConfiguration(DidChangeConfigurationParams params) {
withDefault(JsonUtils.getBoolean(settings, "nextflow.formatting.harshilAlignment"), configuration.harshilAlignment()),
withDefault(JsonUtils.getBoolean(settings, "nextflow.formatting.maheshForm"), configuration.maheshForm()),
withDefault(JsonUtils.getInteger(settings, "nextflow.completion.maxItems"), configuration.maxCompletionItems()),
withDefault(JsonUtils.getString(settings, "nextflow.pluginRegistryUrl"), configuration.pluginRegistryUrl()),
withDefault(JsonUtils.getBoolean(settings, "nextflow.formatting.sortDeclarations"), configuration.sortDeclarations()),
withDefault(JsonUtils.getBoolean(settings, "nextflow.typeChecking"), configuration.typeChecking())
);
Expand All @@ -483,6 +484,7 @@ private <T> T withDefault(T value, T defaultValue) {
private boolean shouldInitialize(LanguageServerConfiguration previous, LanguageServerConfiguration current) {
return previous.errorReportingMode() != current.errorReportingMode()
|| !DefaultGroovyMethods.equals(previous.excludePatterns(), current.excludePatterns())
|| previous.pluginRegistryUrl() != current.pluginRegistryUrl()
|| previous.typeChecking() != current.typeChecking();
}

Expand All @@ -500,8 +502,8 @@ private void initializeWorkspaces() {
progress.update(progressMessage, count * 100 / total);
count++;

scriptServices.get(name).initialize(configuration);
configServices.get(name).initialize(configuration);
scriptServices.get(name).initialize(configuration, configServices.get(name).getPluginSpecCache());
}

progress.end();
Expand All @@ -519,16 +521,16 @@ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
var name = workspaceFolder.getName();
log.debug("workspace/didChangeWorkspaceFolders remove " + name);
workspaceRoots.remove(name);
scriptServices.remove(name).clearDiagnostics();
configServices.remove(name).clearDiagnostics();
scriptServices.remove(name).clearDiagnostics();
}
for( var workspaceFolder : event.getAdded() ) {
var name = workspaceFolder.getName();
var uri = workspaceFolder.getUri();
log.debug("workspace/didChangeWorkspaceFolders add " + name + " " + uri);
addWorkspaceFolder(name, uri);
scriptServices.get(name).initialize(configuration);
configServices.get(name).initialize(configuration);
scriptServices.get(name).initialize(configuration, configServices.get(name).getPluginSpecCache());
}
}

Expand Down Expand Up @@ -620,13 +622,13 @@ public CompletableFuture<Either<List<? extends SymbolInformation>, List<? extend
private void addWorkspaceFolder(String name, String uri) {
workspaceRoots.put(name, uri);

var scriptService = new ScriptService(uri);
scriptService.connect(client);
scriptServices.put(name, scriptService);

var configService = new ConfigService(uri);
configService.connect(client);
configServices.put(name, configService);

var scriptService = new ScriptService(uri);
scriptService.connect(client);
scriptServices.put(name, scriptService);
}

private String relativePath(String uri) {
Expand Down Expand Up @@ -654,12 +656,12 @@ private LanguageService getLanguageService(String uri) {
return service;
}

private LanguageService getLanguageService0(String uri, Map<String, LanguageService> services) {
private LanguageService getLanguageService0(String uri, Map<String, ? extends LanguageService> services) {
var service = workspaceRoots.entrySet().stream()
.filter((entry) -> entry.getValue() != null && uri.startsWith(entry.getValue()))
.findFirst()
.map((entry) -> services.get(entry.getKey()))
.orElse(services.get(DEFAULT_WORKSPACE_FOLDER_NAME));
.map((entry) -> (LanguageService) services.get(entry.getKey()))
.orElse((LanguageService) services.get(DEFAULT_WORKSPACE_FOLDER_NAME));
if( service == null || !service.matchesFile(uri) )
return null;
return service;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public record LanguageServerConfiguration(
boolean harshilAlignment,
boolean maheshForm,
int maxCompletionItems,
String pluginRegistryUrl,
boolean sortDeclarations,
boolean typeChecking
) {
Expand All @@ -41,6 +42,7 @@ public static LanguageServerConfiguration defaults() {
false,
false,
100,
"https://registry.nextflow.io/api/",
false,
false
);
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/nextflow/lsp/services/config/ConfigAstCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Set;

import groovy.lang.GroovyClassLoader;
import nextflow.config.ast.ConfigNode;
import nextflow.config.control.ConfigResolveVisitor;
import nextflow.config.control.ResolveIncludeVisitor;
import nextflow.config.parser.ConfigParserPluginFactory;
Expand All @@ -30,6 +31,7 @@
import nextflow.lsp.compiler.LanguageServerErrorCollector;
import nextflow.lsp.file.FileCache;
import nextflow.lsp.services.LanguageServerConfiguration;
import nextflow.lsp.spec.PluginSpecCache;
import nextflow.script.control.PhaseAware;
import nextflow.script.control.Phases;
import nextflow.script.types.Types;
Expand All @@ -46,7 +48,7 @@ public class ConfigAstCache extends ASTNodeCache {

private LanguageServerConfiguration configuration;

private Map<String,SpecNode> spec = ConfigSpecFactory.defaultScopes();
private PluginSpecCache pluginSpecCache;

public ConfigAstCache() {
super(createCompiler());
Expand All @@ -65,8 +67,9 @@ private static CompilerConfiguration createConfiguration() {
return config;
}

public void initialize(LanguageServerConfiguration configuration) {
public void initialize(LanguageServerConfiguration configuration, PluginSpecCache pluginSpecCache) {
this.configuration = configuration;
this.pluginSpecCache = pluginSpecCache;
}

@Override
Expand All @@ -92,7 +95,7 @@ protected Set<URI> analyze(Set<URI> uris, FileCache fileCache) {
continue;
// phase 3: name checking
new ConfigResolveVisitor(sourceUnit, compiler().compilationUnit(), Types.DEFAULT_CONFIG_IMPORTS).visit();
new ConfigSpecVisitor(sourceUnit, spec, configuration.typeChecking()).visit();
new ConfigSpecVisitor(sourceUnit, pluginSpecCache, configuration.typeChecking()).visit();
if( sourceUnit.getErrorCollector().hasErrors() )
continue;
// phase 4: type checking
Expand Down Expand Up @@ -121,4 +124,8 @@ public boolean hasSyntaxErrors(URI uri) {
.isPresent();
}

public ConfigNode getConfigNode(URI uri) {
return (ConfigNode) getSourceUnit(uri).getAST();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import nextflow.config.ast.ConfigIncompleteNode;
import nextflow.config.dsl.ConfigDsl;
import nextflow.config.spec.SpecNode;
import nextflow.lsp.ast.ASTNodeCache;
import nextflow.lsp.ast.CompletionHelper;
import nextflow.lsp.services.CompletionProvider;
import nextflow.lsp.util.Logger;
Expand Down Expand Up @@ -62,14 +61,12 @@
*/
public class ConfigCompletionProvider implements CompletionProvider {

private static final List<CompletionItem> TOPLEVEL_ITEMS = topLevelItems();

private static Logger log = Logger.getInstance();

private ASTNodeCache ast;
private ConfigAstCache ast;
private CompletionHelper ch;

public ConfigCompletionProvider(ASTNodeCache ast, int maxItems) {
public ConfigCompletionProvider(ConfigAstCache ast, int maxItems) {
this.ast = ast;
this.ch = new CompletionHelper(maxItems);
}
Expand All @@ -86,17 +83,18 @@ public Either<List<CompletionItem>, CompletionList> completion(TextDocumentIdent
return Either.forLeft(Collections.emptyList());

var nodeStack = ast.getNodesAtPosition(uri, position);
var spec = ast.getConfigNode(uri).getSpec();
if( nodeStack.isEmpty() )
return Either.forLeft(TOPLEVEL_ITEMS);
return Either.forLeft(topLevelItems(spec));

if( isConfigExpression(nodeStack) ) {
addCompletionItems(nodeStack);
}
else {
var names = currentConfigScope(nodeStack);
if( names.isEmpty() )
return Either.forLeft(TOPLEVEL_ITEMS);
addConfigOptions(names);
return Either.forLeft(topLevelItems(spec));
addConfigOptions(names, spec);
}

return ch.isIncomplete()
Expand Down Expand Up @@ -179,8 +177,8 @@ private static List<String> currentConfigScope(List<ASTNode> nodeStack) {
return names;
}

private void addConfigOptions(List<String> names) {
var scope = SpecNode.ROOT.getScope(names);
private void addConfigOptions(List<String> names, SpecNode.Scope spec) {
var scope = spec.getScope(names);
if( scope == null )
return;
scope.children().forEach((name, child) -> {
Expand All @@ -191,9 +189,9 @@ private void addConfigOptions(List<String> names) {
});
}

private static List<CompletionItem> topLevelItems() {
private static List<CompletionItem> topLevelItems(SpecNode.Scope spec) {
var result = new ArrayList<CompletionItem>();
SpecNode.ROOT.children().forEach((name, child) -> {
spec.children().forEach((name, child) -> {
if( child instanceof SpecNode.Option option ) {
result.add(configOption(name, option.description(), option.type()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import nextflow.config.ast.ConfigAssignNode;
import nextflow.config.ast.ConfigBlockNode;
import nextflow.config.spec.SpecNode;
import nextflow.lsp.ast.ASTNodeCache;
import nextflow.lsp.ast.ASTNodeStringUtils;
import nextflow.lsp.ast.LanguageServerASTUtils;
import nextflow.lsp.services.HoverProvider;
Expand All @@ -49,9 +48,9 @@ public class ConfigHoverProvider implements HoverProvider {

private static Logger log = Logger.getInstance();

private ASTNodeCache ast;
private ConfigAstCache ast;

public ConfigHoverProvider(ASTNodeCache ast) {
public ConfigHoverProvider(ConfigAstCache ast) {
this.ast = ast;
}

Expand All @@ -69,7 +68,7 @@ public Hover hover(TextDocumentIdentifier textDocument, Position position) {

var builder = new StringBuilder();

var content = getHoverContent(nodeStack);
var content = getHoverContent(nodeStack, ast.getConfigNode(uri).getSpec());
if( content != null ) {
builder.append(content);
builder.append('\n');
Expand All @@ -94,14 +93,14 @@ public Hover hover(TextDocumentIdentifier textDocument, Position position) {
return new Hover(new MarkupContent(MarkupKind.MARKDOWN, value));
}

protected String getHoverContent(List<ASTNode> nodeStack) {
protected String getHoverContent(List<ASTNode> nodeStack, SpecNode.Scope spec) {
var offsetNode = nodeStack.get(0);
if( offsetNode instanceof ConfigAssignNode assign ) {
var names = getCurrentScope(nodeStack);
names.addAll(assign.names);

var fqName = String.join(".", names);
var option = SpecNode.ROOT.getOption(names);
var option = spec.getOption(names);
if( option != null ) {
var description = StringGroovyMethods.stripIndent(option.description(), true).trim();
var builder = new StringBuilder();
Expand All @@ -120,7 +119,7 @@ else if( Logger.isDebugEnabled() ) {
if( names.isEmpty() )
return null;

var scope = SpecNode.ROOT.getChild(names);
var scope = spec.getChild(names);
if( scope != null ) {
return StringGroovyMethods.stripIndent(scope.description(), true).trim();
}
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/nextflow/lsp/services/config/ConfigService.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import nextflow.lsp.services.LanguageService;
import nextflow.lsp.services.LinkProvider;
import nextflow.lsp.services.SemanticTokensProvider;
import nextflow.lsp.spec.PluginSpecCache;

/**
* Implementation of language services for Nextflow config files.
Expand All @@ -31,6 +32,8 @@
*/
public class ConfigService extends LanguageService {

private PluginSpecCache pluginSpecCache;

private ConfigAstCache astCache;

public ConfigService(String rootUri) {
Expand All @@ -45,12 +48,21 @@ public boolean matchesFile(String uri) {

@Override
public void initialize(LanguageServerConfiguration configuration) {
initialize(configuration, new PluginSpecCache(configuration.pluginRegistryUrl()));
}

public void initialize(LanguageServerConfiguration configuration, PluginSpecCache pluginSpecCache) {
synchronized (this) {
astCache.initialize(configuration);
this.pluginSpecCache = pluginSpecCache;
astCache.initialize(configuration, pluginSpecCache);
}
super.initialize(configuration);
}

public PluginSpecCache getPluginSpecCache() {
return pluginSpecCache;
}

@Override
protected ASTNodeCache getAstCache() {
return astCache;
Expand Down
Loading