Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>DC-Grails-VSNew to Visual Studio Code? Get it now.
DC-Grails-VS

DC-Grails-VS

Lei Gao

|
2 installs
| (0) | Free
Grails development for VS Code: breakpoints that actually bind in .groovy files, closures included, plus the Grails and Gradle commands.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

DC-Grails-VS

Pre-release. The debugger works: breakpoints bind in .groovy files, closures included, and full sessions have been driven end to end against a Grails 7.2.3 application. But it has been exercised on one application, on Windows, by one person. Expect to find things. Known gaps are listed at the bottom; the issue tracker is on GitHub.

Marketplace gaolei8888.grails-gradle-extension
Source github.com/gaolei8888/dc-grails-vs
Issues github.com/gaolei8888/dc-grails-vs/issues
Changelog CHANGELOG.md

Installing from the command line — the --pre-release is needed while this is one:

code --install-extension gaolei8888.grails-gradle-extension --pre-release

Grails and Gradle commands from the command palette and a sidebar tree — and a debugger that can actually stop on a line of Groovy.

Breakpoints in .groovy files

VSCode does not bind breakpoints in Groovy. The Java debugger resolves "file → class name" through JDT, and JDT does not index .groovy, so the breakpoint is never registered with the JVM and the dot stays hollow. No setting changes this.

This extension ships its own debug adapter. In attach mode it does not parse Groovy at all: it asks the running JVM which class owns a line, which is the only authoritative answer there is. Closures come out for free — a synthetic closure class reports the same source file as the class that encloses it — and classes that are not loaded yet are picked up when they load.

What that means in practice:

  • Breakpoints bind in controllers, services, domain classes and inside closures, including nested ones.
  • They bind in methods rewritten by Grails' AST transforms — a @Transactional method whose body has been moved into $tt__… still stops on the right line.
  • A breakpoint on a line with no bytecode (a method signature, a blank line) slides down to the first line that can hold one, and says so.
  • Variables captured by a closure are shown as their values, not as the groovy.lang.Reference the compiler boxed them in.
  • A Grails scope beside Locals whenever the thread is serving a request, with params, request, response and session. None of those is a local or a field of anything on the stack — they belong to the request, which Spring keeps in a thread local — so nothing else shows them.
  • Maps show their entries. params reads as controller, action, id, not as a wrapper around a table and a modCount.
  • A domain object shows its own properties. GORM's traits compile a field into every domain class they touch, and a class with one property of its own arrives carrying half a dozen of them; those are hidden, and id and version are not.
  • Stepping skips the framework: the Groovy runtime, reflection, the transaction template and the servlet container are stepped over rather than into.
  • Exception breakpoints, caught and uncaught, filtered the same way: it stops where your code threw, not in the framework. A Grails request throws and catches its way through startup and dispatch, and stopping for all of it would be unusable.

Exception breakpoints are in the Breakpoints section of the Run and Debug view. The exception's message is read from the object rather than by calling getMessage(), so nothing runs in the application to describe a stop.

Run Grails: Debug App. It starts gradlew bootRun --debug-jvm, waits for the JVM to report that it is listening, and attaches. Or write your own attach configuration:

{
  "type": "groovy",
  "request": "attach",
  "name": "Attach to Grails",
  "hostName": "localhost",
  "port": 5005
}

Debugging tests

Grails: Debug Tests runs gradlew test --debug-jvm and attaches to the test JVM, which is the same machinery the application uses — Gradle forks it with the same agent on the same port and announces it on the same stream. It offers the spec in the active editor as the filter, so debugging the failure you are looking at is one command; leave the box empty to debug the whole run.

Commands

Every Grails command and Gradle task this project knows about is registered — 201 of them. The ones people reach for are in the Grails view in the activity bar, under Run, Create (empty), Scaffold from domain class, Database Migration and Gradle; the rest are in the command palette under Grails: and Gradle:.

Create and Scaffold are not the same thing. create-controller writes an empty controller; generate-controller scaffolds one, actions and all, from a domain class that already exists — which is why every entry under Scaffold asks for one.

Run, Debug and Stop are also in the status bar, in green and red. While a build is running the pair is replaced by a spinner that turns into a check and the address once the application reports that it is serving; clicking it opens the app, or shows the build output while it is still starting.

The application's log goes to an output channel — Grails - Normal for Run App, Grails - Debug for Debug App — reachable any time with Grails: Show Output.

Requirements

  • A Grails project with a Gradle wrapper (gradlew) in the folder you open.
  • Java 17 or newer on JAVA_HOME or PATH. The debug adapter runs on it; the JDK your application runs on can be a different one.
  • For grails.debug.adapter: "java" only: the Debugger for Java extension.

Settings

Setting Default What it does
grails.run.command "" Command line to start the app instead of the Gradle wrapper. For projects that boot through a script which exports the environment the app needs.
grails.run.debugCommand "" Same, for Debug App. Defaults to grails.run.command with --debug-jvm appended.
grails.run.env {} Environment variables to add for the app process.
grails.run.args [] Extra arguments for bootRun.
grails.debug.adapter groovy groovy uses the adapter in this extension. java hands the session to vscode-java-debug, which cannot bind a breakpoint in a .groovy file.
grails.debug.javaPath "" The java used to run the debug adapter. Defaults to JAVA_HOME, then PATH.

Attach configurations also take sourcePaths (roots for resolving stack frames, defaulted from the project layout), stepFilters (packages to step over), stepIntoProjectCodeOnly and trace.

Stopping

Stop reaches the application, not just the build. Gradle forks the app from its daemon, and the daemon is reused between builds, so the app is only sometimes a descendant of the process this extension started — killing that process tree leaves the app running about as often as not, holding the debug port and 8080. Stop kills the tree and, separately, whatever JVM is listening on the ports the app announced.

Debug App also checks the debug port before starting. If something already holds it, it says what, and offers to stop it — because a JVM that cannot open its debug port produces a build that stops after :findMainClass and prints nothing at all, which is indistinguishable from a hang.

Known issues

  • Step over from the middle of a line overshoots. After a call returns you are partway through the line that made it; a step over issued there runs to the end of the method instead of moving to the next line. Located, not yet fixed.
  • Step into does not enter a @Transactional method. Its entry runs through the transaction template, and stepping climbs back out to the calling line. Put a breakpoint in the method instead.
  • Conditional breakpoints, watch expressions and hover evaluation are not implemented. They need Groovy expressions compiled and evaluated inside the target VM.
  • Attach only. There is no launch configuration; the app is started by the Gradle wrapper and the adapter attaches to it.
  • GSP files are not mapped.

Star it

If you use this, clone it, or build something of your own on top of it, please star the project on GitHub. It takes one click, and it is the main way anyone else finds out this exists.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft