VS Code integration for the Elide build tool. The extension turns an elide.pkl project into a
model the Kotlin LSP by JetBrains can
import — resolved Maven dependencies, source roots, JDK — and adds Elide build/run/test tasks and JDWP debugging.
Android projects get deployment, Logcat, database and Compose preview tooling. Komodo, the native Kotlin language
server, debugger and live Compose preview engine shipped with Elide, is available as an experimental opt-in
(details).
Until the extension is on the Marketplace, install the .vsix from plugins.elide.dev (the same worker that hosts the
IntelliJ plugin repository). The link is version-less and always resolves to the current release:
curl -LOJ "https://plugins.elide.dev/vscode/files?id=elide.elide" # → elide-<version>.vsix
code --install-extension elide-*.vsixOr in VS Code: Extensions ▸ … ▸ Install from VSIX…. https://plugins.elide.dev/vscode lists the published version
and a pinned (&version=) link.
- Elide 1.5+ installed (
~/.local/share/elide,$ELIDE_HOME, orelideonPATH). 1.6.1+ adds live task progress and batched classpath queries; older releases work without them. - For the default language server, the Kotlin by JetBrains extension (
JetBrains.kotlin-server, ≥ 0.0.11), installed separately from the Marketplace. Complete its one-time region / data-sharing setup for Kotlin/Java analysis and JVM debugging. Elide activates independently: sync, build, layoutlib previews and environment diagnostics do not wait for the language server. Opening an Elide Kotlin or Java source document starts the companion when needed. It is not needed with Komodo (elide.kotlinLsp.server=komodo), which requires an Elide release with on-demand Komodo resolution. Choose Elide: Install Komodo explicitly; default use never downloads it. Missing, incompatible or failed Komodo disables native analysis/debug actions and stops stale native resources, while ordinary project sync and builds remain available. Fix the component or override and sync again to recover without reloading the window; settings are never rewritten and no alternative engine is selected automatically. - A JDK for symbol resolution (
$JAVA_HOME, SDKMAN,/Library/Java/JavaVirtualMachines,/usr/lib/jvm, …). Elide's bundled JDK image cannot serve this role: it ships without areleasefile, which IntelliJ needs to enumerate modules.
Opening a folder that contains elide.pkl runs a sync:
elide project export— the project manifest as JSON (declared source sets, Kotlin compiler options, entrypoints). One additionalelide project export --format=intellijper workspace supplies Elide's resolved source conventions for every member, includingsrc/main/kotlin; the extension does not reconstruct them from raw manifest defaults. Explicit source selectors remain intact. Elide 1.5 uses its legacyelide manifestcommand and source schema instead.elide install— only when the lockfile (.dev/elide.lock*.bin) is older than the manifest.- The compile classpath of every compilable source set (plus the runtime and Android
providedones an Android project needs): oneelide classpath --json <set>:<usage>…per project on Elide 1.6.1+, oneelide classpath <set>:<usage>per query before that. workspace.jsonis written at the workspace folder root in the Kotlin LSP's JSON workspace format (one module per source set, one library per jar with-sources.jar/-javadoc.jarattached when present, the selected JDK as SDK, Kotlin language/API level and free compiler args as kotlinc flags).- Two Kotlin LSP settings are maintained:
intellij.buildToolis pinned tojsonin the workspace, andintellij.jdkForSymbolResolutionis written to user settings. A running Kotlin LSP is then asked to reload.
workspace.json is a generated artifact — add it to .gitignore. Every elide.pkl under a workspace folder (outside
.dev/ and node_modules/) becomes a set of modules in that folder's single workspace.json. A manifest nested
inside another project's directory — a vendored checkout, a sample, a fixture — is a separate build the enclosing
project does not invoke: only the outermost manifest of each tree is imported, and changes to the nested ones do not
trigger a sync.
The exception is an Elide workspace: a root manifest declaring workspace.members is resolved with its members, the
way the CLI builds them as one graph. elide install runs once at the root (the lockfile check weighs every member's
manifest), and each member's classpath is then read by the CLI invoked in that member's directory. A member's
classpath names a sibling's JAR as the path that sibling's build writes (<member>/../<sibling>/.dev/artifacts/jar/…);
that entry is not a library but a module dependency on the source sets the JAR packages, exported so it carries
through, which lets the Kotlin LSP resolve sibling code without building it. Once synced, every member is a project of
its own for path-keyed features (code lenses, tests, tasks, debugging), and edits to its manifest resync the workspace.
Opening a member's own directory instead of the root gives that member no siblings: the CLI still puts their JARs on its classpath, but they are build outputs of projects the window does not hold, so the sync reports them as unresolvable rather than inventing a library for a jar that may not exist, and offers to open the workspace root.
Each step of a sync, the elide commands it runs and the build tasks those report are timed. The status bar names
what is in progress, a sync that runs longer than two seconds also shows a cancellable notification
(elide.sync.showProgressNotification), and the Activity view in the Elide sidebar keeps the step tree of every
sync and Compose preview build, with each step's duration, share of the whole and output. The status bar tooltip and
the Elide output channel end each sync with a breakdown of where its time went.
Every CLI process the extension drives itself runs with ELIDE_PROGRESS=json. Elide 1.6.1+ then writes one JSON
record per build event on stderr (task started, progress, finished; messages with absolute source locations; the
verdict), so running tasks and their progress show while they run — Resolving Maven dependencies: 40% (1/3) in
the status bar. The records are rendered back to the text Elide prints without a terminal for the Elide output
channel and logs. Older releases ignore the variable and print a line per finished task, so progress is known as
tasks finish. Task terminals are left in auto (or plain, for the piped terminal of a Compose preview build), since
they are read by a person and the $elide problem matcher.
The Kotlin LSP only reads workspace.json when no build system claims the folder first: it matches jps
(.idea/modules.xml), gradle, maven, and bazel before falling back to the JSON importer, and asks the user to
choose when several match. A checked-in .idea from a teammate on IntelliJ would therefore be imported instead of the
Elide model. Pinning intellij.buildTool to json skips detection, so .idea and workspace.json coexist: IntelliJ
ignores workspace.json, VS Code ignores .idea. The pin is skipped when the window holds a folder without an Elide
project (the setting is window-scoped and would disable that folder's Gradle/Maven import) — set it per workspace by
hand there. An explicit intellij.buildTool in user, workspace, or folder settings is never overwritten.
| Setting | Scope written | Why |
|---|---|---|
intellij.buildTool |
workspace (.vscode/settings.json) |
json; portable, correct for everyone who opens the repo in VS Code. Safe to commit. |
intellij.jdkForSymbolResolution |
user settings | An absolute JDK path for this machine. Committed, it breaks every other checkout: the importer rejects a defaultSdk that is not a directory and the whole import fails. |
A workspace or folder value of intellij.jdkForSymbolResolution that does not resolve on this machine (a path
committed by a teammate, or written by an older version of this extension) is cleared on sync; one that does resolve is
treated as a deliberate project override and kept. In user settings, only a value this extension wrote is updated —
anything you set by hand stays.
| Command | Description |
|---|---|
Elide: Sync Project(s) |
Re-run the sync. |
Elide: Show Menu |
Quick pick with the Elide actions (what the status bar item opens). |
Elide: Run Elide Command… |
Pick build, test, install, or run <entrypoint> and run it as a task. |
Elide: Open Generated Kotlin LSP Workspace |
Open workspace.json. |
Elide: Show Output |
Open the Elide output channel (CLI output, sync log). |
Elide: Show Activity |
Reveal the Activity view: each sync and preview build, its steps and their durations. |
Elide: Open Activity Log |
Open the latest operation's output, with timestamps, in an editor. |
Elide: Select Android Deployment Target… |
Choose and remember the project, APK and device shared by Run and Debug. |
Elide: Check Android Environment |
Inspect local tools, SDK and devices, even before sync succeeds; select a finding for remediation. |
Elide: Refresh Android Devices |
Refresh the sidebar device list without resyncing the project. |
Elide: Pair Android Device over Wi-Fi… |
Pair with a QR or six-digit code, or connect an already-paired device by address. |
Elide: Run Android App… |
Build an APK and install and launch it on a device or the emulator. |
Elide: Debug Android App… |
Build an APK, install and launch it waiting for a debugger, and attach over JDWP. |
Elide: Run Android Instrumented Tests… |
Install a test APK on the deployment target and run its instrumented tests in the Test Explorer. |
Elide: Start Android Emulator, Elide: Stop Android Emulator, Elide: Create Android Virtual Device |
Drive the project's virtual device. |
Elide: Install Android SDK Components |
elide build android-sdk --install-sdk. |
Elide: Create Android Signing Keystore |
elide build android-keystore-<artifact>. |
Elide: Open Compose Preview |
Render Compose previews locally, without a device or emulator: layoutlib snapshots by default, live cards with the experimental Komodo engine; refresh live while editing. |
Elide: Show Android Logcat |
Stream device logs with application, priority and text filters. |
Elide: Explore Android Databases |
Browse and query a debuggable app's Room / SQLite databases on a connected device. |
Task type elide with command (build | run | test | install) and project (root relative to the
workspace folder). Provided tasks: elide: build, elide: test, elide: install, and one elide: run … per
manifest entrypoint (entrypoint, jvm.main, scripts).
The rest of the definition is the invocation:
| Field | Becomes | Example |
|---|---|---|
args |
positional arguments of the subcommand | ["compile"] → elide build compile |
flags |
-f NAME[=VALUE] build flags, read by the manifest as build.flags |
["release"] → -f release |
options |
CLI options, keyed by name | { "no-cache": true, bail: 3 } → --no-cache --bail=3 |
programArgs |
arguments after -- |
["--port", "8080"] → -- --port 8080 |
env |
environment variables for the Elide process | { "CI": "true" } |
An option value of true emits the bare flag, false omits it, an array repeats the option ({ limit: ["workers=4"] }
→ --limit=workers=4), and anything else becomes --name=value; a one-character name gets a single dash. Workspace
settings supply the defaults (elide.flags, elide.build.options, elide.run.options, elide.test.options,
elide.install.options) and each key of a task definition overrides them — false cancels an inherited option.
Every task reports through the $elide problem matcher, so compiler errors and warnings land in Problems and on
the offending line. It matches the two-line kotlinc shape Elide prints — [212ms] error: kotlinc: <message> followed
by In file: <path>[:<line>[:<col>]] — and resolves the path by searching the workspace folder (skipping .dev,
.git and node_modules), so relative paths reported from a project root in a subdirectory still resolve. Paths
containing spaces are supported in both regular task terminals and Compose preview builds. javac diagnostics
interleave symbol:/location: lines between the two and are shown in the terminal only.
Run with Elide / Debug with Elide appear above every fun main( (Kotlin, including @JvmStatic fun main in
an object) and public static void main in a production source root of a synced project, and Run / Debug
in elide.pkl on jvm.main, on each entrypoint element, on each scripts entry (Run script — a script is a
shell command line, so there is nothing to attach a debugger to) and on each artifacts entry (Build). A file
whose main class is the manifest's jvm.main runs as elide run; any other file is passed to Elide as the
entrypoint. Detection is regex-based — no Kotlin or Java parser is involved — so exotic declarations are missed; set
elide.codeLens.enabled to false to turn the lenses off.
The JetBrains Kotlin LSP contributes a second, unqualified Run / Debug pair on the same line from its own
LSJvmRunMainCodeLensProvider. It is a server-side feature with no setting, no registry flag and no exported client
API, so this extension cannot suppress it — hence the with Elide suffix. That pair launches the class through the
IntelliJ debug adapter rather than elide run, so it does not build first and does not see the project's compiled
output, which workspace.json does not carry.
JUnit tests in the test source roots of a synced project appear in the Testing view, grouped project → class
→ method, with nested classes nested. Discovery is static: the test sources of the project model are scanned for
@Test (and @ParameterizedTest, @RepeatedTest, @TestFactory, @TestTemplate) declarations, so the tree is
populated without compiling or running anything, and it follows edits — saved or not — through a file watcher and the
open editor's buffer. Like the code lenses, the scan is regex-based, so exotic declarations are missed.
A run executes elide test --reporter=tap in the project root, adding --test-name-pattern=<pattern> whenever the
selection is narrower than the whole project (the JVM engine full-matches that pattern against pkg.Class#method,
with $ separating nested classes). elide.flags and elide.test.options apply here too, except for reporter,
which stays tap because this run parses that stream. Results are reported as the TAP stream settles: a failure's
message and detail block become the test's message, and the first stack frame naming the test's own file
positions it in the editor. A label that matches no discovered item is added under the project item, so a result is
never dropped; a run that reports no result at all (a pattern matching nothing, a compile error) marks the selected
tests errored with the CLI's stderr.
The Debug profile runs the same command with a bare --debugger and attaches elide.debug.adapter once the JDWP
agent announces itself. That flag takes no address on elide test: the agent always binds 5005, so one debug run at
a time.
The Run with Coverage profile adds --coverage and, once the run exits, shows the reports it wrote in the
editor's coverage gutter and the Test Coverage view: guest (JavaScript/TypeScript) LCOV from
.dev/reports/coverage/js/lcov.info, and JVM coverage from the JaCoCo XML report under
.dev/reports/coverage/jvm/<task>/ (.dev/artifacts/coverage/jvm/<task>/ before Elide 1.6), with per-line branch
counts. Elide writes JVM coverage as JaCoCo execution data (.exec) by default, which cannot be read without the
compiled classes; ask for the XML report in elide.pkl to see it in the editor:
import "elide:Coverage.pkl" as Coverage
coverage {
jvm {
reports {
Coverage.xmlReport()
}
}
}Before Elide 1.6 the same setting is testing { coverage { jvm { reports { Jvm.xmlReport() } } } }, with
import "elide:Jvm.pkl" as Jvm.
JaCoCo sources are resolved against the project's source roots by package and file name, so a Kotlin file whose directory does not match its package is not shown.
Launch configuration type elide:
{
"type": "elide",
"request": "launch",
"name": "Elide: Run (debug)",
"entrypoint": "src/main.kt",
"args": ["--port", "8080"],
"flags": ["release"],
"options": { "coverage": true }
}The extension runs elide run --debugger [flags] [options] [entrypoint] [-- args] in a terminal, waits for the JDWP
agent's Listening for transport dt_socket at address: <port> line, then attaches the JetBrains JVM debugger
(elide.debug.adapter: intellij), Debugger for Java (java, requires vscjava.vscode-java-debug) or Komodo's
Kotlin debugger (komodo, the default for a folder on Komodo's language server). Stopping the
session terminates the Elide process. The JDWP agent always binds port 5005, so one debug session at a time.
The JetBrains debugger is activated before launch even when no Kotlin file is open. If attachment fails, the
extension reports the adapter error and terminates its launched Elide process rather than leaving it waiting.
Closing its terminal also cancels a pending attachment or stops the attached debugger. Launches with the same
display name keep separate ownership of their processes and debug sessions.
args are the debugged application's own arguments; elideArgs adds further positional arguments to the Elide
command itself. flags, options and env layer over elide.flags and elide.<command>.options exactly as in a
task, and --debugger is always added — set debugger in options to choose its value (dap, cdp, a
host:port address).
command selects what is debugged:
command |
Runs | What it acts on |
|---|---|---|
run (default) |
elide run --debugger |
entrypoint: file, manifest script, or empty for entrypoint/jvm.main |
test |
elide test --debugger |
the whole test run; narrow it with "options": { "test-name-pattern": "MyTest" } |
build |
elide build <targets> --debugger |
targets: build targets (run, jvm-test, … — elide build --inspect lists them) |
On build, --debugger is an option of the target task rather than of the build itself, so the configuration must
name at least one target; one with an empty targets assembles nothing and is rejected before any process starts,
and entrypoint is refused there (it belongs to run). Target options go in options the same way. targets may
list several targets, but only one of them can start a JVM: the bare --debugger binds 5005 for each, so two
debuggable targets in one configuration collide on that port. The Build targets section of the Elide sidebar
offers a Debug action on every target that declares --debugger.
{
"type": "elide",
"request": "launch",
"name": "Elide: Build (debug)",
"command": "build",
"targets": ["jvm-test"],
"options": { "select": "class:com.example.SomeTest" }
}A manifest declaring an android block builds for Android. Elide drives aapt2, d8, zipalign and apksigner
directly — no Gradle, no new subcommand — so every step is a build target of the ordinary graph and the extension
reaches all of them through elide build.
The sync reads three things beyond the usual manifest decoding:
- The
androidblock (compile and minimum API level, the declared SDK, the virtual device) and everyAndroidApk/AndroidAab/AndroidAar/AndroidFeatureentry ofartifacts, which become the project model'sandroidsection. - The resource, asset and native-library directories of each artifact, added as resource roots of the source sets
it packages. Applications and libraries fall back to existing
src/main/res,src/main/assetsandsrc/main/jniLibs; features use only their explicitly declared directories and never inherit the base app's. - The provided classpath of each source set, which is where an Android build puts the platform
android.jarand the generatedRclasses: the device carries the framework and the APK's own dex carriesR, so neither is on the compile or runtime classpath the rest of the model is built from, and without them nothingandroid.*and no resource identifier resolves.Ris listed only once the target that compiles it has run, so a sync that does not find it runselide build android-r-<artifact>first.
An application consuming a workspace sibling's Android library reads that library's staged classes.jar
(<lib>/.dev/android/<artifact>/classes.jar), never its published .aar. That path is recognized the same way a
sibling JAR under .dev/artifacts/ is, so the application's modules depend on the library's modules and navigation
crosses the project boundary without requiring a published AAR. Annotation processors may run during sync to
make generated declarations available.
A published .aar is a dependency rather than a sibling's output. Elide unpacks each one into
.dev/android/libraries/<group>/<artifact>/<version>/ and puts the classes.jar inside it on the classpath, so
the extension names the library by the coordinate that path spells out and attaches the -sources.jar of the
archive from the Maven repository — the same library the jar of a non-Android dependency would be. Resolving a
classpath does not unpack anything, so a sync that finds a directory missing runs elide build android-libraries
once before building the model; without it the language server would be handed jars that are not there.
The SDK is located the way a build locates it: android.sdk in the manifest, then $ANDROID_HOME, then
$ANDROID_SDK_ROOT, then the platform's own install location (~/Library/Android/sdk, ~/Android/Sdk,
%LOCALAPPDATA%\Android\Sdk). elide.android.sdk overrides it for extension-side device tooling, including wireless pairing and logcat.
A build reports a missing SDK itself; Elide: Install Android SDK Components
runs elide build android-sdk --install-sdk, which is how the component licenses are accepted.
Each project's sidebar Android section lists its artifacts. The devices adb devices reports (serial, and
whether it is an emulator or in a state that cannot take an install) and the device tools — deployment target,
pairing, environment check, logcat, databases, and the emulator and AVD of each application's android.device —
appear once per workspace, in the top-level project's Android devices & tools section, however many Android
members it has. Library-only members get no emulator rows. The artifacts' device targets:
| Action | Invocation |
|---|---|
| Run an APK | elide build android-run-<artifact> with the selected --device=<serial>, or android-run-emulator-<artifact> for the explicitly selected project AVD. |
| Debug an APK | elide build android-debug-<artifact>, which installs it, launches it waiting for a debugger and forwards its JDWP socket; the extension attaches to the port the target announces. The APK must be debuggable (android:debuggable="true"): newer Elide releases add it to a release = false build, and Debug is offered only for an APK Elide lists android-debug-<artifact> for. |
| Build an APK, AAB or AAR | elide build <artifact> → .dev/artifacts/<name>.apk / .aab / .aar |
| Build a dynamic-feature module | elide build android-module-<feature> → .dev/android/<feature>/<feature>.zip; build its owning AAB to publish the application. |
| Open the R8 mapping | A minify = true artifact gets a mapping row beside it, which opens .dev/artifacts/<name>-mapping.txt — publishing it with elide build android-mapping-<artifact> when it is not there, since building the artifact alone does not — the only way to read a stack trace from a shrunk build. |
| Start / stop the emulator | elide build android-emulator (--no-window when elide.android.emulatorWindow is off), elide build android-emulator-stop |
| Create the AVD | elide build android-avd |
| Create the signing keystore | elide build android-keystore-<artifact> |
Unit tests of an Android project are plain JVM tests and run in the Test Explorer as usual.
Use Select Android Deployment Target…, the Android sidebar action, or the device status-bar item to choose an application and destination. The workspace remembers the project, APK and device for both Run and Debug; each deployment checks the current APK model and asks adb for the device's current state. A disconnected, offline or unauthorized selected device stops deployment rather than silently switching to another device. Explicit artifact/device rows override the remembered choice. With no explicit project, a runnable application owning the active file takes precedence; focusing a library does not make that library a run target.
Run and Debug save dirty file-backed documents first and stop if saving fails. Choosing the project emulator
uses Elide's existing emulator targets. A selected emulator that has stopped is booted before Run or Debug,
through android-emulator for the project's AVD or the SDK emulator for any other, and is found again by AVD
identity; ambiguous instances are refused, and a serial reused by another AVD cannot redirect a remembered
deployment. Cancelling startup prevents deployment even if an adb readiness reply arrives afterward.
Late picker, device-discovery and startup replies cannot replace a newer deployment selection.
elide-android launch configurations also validate their APK/device and accept flags, options and env,
layered over workspace defaults. The selected device takes precedence over build options.
Check Android Environment is available from the command palette, Elide menu and project context menu, including when sync has failed. It checks Elide, a usable JDK, SDK/platform-tools, devices and Kotlin extension availability. A synced model additionally supplies the compile SDK, AVD and APK declarations. Unknown declarations stay marked unknown; an absent optional emulator is not a physical-device failure. Select a finding for settings, sync, output or the relevant SDK/device action. Inspection never installs components, accepts licenses or builds the project. The full report is written to Elide Output. It also detects symlinked workspace paths: JetBrains source breakpoints can report “Source file not found” there. Use the offered Open canonical folder in a new window action for source debugging.
The device tree refreshes after Elide tasks and through Refresh Android Devices; no background adb polling.
Android commands, menu entries and context-menu actions appear only when a synced project declares an android
block (the elide.hasAndroidProjects context key); Check Android Environment also appears while a project is not
synced yet. Invoked anyway, they warn that no synced project declares an android block.
Open Elide: Pair Android Device over Wi-Fi… from the Command Palette or Elide menu, or Pair device over Wi-Fi in the sidebar's Android devices & tools section. Requires Android 11+, current SDK Platform-Tools, and the device and extension host on the same reachable network; no USB connection or Android Studio installation is needed.
- Enable Developer options → Wireless debugging on the device.
- Choose Generate QR code in the panel and Pair device with QR code on the device. Scan using the Wireless debugging scanner. Alternatively, choose Six-digit code, open Pair device with pairing code on the device, refresh discovery, and select its address (or enter it manually) and six-digit code.
- ADB normally connects automatically after pairing. If the device is missing, use the panel's Connect an already-paired device section with the IP address & port from the main Wireless debugging screen. This is a different port from the pairing dialog. Refresh Android Devices if auto-connection arrives later.
- Select the connected device with Select Android Deployment Target… for Run and Debug. Wireless devices also appear in the existing Logcat and database device pickers; pairing does not replace a pinned target.
Discovery uses mDNS; QR pairing waits for its exact generated service for two minutes. On networks that block
mDNS, use manual pairing and connection addresses instead. Check VPNs, firewalls and guest-network isolation
if connections fail. In Remote SSH or containers, the SDK and network are those of the extension host, not the
local VS Code window. The extension does not enable legacy unencrypted adb tcpip debugging.
Failures quote what adb reported. Connect first checks, for up to 5 seconds, that the address accepts a connection, so an unreachable or refused address fails promptly with the likely cause instead of waiting for adb. The pairing port is not probed that way, since a stray connection could count as a failed pairing attempt.
Pairing codes are not saved or logged, are passed to ADB through stdin, and are masked wherever adb echoes them in an error. Closing the panel or cancelling stops its work and discards the QR code, but cannot undo pairing already accepted by the device. To revoke trust, select this workstation under the device's Paired devices and choose Forget.
Elide: Open Compose Preview renders locally using Google's standalone Compose renderer and Android layoutlib. It requires no device, emulator, Android Studio installation, signing keystore, or debuggable APK. The extension builds JVM classes and linked Android resources, not DEX or an installable application.
Enable kotlin.features.compose, include Compose ui-tooling, sync, and open a Kotlin file containing
@Preview / @Composable functions; the editor-title button appears on such files of an Android application. The
gallery renders the file's compiled previews, including annotation configuration, member functions, multi-preview
annotations and @PreviewParameter providers. Kotlin/Java edits, including unsaved changes, use persistent
incremental compilers and renderers. Resource/configuration saves rebuild the resource context, chosen automatically
as the application packaging the file or its library; a rebuild that changes only resources or R classes keeps the
warm compiler.
Kapt processors such as Room can live in the preview-bearing module. The live worker uses that module's
recorded Kotlin toolchain, compiles against private generated inputs, then verifies kapt stubs in the background.
Body-only changes can confirm without reprocessing. Declaration changes rerun processors, but retain warm
compiler state and the displayed images when generated sources, classes and resources are unchanged—for
example, when adding a composable beside Room entities. Changed generated output is recompiled and repainted.
Processor errors remain visible instead of confirming stale images, and only confirmed galleries are cached. This requires
Elide's kotlinc.toolchain.json, kapt.args, and kapt.generated.json records; rebuild after upgrading Elide.
The themed gallery retains its last good images through compilation errors and swaps decoded updates without blanking the canvas. Enable Inspect for measured layout outlines, selection and dimensions, and source navigation to each component's call site, including workspace-library call sites; navigation pauses while its preview is out of date. A failed preview build must succeed on a later refresh before the gallery is marked current; a Kotlin edit alone cannot bypass failed resource preparation. Closing a preview stops its build, including pending preparation.
The experimental komodo engine (elide.android.composePreview.engine = komodo, with elide.kotlinLsp.server =
komodo) renders every card live inside the workspace's Komodo language server, from its own analysis of unsaved
Kotlin edits: clicks run handlers, compile errors land in Problems, and Inspect selects composable calls by their
measured bounds and opens their call sites, also after live edits. Click or Tab to a live card to send keys only to that card: text
input, modifiers, Backspace/Delete, arrows, Enter and Tab between Compose controls. Press F6 or Shift+F6 to
leave; ordinary Escape still reaches Compose. Leaving the canvas, switching cards or enabling Inspect releases
held keys. IME composition and clipboard paste are not supported. There is no fallback to layoutlib.
Native previews are effectively disabled while Komodo is missing or incompatible, without changing your engine preference or installing anything automatically. Opening, prewarming and agent rendering check compatibility before building or preparing preview resources. Losing availability closes native panels, cancels in-flight work and retires agent sessions and warm native backends; layoutlib and source-only agent tools remain available. Install Komodo explicitly with Elide: Install Komodo, correct its settings or sync to recheck compatibility, then reopen the preview or start a new agent render. A rejected native session protocol stays disabled until that recheck, without build loops.
The first preview downloads a pinned, SHA-256-verified renderer/runtime from Google Maven into extension storage.
Subsequent previews reuse it offline. A matching-architecture JDK 17+ is required; supported extension hosts are
macOS arm64/x64, Linux x64 and Windows x64. The default layoutlib engine produces snapshots; neither engine is
interactive device mirroring or in-process Live Edit. See the extension guide for setup
and runtime limitations.
Elide builds with Android support (currently the internal release's distribution; not yet in a public Elide
release) support kapt and Hilt Android entry-point transformation.
Declare processors in dependencies.maven.processors, use a managed/external Kotlin toolchain with kapt,
and enable android.hilt for Hilt. Sync discovers the project's actual compile targets, generates processor
sources and imports the Java/kapt output roots without importing kapt stubs. Workspace AARs retain generated
components and Hilt aggregation metadata, allowing application bindings to come from sibling libraries.
Hilt bindings owned by dynamically delivered code cannot be installed into the base component; use Dagger component dependencies against an entry point exposed by the base app.
Use workspace AndroidAar projects for reusable libraries. Inside the application project, an AndroidAab
lists its features; each AndroidFeature identifies that AAB through base, owns disjoint source sets, and
can declare featureDependencies. The sidebar shows the owning base, delivery mode and feature dependencies.
Features are buildable modules, not independently runnable APKs.
Delivery is on-demand by default or install-time. Install-time features remain removable splits, preserving
their identity when another feature depends on them. Elide packages base/shared classes once and gives each
feature its own resources, assets and dex. Install an AAB through bundletool or Play; the APK Run/Debug commands
do not perform Play delivery. These capabilities require an Elide build with Android support.
Elide: Show Android Logcat starts streaming without confirming filters: it uses the application being edited or
the Android deployment target (project, device, application), else the only ready device and the project's
application, reading the application ID from the built APK so renamed variants match. Several variants pick the one
installed on the device; only a genuinely ambiguous project, device or application is offered as a choice. New
streams show Info and above; showing a running stream reveals it without restarting. The Android tree and device
context menu expose it too.
Records are aligned into time, PID-TID, tag, priority and message columns, with stack traces and multi-line
messages indented under one header and priorities, tags, exceptions and application frames color-coded by the theme.
Each project owns an output channel and adb stream. Filter, Stop, and Restart Android Logcat control
the stream; Clear Android Logcat Output clears only the editor, not the device's shared log buffers.
Filter Android Logcat is one input holding the current filter, e.g. package:com.example.app level:warn Exception:
package: names the application (leave it out for all), level: the minimum priority (verbose, debug, info,
warn, error, fatal or V…F; Info when absent), and every other word is case-insensitive literal text;
"quote" text with spaces or colons. A new, restarted or refiltered stream does not replay the device's whole
buffer: filtered to a running application, it starts at that application's current process start; otherwise it
starts now. Package filtering uses the installed UID, surviving process restarts and including secondary processes
and any apps sharing that UID. On Android 12+ it uses logcat --uid; on Android 7–11 it follows only the app's running main process (--pid), so restart logcat after the app restarts.
Streams and pending device/application discovery stop when their project is removed or the extension closes;
cancelled discovery cannot open a late application picker.
The startup header reports the requested device-clock timestamp, not reader readiness. Android 16 emulator
qualification found records emitted immediately after starting a timestamp-filtered stream missing from live
adb logcat output but present in a later dump; direct adb reproduces this without the extension. For startup
diagnosis, retain adb -s <serial> logcat -d -v threadtime output as well. The extension does not treat a fixed delay as proof of readiness.
Ctrl/Cmd-click a Kotlin or Java application stack frame to open its source line. Navigation indexes the synced source roots across workspace modules and uses both package and source filename, including nested classes and Kotlin file facades. Native/unknown frames, out-of-range lines and ambiguous same-package filenames remain unlinked. Obfuscated release traces still require the matching R8 mapping; this does not perform retracing.
Run Elide: Explore Android Databases, or choose Explore databases in the sidebar's Android devices & tools
section or a device's context menu. Select an installed application on the connected device. Requires Android 8.0 /
API 26 or newer, a debuggable app that permits adb run-as, and a trusted, synced Android project. The application
picker asks run-as about every installed application first: debuggable ones are listed as openable, and the rest
appear under Cannot be opened with the reason, e.g. a release build that is not debuggable.
- Browse databases, tables, views, indexes and trigger definitions; inspect rows in 100-row pages.
- Run one read-only SQL query, with exact 64-bit integers, NULLs and Base64 BLOBs. Use
ORDER BYfor predictable paging. - Refresh manually or every 2 / 5 seconds while the panel is visible. Refresh re-runs the last successful read, not an edited SQL draft. A failed or cancelled query clears that read; Refresh then updates only the catalog until another query succeeds, rather than showing older rows under the failed or cancelled SQL.
- Execute write… confirms the app, database, device and SQL before committing a single statement with foreign keys enabled. Reads never implicitly become writes. Cancellation during a write leaves its outcome unknown; refresh before retrying.
A bundled Android helper opens the app's actual SQLite files, including committed WAL data; no database copy,
root access, device sqlite3 executable or app dependency is required. One helper process runs per open panel and
serves all of its requests over its pipes, so a query costs tens of milliseconds rather than a VM start; a helper
that fails or is cancelled is stopped, on the device too, and the next request starts a fresh one. It is scoped to
the foreground Android user recorded by the package picker, and refuses access if that user changes—even before
the first query. Select the application again under the new profile. Closing the panel stops its requests and
helper and removes its temporary DEX on a best-effort basis.
This is not Android Studio's in-process inspector: external writes do not notify Room observers, and pages do
not share a retained snapshot. It inspects on-disk SQLite files under the app's databases/ directory, not
in-memory, encrypted, device-protected or custom-location databases. PRAGMA statements, ATTACH,
transaction-control/maintenance commands, multiple statements and compound CREATE TRIGGER bodies are not
supported; read what a pragma reports through its table-valued function, e.g.
SELECT * FROM pragma_table_info('items'). Results are bounded to 4 MiB, 256 columns and 256 KiB per cell; oversized
values fail explicitly rather than being silently truncated. Select smaller values or fewer columns when needed.
JavaScript/TypeScript build extensions use VS Code's built-in TypeScript language service. In an
Elide project (an ancestor contains elide.pkl), the extension supplies the build API declarations:
ElideBuild, task contexts/options, state, inputs/outputs and ctx.tools. Completion, hover,
Go to Definition and type diagnostics work before the first sync, with or without tsconfig.json.
No dependency install, generated configuration or Kotlin language server is needed. Existing
project-provided global ElideBuild declarations take precedence over the bundled API.
- Create
tools/tasks.tsand insert Elide build extension from Insert Snippet (prefixelide-build-extension). Useelide-build-taskto add another task. The JavaScript snippet supplies the equivalent JSDoc annotation; enable// @ts-checkfor JavaScript type diagnostics. - Declare the module in
elide.pkl:Local literal declarations, includingextensions { "tools/tasks.ts" }
new { from = "tools/tasks.ts" config { ... } }, link directly to their files from the current editor buffer. Shared member paths such as../tools/tasks.tsstay within the Elide workspace. Links never fetch remote extensions. Computed paths, raw/multiline Pkl strings and files outside the source tree are not linked. - In Elide → Tasks → Build targets, run the task your extension declares. Save module/helper edits to refresh the task list; this does not re-sync JVM dependencies. Builds use saved files.
- Package reusable extensions with
Extensions.ExtensionArchive. Its manifest Build lens runspackage-extension-<artifact key>and writes the.elxarchive. With Pkl language support installed,elide-extensioninserts a declaration andelide-extension-archiveinserts an artifact (afterimport "elide:Extensions.pkl" as Extensions).
The runnable example is samples/build-extension. Build/sync require
an Elide distribution supporting build extensions, selected with elide.home; some older
development distributions share a version number without supporting them. Editor declarations
are a bundled API snapshot, not a CLI capability probe or a substitute for declarations in a
standalone tsc build. They do not install Node.js types or change the project's compiler settings.
| Setting | Default | Description |
|---|---|---|
elide.home |
"" |
Distribution root containing bin/elide. |
elide.jdk.home |
"" |
JDK for symbol resolution; else jvm.javaHome, $JAVA_HOME, installed JDKs matching jvm.target. |
elide.sync.onStartup |
true |
Sync when the workspace opens. |
elide.sync.onManifestChange |
"prompt" |
always / prompt / never when elide.pkl changes, or when the lockfile's resolved dependencies do. Every elide invocation rewrites .dev/elide.lock*.bin, so rewrites with unchanged content are ignored. |
elide.kotlinLsp.writeWorkspaceJson |
true |
Write <folder>/workspace.json for the JetBrains server; never for a Komodo folder. |
elide.kotlinLsp.server |
"jetbrains" |
komodo (experimental): native Kotlin services resolved and installed on demand by Elide. |
elide.kotlinLsp.komodo.path, .pluginDirectory, .javaHome, .maxHeap |
"" |
Developer overrides for executable, compiler plugins, JDK inputs and heap. Empty uses Elide's managed resolution. |
elide.install.classifiers |
["sources"] |
Classifiers installed for declared Maven packages (sources, docs); empty installs classes only. Elide's own Kotlin/JUnit jars have none. |
elide.flags |
[] |
-f NAME[=VALUE] build flags for every invocation, sync included; changing them marks the model stale. |
elide.build.options |
{} |
Default options for elide build, e.g. { "no-cache": true }. |
elide.run.options |
{} |
Default options for elide run. |
elide.test.options |
{} |
Default options for elide test; Test Explorer runs use them too, except reporter. |
elide.install.options |
{} |
Default options for the elide install task. |
elide.codeLens.enabled |
true |
Show the Run/Debug code lenses described above. |
elide.debug.adapter |
"intellij" |
intellij, java or komodo; unset, a Komodo folder uses komodo. |
elide.debug.nativeMiMode, elide.debug.miDebuggerPath |
"auto", "" |
GDB/LLDB selection for elide-native Native Image sessions. |
elide.android.sdk |
"" |
SDK root the device list is read from; empty resolves the one a build would use. |
elide.android.emulatorWindow |
true |
Boot the emulator with a window; off passes --no-window. |
elide.android.composePreview.prewarm |
true |
Start the Compose preview backend when a file with @Preview functions is focused. |
elide.android.composePreview.keepAliveMinutes |
10 |
Minutes an unused Compose preview backend stays warm; 0 stops it with its panel. |
elide.android.composePreview.engine |
"layoutlib" |
komodo (experimental): live cards in the workspace's Komodo language server; requires elide.kotlinLsp.server = komodo. |
packages/core—@elide/ide-core: editor-agnostic library (Elide discovery, CLI runner, manifest decoding, project model,workspace.jsonemitter). No VS Code dependency; reusable by other TypeScript-based editor integrations.packages/vscode— the extension; itsCHANGELOG.mdis the release history shown on the Marketplace, generated from commit subjects by release-please when a release is cut.samples/ktjvm— Kotlin/JVM sample used by the integration test.tools/deploy.sh— packaging and publication toplugins.elide.dev.tools/bazel— repository-local Bazel rules (a hermetic Bun for the unit tests).
Building needs only Bazelisk; Bazel fetches every other tool at a pinned
version, and outputs stay in bazel-bin/, so several worktrees can build and test at once.
bazel test //... # core (tsc) + extension (esbuild, type-check) + .vsix + unit tests
bazel run //:gazelle # regenerate BUILD files after adding or moving sources
bazel run //packages/vscode:integration_test # drives real VS Code + Kotlin LSP against samples/ktjvmThe integration test requires VS Code at /Applications/Visual Studio Code.app, JetBrains.kotlin-server installed
in ~/.vscode/extensions, elide installed, and network access (it adds Guava to the sample). Press F5 in this repo to
run the extension against samples/ktjvm. CONTRIBUTING.md has the full dev loop.
Issues and pull requests are welcome. CONTRIBUTING.md covers the dev loop, the commit convention
(Conventional Commits, enforced on pull requests), and the release process; participation is governed by the
Code of Conduct. Report vulnerabilities privately as described in
SECURITY.md — not in a public issue.
MIT — see LICENSE.
{ "type": "elide", "command": "test", "args": ["src/api"], "options": { "bail": 3, "test-timeout": 5000 }, "programArgs": ["--filter", "slow"] }