Skip to content

Improve rendering of DAG preview #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ErikDanielsson
Copy link
Contributor

@ErikDanielsson ErikDanielsson commented Jul 15, 2025

This PR makes quite substantial changes to the DAG preview feature of the language server. First and foremost it adds support for rendering control flow correctly (issues #93 #88).

The solution to these issues is to use scoped symbol tables where each name in the program can correspond to several nodes in the control flow DAG. As we enter a new conditional statement each of the two branches inherits the symbols from the outer scope. Once we leave the conditional statement the symbol table for the outer scope is the union of the symbol table for each branch, minus any local variables declared.

To have separate plates for separate branches I have also added subgraphs for each conditional branch. The conditional node (corresponding to the boolean in the if) is always a predecessor. Since an a conditional branch can contain a conditional branch, the subgraphs are represented as a tree structure and rendered accordingly.

Example: if we call the symbol table s then we have the following

workflow {
    test  = params.echo // s = {'test': {<Node>("test", ...) , 0)>}
    if (test) {
        echo = TOUCH(params.echo) // s = {'test': {<Node(params.echo, 0)>, 'echo': {<Node('echo', 1)>}
    } else {
        echo = DEFAULT() // s = {'test': {<Node(params.echo, 0)>, 'echo': {<Node('echo', 2)>}
    }
    // s = {'test': {<Node(params.echo, 0)>, 'echo': {<Node('echo', 1)>, <Node('echo', 2)>}
   // Overwrite test variable
    test  = APPEND(echo) // s = {'test': {<Node(params.echo, 1)>, 'echo': {<Node('echo', 1)>, <Node('echo', 2)>}
}

which will be rendered as the DAG

flowchart TB
subgraph " "
  subgraph params
    v0["echo"]
  end
  v0["echo"]
  v2([conditional])
  v7([APPEND])
  subgraph s1[" "]
    v3([TOUCH])
  end
  subgraph s2[" "]
    v5([DEFAULT])
  end
    v0 --> v2
    v0 --> v3
    v3 --> v7
    v5 --> v7
    v2 --> s2
    v2 --> s1
end
Loading

If a global variable is assigned in one branch but not the other then it is implicitly initialized with null. To address this, we check before entering the outside scope whether this was the case and add a null node to the scope where the variable is not declared.
This means that the code:

workflow {
    if (params.echo) {
        echo = TOUCH(params.echo)
    } 
    APPEND(echo) 
}

which will be rendered as

flowchart TB
subgraph " "
  subgraph params
    v0["echo"]
  end
  v0["echo"]
  v1([conditional])
  v4{null}
  v6([APPEND])
  subgraph s1[" "]
    v2([TOUCH])
  end
    v0 --> v1
    v0 --> v2
    v2 --> v6
    v4 --> v6
    v1 --> s1
end
Loading

Removing nodes that shouldn't be rendered

By default we only want to render inputs, processes/(sub)workflows and outputs. This means that we need to collapse the rest of the graph. There are two types of nodes we remove:

  • Variable nodes
  • Disconnected null nodes

The second case is new and correspond to the situation where a variable is is initalized in one branch but never used outside the branch, which will create a disconnected null in the other branch. We remove nodes in place via a DFS in the Graph.collapseGraph function to which we pass predicates for nodes that should always be hidden (variable nodes) and nodes that should be hidden if they become disconnected (null nodes).

Configuration support for rendering variable nodes

I have also added the configuration option nextflow.dag.showVariables which toggles whether variables should be shown (or if the graph should be collapsed). It is false by default. I added it since it could be (was for me) useful for debugging purposes.

If this is not desired we can revert the changes in 287324c. Here is the corresponding PR on the vscode-code-language side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing connection in DAG for conditional execution Improve rendering of conditional blocks in DAG preview
1 participant