Skip to content

Commit b3db41d

Browse files
committed
Record types
1 parent f805371 commit b3db41d

13 files changed

Lines changed: 420 additions & 87 deletions

build.gradle

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ configurations {
3333
}
3434

3535
dependencies {
36-
implementation 'io.nextflow:nf-lang:26.01.0-edge'
36+
implementation 'io.nextflow:nf-lang:26.02.0-edge'
3737
implementation 'org.apache.groovy:groovy:4.0.30'
3838
implementation 'org.apache.groovy:groovy-json:4.0.30'
3939
implementation 'org.apache.groovy:groovy-yaml:4.0.30'
@@ -45,12 +45,12 @@ dependencies {
4545
runtimeOnly 'org.yaml:snakeyaml:2.2'
4646

4747
// include Nextflow runtime at build-time to extract language definitions
48-
nextflowRuntime 'io.nextflow:nextflow:26.01.0-edge'
49-
nextflowRuntime 'io.nextflow:nf-amazon:3.7.0'
50-
nextflowRuntime 'io.nextflow:nf-azure:1.21.0'
51-
nextflowRuntime 'io.nextflow:nf-google:1.26.0'
48+
nextflowRuntime 'io.nextflow:nextflow:26.02.0-edge'
49+
nextflowRuntime 'io.nextflow:nf-amazon:3.7.1'
50+
nextflowRuntime 'io.nextflow:nf-azure:1.22.0'
51+
nextflowRuntime 'io.nextflow:nf-google:1.26.1'
5252
nextflowRuntime 'io.nextflow:nf-k8s:1.5.0'
53-
nextflowRuntime 'io.nextflow:nf-tower:1.20.0'
53+
nextflowRuntime 'io.nextflow:nf-tower:1.21.0'
5454
nextflowRuntime 'io.nextflow:nf-wave:1.18.0'
5555

5656
testImplementation ('org.objenesis:objenesis:3.4')

src/main/java/nextflow/lsp/ast/ASTNodeStringUtils.java

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import nextflow.script.ast.ProcessNode;
2929
import nextflow.script.ast.ProcessNodeV1;
3030
import nextflow.script.ast.ProcessNodeV2;
31+
import nextflow.script.ast.RecordNode;
3132
import nextflow.script.ast.TupleParameter;
3233
import nextflow.script.ast.WorkflowNode;
3334
import nextflow.script.dsl.Constant;
@@ -93,7 +94,9 @@ public static String getLabel(ASTNode node) {
9394

9495
private static String classToLabel(ClassNode node) {
9596
var builder = new StringBuilder();
96-
if( node.isEnum() )
97+
if( node instanceof RecordNode )
98+
builder.append("record ");
99+
else if( node.isEnum() )
97100
builder.append("enum ");
98101
else
99102
builder.append("class ");
@@ -158,11 +161,11 @@ private static void typedOutput(Expression output, Formatter fmt) {
158161
var type = getType(target);
159162
if( fmt.hasType(type) ) {
160163
fmt.append(": ");
161-
fmt.visitTypeAnnotation(type);
164+
fmt.append(TypesEx.getName(type));
162165
}
163166
}
164167
else {
165-
fmt.visitTypeAnnotation(getType(output));
168+
fmt.append(TypesEx.getName(getType(output)));
166169
}
167170
}
168171

@@ -181,20 +184,23 @@ private static String processToLabel(ProcessNodeV2 node) {
181184
for( var input : node.inputs ) {
182185
fmt.appendIndent();
183186
if( input instanceof TupleParameter tp ) {
187+
var components = Arrays.stream(tp.components)
188+
.map(p -> p.getName())
189+
.collect(Collectors.joining(", "));
184190
fmt.append('(');
185-
fmt.append(
186-
Arrays.stream(tp.components)
187-
.map(p -> p.getName())
188-
.collect(Collectors.joining(", "))
189-
);
191+
fmt.append(components);
190192
fmt.append(')');
191193
}
192194
else {
193195
fmt.append(input.getName());
194196
}
195197
if( fmt.hasType(input) ) {
196198
fmt.append(": ");
197-
fmt.visitTypeAnnotation(input.getType());
199+
var type = input.getType();
200+
if( type.getNameWithoutPackage().startsWith("__Record") )
201+
processRecordToLabel(type.redirect(), fmt);
202+
else
203+
fmt.visitTypeAnnotation(type);
198204
}
199205
fmt.appendNewLine();
200206
}
@@ -217,6 +223,24 @@ private static String processToLabel(ProcessNodeV2 node) {
217223
return fmt.toString();
218224
}
219225

226+
private static void processRecordToLabel(ClassNode type, Formatter fmt) {
227+
fmt.append("Record {");
228+
fmt.appendNewLine();
229+
fmt.incIndent();
230+
for( var fn : type.getFields() ) {
231+
fmt.appendIndent();
232+
fmt.append(fn.getName());
233+
if( fmt.hasType(fn) ) {
234+
fmt.append(": ");
235+
fmt.append(TypesEx.getName(fn.getType()));
236+
}
237+
fmt.appendNewLine();
238+
}
239+
fmt.decIndent();
240+
fmt.appendIndent();
241+
fmt.append('}');
242+
}
243+
220244
private static String processToLabel(ProcessNodeV1 node) {
221245
var fmt = new Formatter(new FormattingOptions(2, true));
222246
fmt.append("process ");

src/main/java/nextflow/lsp/services/script/ProcessConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private Parameter typedInput(MethodCallExpression call, List<Statement> stagers)
145145
param = nextParam(type);
146146
var stageName = stageName(call);
147147
if( stageName != null ) {
148-
var stageValue = constX(param.getName());
148+
var stageValue = varX(param.getName());
149149
var stager = stmt(callThisX("stageAs", args(stageName, stageValue)));
150150
stagers.add(stager);
151151
}

src/main/java/nextflow/lsp/services/script/ScriptAstCache.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import nextflow.script.ast.FunctionNode;
3535
import nextflow.script.ast.IncludeNode;
3636
import nextflow.script.ast.ProcessNode;
37+
import nextflow.script.ast.RecordNode;
3738
import nextflow.script.ast.ScriptNode;
3839
import nextflow.script.ast.WorkflowNode;
3940
import nextflow.script.control.ModuleResolver;
@@ -45,8 +46,8 @@
4546
import nextflow.script.parser.ScriptParserPluginFactory;
4647
import nextflow.script.types.Types;
4748
import org.codehaus.groovy.ast.ASTNode;
49+
import org.codehaus.groovy.ast.AnnotatedNode;
4850
import org.codehaus.groovy.ast.ClassNode;
49-
import org.codehaus.groovy.ast.MethodNode;
5051
import org.codehaus.groovy.control.CompilerConfiguration;
5152
import org.codehaus.groovy.control.SourceUnit;
5253
import org.codehaus.groovy.control.messages.WarningMessage;
@@ -134,7 +135,11 @@ protected Set<URI> analyze(Set<URI> uris, FileCache fileCache) {
134135
// phase 3: name checking
135136
new ScriptResolveVisitor(sourceUnit, compiler().compilationUnit(), Types.DEFAULT_SCRIPT_IMPORTS, libImports).visit();
136137
new ParameterSchemaVisitor(sourceUnit).visit();
137-
if( sourceUnit.getErrorCollector().hasErrors() )
138+
}
139+
140+
for( var uri : changedUris ) {
141+
var sourceUnit = getSourceUnit(uri);
142+
if( sourceUnit == null || sourceUnit.getErrorCollector().hasErrors() )
138143
continue;
139144
// phase 4: type checking
140145
new TypeCheckingVisitorEx(sourceUnit, configuration.typeChecking()).visit();
@@ -186,19 +191,21 @@ public List<IncludeNode> getIncludeNodes(URI uri) {
186191
return scriptNode.getIncludes();
187192
}
188193

189-
public List<MethodNode> getDefinitions() {
190-
var result = new ArrayList<MethodNode>();
194+
public List<AnnotatedNode> getDefinitions() {
195+
var result = new ArrayList<AnnotatedNode>();
191196
result.addAll(getFunctionNodes());
192197
result.addAll(getProcessNodes());
193198
result.addAll(getWorkflowNodes());
199+
result.addAll(getTypeNodes());
194200
return result;
195201
}
196202

197-
public List<MethodNode> getDefinitions(URI uri) {
198-
var result = new ArrayList<MethodNode>();
203+
public List<AnnotatedNode> getDefinitions(URI uri) {
204+
var result = new ArrayList<AnnotatedNode>();
199205
result.addAll(getFunctionNodes(uri));
200206
result.addAll(getProcessNodes(uri));
201207
result.addAll(getWorkflowNodes(uri));
208+
result.addAll(getTypeNodes(uri));
202209
return result;
203210
}
204211

src/main/java/nextflow/lsp/services/script/ScriptAstParentVisitor.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
import nextflow.script.ast.ParamBlockNode;
2727
import nextflow.script.ast.ProcessNodeV1;
2828
import nextflow.script.ast.ProcessNodeV2;
29+
import nextflow.script.ast.RecordNode;
2930
import nextflow.script.ast.ScriptNode;
3031
import nextflow.script.ast.ScriptVisitorSupport;
31-
import nextflow.script.ast.TupleParameter;
3232
import nextflow.script.ast.WorkflowNode;
3333
import org.codehaus.groovy.ast.ASTNode;
3434
import org.codehaus.groovy.ast.ClassNode;
@@ -172,6 +172,17 @@ public void visitFunction(FunctionNode node) {
172172
}
173173
}
174174

175+
@Override
176+
public void visitRecord(RecordNode node) {
177+
lookup.push(node);
178+
try {
179+
super.visitRecord(node);
180+
}
181+
finally {
182+
lookup.pop();
183+
}
184+
}
185+
175186
@Override
176187
public void visitEnum(ClassNode node) {
177188
lookup.push(node);

src/main/java/nextflow/lsp/services/script/ScriptCompletionProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ private void addItemsFromScope(VariableScope scope, String namePrefix, ASTNode d
152152
ch.addTypes(ast.getTypeNodes(uri), namePrefix);
153153

154154
if( !extended ) {
155-
addIncludes(namePrefix);
155+
addIncludedMethods(namePrefix);
156156
return;
157157
}
158158
if( declarationNode instanceof FunctionNode || declarationNode instanceof ProcessNode || declarationNode instanceof OutputNode ) {
@@ -165,11 +165,11 @@ private void addItemsFromScope(VariableScope scope, String namePrefix, ASTNode d
165165
}
166166
}
167167

168-
private void addIncludes(String namePrefix) {
168+
private void addIncludedMethods(String namePrefix) {
169169
for( var includeNode : ast.getIncludeNodes(uri) ) {
170170
for( var entry : includeNode.entries ) {
171171
var node = entry.getTarget();
172-
if( node == null || ast.getURI(node) == null )
172+
if( !(node instanceof MethodNode) || ast.getURI(node) == null )
173173
continue;
174174

175175
var name = entry.getNameOrAlias();

src/main/java/nextflow/lsp/services/script/ScriptSemanticTokensProvider.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,12 @@ public void visitFeatureFlag(FeatureFlagNode node) {
109109
@Override
110110
public void visitInclude(IncludeNode node) {
111111
for( var entry : node.entries ) {
112-
tok.append(entry.getNodeMetaData("_START_NAME"), entry.name, SemanticTokenTypes.Function);
112+
var type = entry.getTarget() instanceof ClassNode
113+
? SemanticTokenTypes.Type
114+
: SemanticTokenTypes.Function;
115+
tok.append(entry.getNodeMetaData("_START_NAME"), entry.name, type);
113116
if( entry.alias != null )
114-
tok.append(entry.getNodeMetaData("_START_ALIAS"), entry.alias, SemanticTokenTypes.Function);
117+
tok.append(entry.getNodeMetaData("_START_ALIAS"), entry.alias, type);
115118
}
116119
}
117120

src/main/java/nextflow/lsp/services/script/ScriptSymbolProvider.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import nextflow.lsp.util.Logger;
2626
import nextflow.script.ast.FunctionNode;
2727
import nextflow.script.ast.ProcessNode;
28+
import nextflow.script.ast.RecordNode;
2829
import nextflow.script.ast.WorkflowNode;
2930
import org.codehaus.groovy.ast.ASTNode;
3031
import org.codehaus.groovy.ast.ClassNode;
@@ -108,6 +109,8 @@ private void addWorkspaceSymbol(ASTNode node, String query, List<WorkspaceSymbol
108109
}
109110

110111
private static String getSymbolName(ASTNode node) {
112+
if( node instanceof RecordNode rn )
113+
return "record " + rn.getName();
111114
if( node instanceof ClassNode cn && cn.isEnum() )
112115
return "enum " + cn.getName();
113116
if( node instanceof FunctionNode fn )
@@ -122,9 +125,11 @@ private static String getSymbolName(ASTNode node) {
122125
}
123126

124127
private static SymbolKind getSymbolKind(ASTNode node) {
128+
if( node instanceof RecordNode )
129+
return SymbolKind.Struct;
125130
if( node instanceof ClassNode cn && cn.isEnum() )
126131
return SymbolKind.Enum;
127-
if( node instanceof MethodNode mn )
132+
if( node instanceof MethodNode )
128133
return SymbolKind.Function;
129134
return null;
130135
}

0 commit comments

Comments
 (0)