2020import java .util .stream .Stream ;
2121
2222import groovy .lang .groovydoc .Groovydoc ;
23+ import groovy .transform .NamedParams ;
2324import nextflow .lsp .util .Logger ;
2425import nextflow .script .ast .AssignmentExpression ;
2526import nextflow .script .ast .FeatureFlagNode ;
3435import nextflow .script .dsl .DslScope ;
3536import nextflow .script .dsl .FeatureFlag ;
3637import nextflow .script .dsl .Namespace ;
37- import nextflow .script .dsl .Operator ;
38- import nextflow .script .dsl .OutputDsl ;
3938import nextflow .script .dsl .ProcessDsl ;
4039import nextflow .script .formatter .FormattingOptions ;
4140import nextflow .script .formatter .Formatter ;
42- import nextflow .script .types .TypeChecker ;
4341import nextflow .script .types .TypesEx ;
4442import org .codehaus .groovy .ast .AnnotatedNode ;
4543import org .codehaus .groovy .ast .ASTNode ;
4947import org .codehaus .groovy .ast .MethodNode ;
5048import org .codehaus .groovy .ast .Parameter ;
5149import org .codehaus .groovy .ast .Variable ;
50+ import org .codehaus .groovy .ast .expr .ClassExpression ;
5251import org .codehaus .groovy .ast .expr .Expression ;
5352import org .codehaus .groovy .ast .expr .VariableExpression ;
5453import org .codehaus .groovy .ast .stmt .ExpressionStatement ;
5554import org .codehaus .groovy .runtime .StringGroovyMethods ;
5655
5756import static nextflow .script .ast .ASTUtils .*;
57+ import static nextflow .script .types .TypeCheckingUtils .*;
5858
5959/**
6060 * Utility methods for retreiving text information for ast nodes.
@@ -82,6 +82,9 @@ public static String getLabel(ASTNode node) {
8282 if ( node instanceof MethodNode mn )
8383 return methodToLabel (mn );
8484
85+ if ( node instanceof Parameter param )
86+ return parameterToLabel (param );
87+
8588 if ( node instanceof Variable var )
8689 return variableToLabel (var );
8790
@@ -150,15 +153,16 @@ private static String workflowToLabel(WorkflowNode node) {
150153
151154 private static void typedOutput (Expression output , Formatter fmt ) {
152155 if ( output instanceof AssignmentExpression assign ) {
153- var target = (VariableExpression ) assign .getLeftExpression ();
154- fmt .append (target .getText ());
155- if ( fmt .hasType (target ) ) {
156+ var target = assign .getLeftExpression ();
157+ fmt .visit (target );
158+ var type = getType (target );
159+ if ( fmt .hasType (type ) ) {
156160 fmt .append (": " );
157- fmt .visitTypeAnnotation (target . getType () );
161+ fmt .visitTypeAnnotation (type );
158162 }
159163 }
160164 else {
161- fmt .visit ( output );
165+ fmt .visitTypeAnnotation ( getType ( output ) );
162166 }
163167 }
164168
@@ -258,7 +262,7 @@ private static String methodToLabel(MethodNode node) {
258262 if ( TypesEx .isNamespace (node ) )
259263 return "(namespace) " + name ;
260264 var fn = new FieldNode (name , 0xF , node .getReturnType (), node .getDeclaringClass (), null );
261- return variableToLabel (fn );
265+ return parameterToLabel (fn );
262266 }
263267
264268 var label = methodTypeLabel (node );
@@ -271,17 +275,18 @@ private static String methodToLabel(MethodNode node) {
271275 return builder .toString ();
272276 }
273277
278+ var declaringType = node .getDeclaringClass ();
274279 var builder = new StringBuilder ();
275280 if ( node instanceof FunctionNode ) {
276281 builder .append ("def " );
277282 }
278- else if ( ! isDslFunction ( node ) && ! isNamespaceFunction ( node ) ) {
279- builder .append (TypesEx .getName (node . getDeclaringClass () ));
283+ else if ( isDeclaringTypeVisible ( declaringType ) ) {
284+ builder .append (TypesEx .getName (declaringType ));
280285 builder .append (' ' );
281286 }
282287 else if ( Logger .isDebugEnabled () ) {
283288 builder .append ('[' );
284- builder .append (TypesEx .getName (node . getDeclaringClass () ));
289+ builder .append (TypesEx .getName (declaringType ));
285290 builder .append ("] " );
286291 }
287292 builder .append (node .getName ());
@@ -295,49 +300,63 @@ else if( Logger.isDebugEnabled() ) {
295300 return builder .toString ();
296301 }
297302
298- private static boolean isDslFunction ( MethodNode mn ) {
299- return mn . getDeclaringClass (). implementsInterface (ClassHelper .makeCached (DslScope .class ));
300- }
301-
302- private static boolean isNamespaceFunction ( MethodNode mn ) {
303- return mn . getDeclaringClass (). implementsInterface ( ClassHelper . makeCached ( Namespace . class )) ;
303+ private static boolean isDeclaringTypeVisible ( ClassNode declaringType ) {
304+ if ( declaringType . implementsInterface (ClassHelper .makeCached (DslScope .class )) )
305+ return false ;
306+ if ( declaringType . implementsInterface ( ClassHelper . makeCached ( Namespace . class )) )
307+ return false ;
308+ return true ;
304309 }
305310
306311 private static String methodTypeLabel (MethodNode mn ) {
307312 if ( mn instanceof FunctionNode )
308313 return null ;
309- if ( findAnnotation (mn , Operator .class ).isPresent () )
310- return "operator" ;
311314 var cn = mn .getDeclaringClass ();
312315 if ( cn .isPrimaryClassNode () )
313316 return null ;
314317 var type = cn .getTypeClass ();
315- if ( type == ProcessDsl .DirectiveDsl .class )
316- return "process directive" ;
317318 if ( type == ProcessDsl .InputDslV1 .class )
318319 return "process input" ;
319320 if ( type == ProcessDsl .OutputDslV1 .class )
320321 return "process output" ;
321- if ( type == OutputDsl .class )
322- return "output directive" ;
323- if ( type == OutputDsl .IndexDsl .class )
324- return "output index directive" ;
325322 return null ;
326323 }
327324
328325 public static String parametersToLabel (Parameter [] params ) {
329- return Stream .of (params )
330- .map (param -> variableToLabel (param ))
331- .collect (Collectors .joining (", " ));
326+ var hasNamedParams = params .length > 0 && findAnnotation (params [0 ], NamedParams .class ).isPresent ();
327+ var builder = new StringBuilder ();
328+ for ( int i = hasNamedParams ? 1 : 0 ; i < params .length ; i ++ ) {
329+ builder .append (parameterToLabel (params [i ]));
330+ if ( i < params .length - 1 || hasNamedParams )
331+ builder .append (", " );
332+ }
333+ if ( hasNamedParams )
334+ builder .append ("[options]" );
335+ return builder .toString ();
332336 }
333337
334- private static String variableToLabel (Variable variable ) {
338+ private static boolean isNamedParams (Parameter parameter ) {
339+ return findAnnotation (parameter , NamedParams .class ).isPresent ();
340+ }
341+
342+ private static String parameterToLabel (Variable parameter ) {
335343 var builder = new StringBuilder ();
336- builder .append (variable .getName ());
337- var type = TypeChecker .getType (variable );
344+ builder .append (parameter .getName ());
345+ var type = parameter .getType ();
338346 if ( type .isArray () )
339347 builder .append ("..." );
340- if ( !ClassHelper .isObjectType (type ) ) {
348+ if ( !ClassHelper .isObjectType (type ) || type .isGenericsPlaceHolder () ) {
349+ builder .append (": " );
350+ builder .append (TypesEx .getName (type ));
351+ }
352+ return builder .toString ();
353+ }
354+
355+ private static String variableToLabel (Variable variable ) {
356+ var builder = new StringBuilder ();
357+ builder .append (variable .getName ());
358+ var type = getType (variable );
359+ if ( !ClassHelper .isObjectType (type ) || type .isGenericsPlaceHolder () ) {
341360 builder .append (": " );
342361 builder .append (TypesEx .getName (type ));
343362 }
@@ -377,6 +396,9 @@ public static String getDocumentation(ASTNode node) {
377396 var result = groovydocToMarkdown (mn .getGroovydoc ());
378397 if ( result == null )
379398 result = annotationValueToMarkdown (mn );
399+ var namedParams = namedParams (mn .getParameters ());
400+ if ( namedParams != null )
401+ result = (result != null ? result : "" ) + namedParams ;
380402 return result ;
381403 }
382404
@@ -395,6 +417,30 @@ private static String annotationValueToMarkdown(AnnotatedNode node) {
395417 .orElse (null );
396418 }
397419
420+ private static String namedParams (Parameter [] parameters ) {
421+ if ( parameters .length == 0 )
422+ return null ;
423+ var param = parameters [0 ];
424+ if ( !TypesEx .isEqual (param .getType (), ClassHelper .MAP_TYPE ) )
425+ return null ;
426+ var namedParams = asNamedParams (param );
427+ if ( namedParams .isEmpty () )
428+ return null ;
429+ var builder = new StringBuilder ();
430+ builder .append ("\n \n Available options:\n " );
431+ namedParams .forEach ((name , an ) -> {
432+ var namedParam = asNamedParam (an );
433+ builder .append ("\n `" );
434+ builder .append (name );
435+ if ( !ClassHelper .isObjectType (namedParam .getType ()) ) {
436+ builder .append (": " );
437+ builder .append (TypesEx .getName (namedParam .getType ()));
438+ }
439+ builder .append ("`\n " );
440+ });
441+ return builder .toString ();
442+ }
443+
398444 private static String groovydocToMarkdown (Groovydoc groovydoc ) {
399445 if ( groovydoc == null || !groovydoc .isPresent () )
400446 return null ;
0 commit comments