Tree-sitter grammar and Zed extension for Cylc ! 🥳

Tree-sitter grammar for Cylc

Here’s a Tree-sitter grammar for workflow configuration files (.cylc or suite.rc).

Because Tree-sitter builds a complete concrete syntax tree, it enables IDE-side features typically reserved for language servers, such as a hierarchical file outline, external language injection for specific code sections (e.g. BASH for scripts), and more.

Zed Editor Extension for Cylc

Additionally, I’ve developed a Zed Editor extension based on this grammar to showcase its capabilities. It’s currently the only way to enable syntax highlighting in Zed, as the editor does not support TextMate grammars. Here are some of its key features:

Description
:rainbow: Syntax Highlighting Provides comprehensive highlighting for .cylc and suite.rc files.
:scroll: Hierarchical Outline Displays a collapsible, structured view of your Cylc configuration file, making it easy to navigate large workflows.
:keyboard: Auto-Indentation Automatically adjusts indentation as you write, ensuring your workflow configuration is consistently formatted.
:open_file_folder: Code Folding Collapse or expand sections of your Cylc files for easier management of large blocks of code.
:desktop_computer: BASH Language Injection Embedded Shell syntax inside script settings and the [[environment]] section, for easier validation of your tasks scripts.

IDEs which use Tree-sitter for language support

Development

The repositories are currently hosted under my personal GitHub account, not the Cylc organization. The projects are licensed under MIT, not GPLv3. Feel free to reach out if you would prefer a license or repo ownership change.

While the current grammar is suitable for daily use, certain edge cases will be difficult to address without deeper refactoring. Specifically, everything related to string parsing is, in my opinion, RegEx spaghetti.
I began writing a parser for ISO 8601 datetimes, durations, and Cylc recurrences, but quickly abandoned it. It’s almost a grammar in and of itself. I decided to release the grammar before completing the full implementation.

This means the “API” (the generated concrete syntax trees) is still subject to change, and these changes should probably occur before developing other IDE extensions.

4 Likes

This looks great (especially the Zed screenshot), thanks so much for taking the initiative and sharing this!

I haven’t tried it out yet but will try to get it working with neovim when I get the chance.

I began writing a parser for ISO 8601 datetimes, durations, and Cylc recurrences, but quickly abandoned it. It’s almost a grammar in and of itself

That matches my experience of writing the Pygments lexer!

The projects are licensed under MIT, not GPLv3

That’s no problem. The cylc-flow code is GPL, however, many of the smaller repos (including the TextMate grammar) are BSD licensed to make them more easily usable as plugins.

If you haven’t bumped into them on your travels, we have some reference files to assist with writing lexers that aim the cover the Cylc syntax.

1 Like

If you haven’t bumped into them on your travels, we have some reference files to assist with writing lexers that aim the cover the Cylc syntax.

Thanks for pointing that out! I came across the tests/ directory in the repo but didn’t think to check the reference-files/ folder (much more useful for manual testing!)

I wrote a few tests during development, using Tree-sitter’s own testing suite, but they no longer pass due to changes in the produced API (e.g. node names). I’ll rewrite them based on these files.

    # SYNTAX: 2.4
    # text inside <> should be highlighted
    # optionally suite / task could be highlighted
    [[graph]]
    R1 = """
    poller<other.suite::foo> => foo
    foo & poller<other.suite::task_bar> => baz

    poller<other-suite::foo:fail> => bar
    poller<other.suite::foo>:start => bar

    <oops suite::foo> => bar```
    """

Is this other-suite::foo syntax still in use? I’ve never come across it, and I don’t believe the Tree-sitter grammar currently supports it.

Thankyou @elliotfontaine - that is really awesome. I’ve been using Zed lately (and neovim) and had vaguely considered attempting this myself. But only vaguely!

That syntax automatically creates a task definition with task scripting that uses the cylc workflow-state command to poll for a task to achieve a state in other workflow. We don’t recommend using it anymore, but unfortunately I did find a few cases (at my site) where it’s still in use. It’s pretty rare though. I wouldn’t bust a gut to support it in in the grammar.