vernac / architecture, expressed.
Browse documentation

Building a language that feels native in IntelliJ

A useful DSL plugin must understand more than syntax. It must preserve the meaning of the user’s source while cooperating with the IDE’s type system, indexes, debugger and project model. Embedding Java makes that cooperation especially important: users expect ordinary Java assistance inside a language-specific shell.

This collection records engineering lessons from Vernac’s implementation. It is intended for contributors and developers building other language plugins, including a possible SQLJ integration. It explains mechanisms and trade-offs, not a new language contract or a promise of support for every IntelliJ version.

Read by problem

Your problem Start here
Decide what belongs in LSP, PSI or Java injection Language and Java architecture
Java looks correct but types, factories or completion are missing Resolution and completion
Ctrl+B works but Ctrl-hover has no link feedback References and navigation
Broken or unsaved code disables assistance Editor recovery
Breakpoints work but expressions are red or cannot complete Debugger contexts
Decide what a debugger may hide or evaluate Honest presentation
Plan a Java/SQLJ language plugin Applying the lessons to SQLJ

Platform vocabulary

Term Meaning here
PSI IntelliJ’s structured program model, used for references, types and code insight
LSP Language Server Protocol; a portable editor/server interface
VFS IntelliJ’s virtual file system and its change notifications
EDT Event Dispatch Thread, responsible for UI work; long tasks must not block it
JDI Java Debug Interface, used to inspect/control a real JVM
Injection/projection Java context assembled from mapped DSL ranges plus synthetic scaffolding

Evidence and current boundaries

The observations come from compiler tests, exact-SDK compilation, real-JVM/JDI probes and user acceptance in IntelliJ IDEA Ultimate 262.10968.63, during 0.5.0-SNAPSHOT development. Those forms of evidence answer different questions; none alone establishes complete IDE correctness. See the verification ladder.

Native debugging, expression evaluation, conditions/logpoints and the reviewed Java/DSL navigation paths have been exercised in the installed IDE. Live, pre-generation ordinary-VO Java APIs are accepted. The follow-up extends this to IDs, enums, entities, aggregates, access views and explicit collections, pending installed-IDE acceptance. The short-name-cache connection initially failed in the installed IDE. A context report isolated a host/injected-file scope-union bug; its retest confirmed class-name candidates. Short-name insertion and static-member completion are now confirmed too. A context report identified copied hosts losing their scope and early physical package caching. Native host-copy/package regressions cover the corrections; each newly projected type kind still needs installed-popup acceptance. Do not interpret component tests as verified support for all completion contexts.

Cross-language rename, Hot Swap, remote debugging and debugging dependency-module sources remain separate workstreams. A read-only target or a source-map sidecar does not implement them automatically.

How to extend this collection

Record the symptom, responsible IDE layer, invariant, remedy and remaining test boundary. Link to the implementation notes instead of duplicating their full API inventory. Keep private customer code, credentials, transient logs and chat transcripts out of these pages. When an assumption changes, update the lesson’s status as well as the code.

For the user experience, read the plugin tutorial and debugging walkthrough. For implementation details, use contributor documentation. For other topics, open the documentation map.