Skip to content
elide-devPublic

About

VSCode extension for Elide

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Elide for Visual Studio Code

CI License: MIT

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.

Install

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-*.vsix

Or in VS Code: Extensions ▸ … ▸ Install from VSIX…. https://plugins.elide.dev/vscode lists the published version and a pinned (&version=) link.

Requirements

  • Elide 1.5+ installed (~/.local/share/elide, $ELIDE_HOME, or elide on PATH). 1.6.1+ adds batched classpath queries; older releases resolve one query at a time.
  • The Kotlin by JetBrains extension (JetBrains.kotlin-server, ≥ 0.0.11), installed automatically as a companion extension. Complete its one-time region / data-sharing setup for Kotlin/Java analysis and JVM debugging. Elide activates independently: sync, build and environment diagnostics do not wait for the language server. Opening an Elide Kotlin or Java source document starts the companion when needed.
  • 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 a release file, which IntelliJ needs to enumerate modules.

How it works

Opening a folder that contains elide.pkl runs a sync:

  1. elide project export — the resolved project manifest as JSON (source sets, Kotlin compiler options, entrypoints). Elide 1.5 answers the same document to elide manifest, which 1.6 retired; the reported version decides which is run.
  2. elide install — only when the lockfile (.dev/elide.lock*.bin) is older than the manifest.
  3. The compile classpath of every compilable source set: one elide classpath --json <set>:<usage>… per project on Elide 1.6.1+, one elide classpath <set>:<usage> per query before that.
  4. workspace.json is 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.jar attached when present, the selected JDK as SDK, Kotlin language/API level and free compiler args as kotlinc flags).
  5. Two Kotlin LSP settings are maintained: intellij.buildTool is pinned to json in the workspace, and intellij.jdkForSymbolResolution is 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, 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.

Build tasks are read from Elide's ordinary text output when it reports them. The extension does not request structured progress records or infer percentages and task totals that the CLI does not provide.

Mixed-editor checkouts (.idea, Gradle, Maven)

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.

Settings the extension writes

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.

Commands

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, its steps and their durations.
Elide: Open Activity Log Open the latest operation's output, with timestamps, in an editor.
Elide: Check Android Environment Inspect the installed SDK, platform-tools and connected devices without requiring a synced Elide project.
Elide: Show Android Logcat Stream device logs with application, priority and text filters.

Tasks

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.

{
  "type": "elide",
  "command": "test",
  "args": ["src/api"],
  "options": { "bail": 3, "test-timeout": 5000 },
  "programArgs": ["--filter", "slow"]
}

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. javac diagnostics interleave symbol:/location: lines between the two and are shown in the terminal only.

Run and debug code lenses

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.

Test Explorer

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.

Debugging

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) or Debugger for Java (java, requires vscjava.vscode-java-debug). Stopping the session terminates the Elide process. The JDWP agent always binds port 5005, so one debug session at a time.

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" }
}

Android SDK tools

The extension supports standalone device diagnostics and Logcat through an independently installed Android SDK. It does not build or deploy Android applications, manage emulators, or render Compose previews: those integrations depend on upstream runtime capabilities that are not released.

Set elide.android.sdk, or use $ANDROID_HOME, $ANDROID_SDK_ROOT, or the conventional SDK location (~/Library/Android/sdk, ~/Android/Sdk, %LOCALAPPDATA%\Android\Sdk). Check Android Environment inspects SDK/platform-tools and connected-device state. It never installs SDK components, accepts licenses, builds, or deploys an application.

Logcat

Show Android Logcat selects an open workspace folder and a connected device, then streams all applications. No elide.pkl or successful sync is required. Filter Android Logcat can select an application ID manually or read it from an existing APK, including renamed application IDs, and apply priority and literal-text filters. Showing a running stream reveals it without restarting.

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 workspace folder 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. Restart and filter changes replay available device logs. Package filtering uses the installed UID, surviving process restarts and including secondary processes and any apps sharing that UID. It requires logcat --uid. Streams stop when their workspace folder is removed or the extension closes.

Ctrl/Cmd-click a Kotlin or Java application stack frame to open its source line. Navigation indexes synced source roots and ordinary unsynced workspace folders, using 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.

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.
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 or java.
elide.android.sdk "" SDK root for standalone diagnostics and Logcat; else environment and conventional SDK locations.

Repository layout

  • packages/core — @elide/ide-core: editor-agnostic library (Elide discovery, CLI runner, manifest decoding, project model, workspace.json emitter). No VS Code dependency; reusable by other TypeScript-based editor integrations.
  • packages/vscode — the extension; its CHANGELOG.md is 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 to plugins.elide.dev.
  • tools/bazel — repository-local Bazel rules (a hermetic Bun for the unit tests).

Development

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/ktjvm

The 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.

Contributing

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.

License

MIT — see LICENSE.

About

VSCode extension for Elide

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages