Skip to content
Draft
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
48 changes: 24 additions & 24 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ test {

configurations {
nextflowRuntime
schemaImplementation.extendsFrom(nextflowRuntime)
coreSpecImplementation.extendsFrom(nextflowRuntime)
}

dependencies {
implementation 'io.nextflow:nf-lang:25.07.0-edge'
implementation 'io.nextflow:nf-lang:25.08.0-edge'
implementation 'org.apache.groovy:groovy:4.0.28'
implementation 'org.apache.groovy:groovy-json:4.0.28'
implementation 'org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.0'
Expand All @@ -45,37 +45,37 @@ dependencies {
runtimeOnly 'org.yaml:snakeyaml:2.2'

// include Nextflow runtime at build-time to extract language definitions
nextflowRuntime 'io.nextflow:nextflow:25.07.0-edge'
nextflowRuntime 'io.nextflow:nf-amazon:3.1.0'
nextflowRuntime 'io.nextflow:nf-azure:1.19.0'
nextflowRuntime 'io.nextflow:nf-google:1.22.2'
nextflowRuntime 'io.nextflow:nf-k8s:1.1.1'
nextflowRuntime 'io.nextflow:nf-tower:1.14.0'
nextflowRuntime 'io.nextflow:nf-wave:1.14.1'
nextflowRuntime 'io.nextflow:nextflow:25.08.0-edge'
nextflowRuntime 'io.nextflow:nf-amazon:3.2.0'
nextflowRuntime 'io.nextflow:nf-azure:1.20.0'
nextflowRuntime 'io.nextflow:nf-google:1.23.0'
nextflowRuntime 'io.nextflow:nf-k8s:1.2.0'
nextflowRuntime 'io.nextflow:nf-tower:1.15.0'
nextflowRuntime 'io.nextflow:nf-wave:1.15.0'

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' }
}

sourceSets {
schema {
groovy.srcDir 'src/schema/groovy'
coreSpec {
groovy.srcDir 'src/spec/groovy'
compileClasspath += configurations.nextflowRuntime
runtimeClasspath += configurations.nextflowRuntime
}
}

task buildSchema(type: JavaExec) {
description = 'Build JSON schema of Nextflow configuration'
task buildCoreSpec(type: JavaExec) {
description = 'Build spec of core definitions'
group = 'build'

dependsOn compileSchemaGroovy
classpath = sourceSets.schema.runtimeClasspath
mainClass.set('nextflow.SchemaRenderer')
args "$buildDir/generated/index.json"
dependsOn compileCoreSpecGroovy
classpath = sourceSets.coreSpec.runtimeClasspath
mainClass.set('nextflow.SpecRenderer')
args "$buildDir/generated/definitions.json"

outputs.file("$buildDir/generated/index.json")
outputs.file("$buildDir/generated/definitions.json")
outputs.cacheIf { true }

doFirst {
Expand All @@ -84,17 +84,17 @@ task buildSchema(type: JavaExec) {
}

processResources {
dependsOn buildSchema
from(buildSchema.outputs.files) {
into 'schema'
dependsOn buildCoreSpec
from(buildCoreSpec.outputs.files) {
into 'spec'
}
}

jar {
dependsOn buildSchema
dependsOn buildCoreSpec
from("$buildDir/generated") {
include 'index.json'
into 'schema'
include 'definitions.json'
into 'spec'
}
}

Expand Down
25 changes: 13 additions & 12 deletions src/main/java/nextflow/lsp/NextflowLanguageServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,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 @@ -458,6 +458,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 Down Expand Up @@ -497,8 +498,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 @@ -516,16 +517,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 @@ -585,13 +586,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 @@ -619,12 +620,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 SchemaNode.Scope schema = ConfigSchemaFactory.load();
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 ConfigSchemaVisitor(sourceUnit, schema, configuration.typeChecking()).visit();
new ConfigSchemaVisitor(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.schema.SchemaNode;
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 schema = ast.getConfigNode(uri).getSchema();
if( nodeStack.isEmpty() )
return Either.forLeft(TOPLEVEL_ITEMS);
return Either.forLeft(topLevelItems(schema));

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

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 = SchemaNode.ROOT.getScope(names);
private void addConfigOptions(List<String> names, SchemaNode.Scope schema) {
var scope = schema.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(SchemaNode.Scope schema) {
var result = new ArrayList<CompletionItem>();
SchemaNode.ROOT.children().forEach((name, child) -> {
schema.children().forEach((name, child) -> {
if( child instanceof SchemaNode.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.schema.SchemaNode;
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).getSchema());
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, SchemaNode.Scope schema) {
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 = SchemaNode.ROOT.getOption(names);
var option = schema.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 = SchemaNode.ROOT.getChild(names);
var scope = schema.getChild(names);
if( scope != null ) {
return StringGroovyMethods.stripIndent(scope.description(), true).trim();
}
Expand Down
Loading
Loading