Skip to content

Symbol resolution #7

@bentsherman

Description

@bentsherman

Symbol resolution is the basis of many language features:

  • error checking (undefined variable, redefined variable, etc)
  • hover hints
  • completion
  • go to definition
  • find references
  • function signature help
  • call hierarchy
  • rename

Once you can determine the set of available symbols in a given scope, and trace symbol references back to symbol definitions, you can implement all of the above features.

This issue is mainly a way for me to think through all of the moving parts, because it's complicated. But you can share your opinion too I guess.

Script

  • Symbols come from many different sources:
    • Implicit variables
    • Implicit functions, types (i.e. Nextflow standard library)
    • Java/Groovy standard library
    • processes, functions, workflows
  • There are also different kinds of local variables:
    • pipeline params
    • function/closure parameters
    • process/workflow inputs
    • local variables (may or may not be declared with def)
  • There are also local methods:
    • operators
    • process directives
    • output directives
  • Need to resolve symbols in an additional AST transform
    • declare built-in symbols
    • declare symbols for top-level definitions, function/closure params, etc
    • check symbol references to make sure they are defined
    • emit errors for missing symbols, name conflicts, etc
  • Need to collect the set of available symbols in a given scope:
    • built-in
    • imported via include statement
    • defined as top-level component
    • declared in current or parent scope
    • for completion, external symbols should be included but add an include statement if an external symbol is selected
  • Include Groovydoc for top-level definitions in completion detail, hover hints

Built-in symbols should be annotated in the Nextflow codebase, then the language server can query these annotations. For example:

@StdLib('''
  The `map` operator applies a mapping function to each value from a source channel.

  [Read more](https://nextflow.io/docs/latest/operator.html#map)
''')
DataflowWriteChannel map(DataflowReadChannel source, Closure closure) {
    // ...
}

For now I can keep these definitions in the language server and then move them into Nextflow once they are stable. Even better if we can do it with Groovydoc instead of annotations, but I'm not sure if that's possible.

Config

  • Symbols come from built-in variables and config options
  • Built-in variables are defined here: https://nextflow.io/docs/latest/script.html#configuration-implicit-variables
  • Config options are defined throughout the Nextflow codebase and plugins
  • Config schema nextflow#4201 will allow the language server to query the set of all config options along with types and documentation
  • Config options from third-party plugins can also be supported as long as the plugin is defined in the config file -- the language server can parse the config file, get the list of extra plugins, download those plugins via Nextflow's plugin system and load their config schemas
  • Dynamic process directives should have the same context as the process definition (process inputs, task, etc) but that is trickier

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions