Beyond DSLs: Domain-specific thinking in the IDE

DSLs not only offer compact descriptions, they also enable better IDE interactions.

by Sung-Shik Jongmans on 11 Feb 2025

Sung-Shik Jongmans

Domain-specific languages tooling

I write a lot of Java. These days, I use Visual Studio Code. Sometimes, I see a variable but don’t remember its initial value. No worries: I just put the cursor on the variable, press F12, and watch the editor navigate to the declaration. It’s called “Go to Definition” in Visual Studio Code. Many other IDEs have it, too, and for many other languages. It’s a very useful feature.

So… Should IDEs for DSLs have “Go to Definition” as well?

“Go to Definition” is fantastic for languages like Java, Python, and C. So, yes, surely we need something similar. At the risk of stating the obvious, though, there’s something special about DSLs… They’re domain-specific. Shouldn’t we try to leverage that, too? How about offering domain-specific ways to navigate code, beyond “Go to Definition”?

Here’s the key point of this post: generally, domain-specific thinking isn’t just a language concern; it’s also a tooling concern. What works well for the Javas, Pythons, and Cs of this world might not work well for a DSL. Or, there could be complementary alternatives. With each DSL tool, we design – and immerse in the domain – we therefore ask ourselves… Can the user interface be aligned with domain concepts? Can we take advantage of those concepts to improve the user experience? Domain-specific thinking shouldn’t end with the DSL but continue with the IDE.

To illustrate this point, let’s take a look at two kinds of code navigation for the Bird DSL (To be published): not only “Go to Definition” but also something domain-specific. Along the way, we’ll also see the utility of Code Lenses and visualization support in Visual Studio Code.

Bird in a eggnutshell

Bird is a DSL we designed to define binary data formats. We wrote about it in a other post. For instance, the following code defines the PNG format in Bird:

struct PNG @(encoding = US_ASCII, endianness = BIG) {
    Signature _
    Chunk[]   chunks
    IEND      end
}

This code states that each PNG image consists of a signature (metadata), a number of chunks (the actual data), and a special marker at the end.

The Bird IDE we built is an extension of Visual Studio Code. It’s open-source and easy to install. “Go to Definition” is, of course, one of the definitions we included. It works essentially the same way for Bird as for Java, Python, and C. The following video shows its usage:

VS Code jumping to definition in a Bird DSL file

Go Fly to definition”

How can we take advantage of Bird’s domain concepts to improve the user experience? Bird’s domain is “binary data formats”. These formats tend to be organized as graphs. However, as graphs grow larger, it’s easy to lose track of the bigger picture. So, to assist the programmer in creating a mental image of Bird code, code navigation is provided via interactive visualizations. This allows us to accomplish two things at once:

  1. The visualizations offer bird’s-eye views of binary data formats, specifically tailored to their hierarchical structure. Such overviews can be quite insightful.
  2. The mechanism to navigate from visualizations to code clearly links higher-level designs to lower-level implementations. Meanwhile, complementarily, “Go to Definition” continues to be useful when editing code.

To cut a long story short, we built a lightweight engine to generate interactive visualizations of binary data formats, along with code navigation support. DSLs are particularly suitable for this kind of tooling, as they are relatively small and have well-defined semantics. In contrast, building such tools for general-purpose languages tends to be much more complicated and time-consuming.

The following video shows the usage of the visualization engine.

VS Code jumping to definition via an animation

The video starts from the definition of the general image format in code. By clicking the Code Lens above the definition (“Visualize Image”), the user triggers the generation of an interactive visualization. It shows that an image is either PNG or JPEG. PNG consists of a signature, chunks, and special token, while JPEG consists of a header, footer, and scan, et cetera. By clicking on any of the boxes, the user is navigated to the corresponding code.

Under the hood

The Bird IDE – including “Go to Definition”, our visualization engine, and Code Lens integration – is built using a combination of Rascal, the Language Server Protocol, and the Visual Studio Code API. These are all proven, open-source technologies that we know very well. By leveraging existing Rascal frameworks, our Bird-specific code is small, concise, and easy to maintain.

Key takeaways

  1. Domain-specific thinking isn’t just a language concern; it’s also a tooling concern.
  2. The Bird IDE (extension to Visual Studio Code) is an illustrative example of domain-specific interaction with code.
  3. Rascal allows us to build domain-specific tooling that’s easy to maintain.

Get in touch

Do you face tooling-related or any other language engineering challenges? Then reach out to us. We look forward to discussing how our solutions could help you.

Recent posts

Any substantial change to critical software should be backed by a high confidence in correctness. Testing can be difficult when working with existing software systems. Automated tests might be sparse and hard to write effectively, while manual testing practices are often error-prone and labor-intensive. During the reverse-engineering of legacy systems, in particular, correctness can be challenging to define and components might interact in non-obvious ways. Therefore, simply “writing tests” is not as simple as it sounds.

Read More…

I write a lot of Java. These days, I use Visual Studio Code. Sometimes, I see a variable but do not remember its initial value. No worries: I just put the cursor on the variable, press F12, and watch the editor navigate to the declaration. It’s called Go to Definition in Visual Studio Code. Many other IDEs have it, too, and for many other languages. It’s a very useful feature. So… Should IDEs for DSLs have Go to Definition as well?

Read More…