Skip to content
ag-libsPublic

About

No description, website, or topics provided.

Resources

Stars

25 stars

Watchers

1 watching

Forks

Latest commit

 

History

1,371 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lathe

Maven Central CI

A Java language server that works from your build — no project import, no classpath setup.

Lathe is a Java language server — code intelligence, diagnostics, and run, test, and debug — driven by your build rather than a separate project model. It is built on the JDK's own Java compiler, so its analysis matches what javac sees.

If you've fought a Java LSP, the pain is usually project import and classpath or module-path config drifting from the build. Lathe skips that: its model comes straight from your Maven build — the exact configuration Maven compiles, tests, and runs with — so the setups hardest to get right in an editor (modular projects, annotation processors, plugins that add source roots) tend to just work.

Because the editor uses the build's own configuration, it stays in sync with it — diagnostics are what the compiler reports, and runs and tests replay the real launch without a Maven rebuild.

Setup is one extension registration, a first build, and a plugin line in your Neovim config.

Lathe ships a Neovim client, a VS Code extension, and an MCP server for AI coding agents.

Beyond Maven, Lathe also works from the OpenJDK make build.

Demo

A ~1-minute walkthrough — capture your Maven build's classpath once, then a zero-config Java IDE: completion and live diagnostics, running a modular main, debugging with live expression eval, and tests — all replayed from your build.

Lathe demo — a zero-config Java IDE from your Maven build

Features

Lathe implements the standard LSP feature surface, plus run, test, and debug. Every capability is available to any LSP client; see Editors for the client that drives them and its key bindings.

Code intelligence

Feature What it does LSP method
Go to definition jumps to local sources, unpacked dependency JAR sources, and JDK sources textDocument/definition
Go to declaration navigates to the overridden interface or abstract-method contract textDocument/declaration
Implementation / subtypes concrete implementations of a method, or all subtypes of a type across the workspace textDocument/implementation
Find references usages across the workspace textDocument/references
Highlight uses read/write uses of the symbol under the cursor, within the current file textDocument/documentHighlight
Rename renames a symbol and every reference across the whole reactor — all modules — as one atomic edit (locals, fields, methods incl. the override family, record components, constructors) textDocument/rename · textDocument/prepareRename
Instantiation sites where a type is instantiated (new AppServer(...)), from the type under the cursor workspace/executeCommand · lathe.instantiations
Hover AST-resolved Javadoc, rendered as Markdown textDocument/hover
Signature help parameter lists for methods and constructors textDocument/signatureHelp
Completion types, methods, and variables, with automatic import insertion textDocument/completion
Document / workspace symbols file outline; workspace search with CamelCase-hump matching (ASF finds AbstractServerFactory) textDocument/documentSymbol, workspace/symbol
Type hierarchy supertypes and subtypes of the symbol under the cursor, one level at a time textDocument/prepareTypeHierarchy
Full type hierarchy all transitive supertypes and subtypes of the type under the cursor at once, tagged by relation workspace/executeCommand · lathe.typeHierarchy
Add missing imports resolve every unimported type in the file in one pass — unambiguous names are added automatically, ambiguous ones prompt workspace/executeCommand · lathe.missingImports
Call hierarchy incoming and outgoing calls of a method textDocument/prepareCallHierarchy
Semantic tokens highlights static/deprecated members, enum constants, type parameters, annotations textDocument/semanticTokens/full
Folding classes, methods, blocks, and import groups textDocument/foldingRange

Diagnostics & formatting

Feature What it does LSP method
Diagnostics javac errors and warnings exactly as configured in Maven, plus unused private members and locals textDocument/publishDiagnostics
Code actions quick fixes and refactors: missing imports, add throws, wrap in try/catch, declare local, replace var with the inferred type, extract variable / constant / field, add a final field as a constructor parameter, stub a missing method textDocument/codeAction
Formatting (per-workspace) whole-document google-java-format / AOSP, or the project's own Spotless formatter via mvn spotless:apply, or a custom external command — auto-detected per project textDocument/formatting

Formatting follows a per-workspace style, auto-detected from the project's spotless-maven-plugin by lathe:sync (a committed lathe-style.json or the editor's global default otherwise). It is advertised only when a formatter resolves: the built-in google/aosp engine (fast, in-process); a non-google Spotless formatter (eclipse, palantir, …) delegated to mvn spotless:apply on the edited file (preferring mvnd → ./mvnw → mvn), so the editor applies the project's own formatter; or a custom stdin/stdout command. Opt out with -Dlathe.spotless=false or a committed lathe-style.json. Live-edit indentation follows the same style file and is otherwise a separate client concern.

Run, test & debug

Feature What it does LSP method
Run a main replays a main from captured .lathe/ bytecode — no Maven rebuild, live output workspace/executeCommand · lathe.run.main
Tests discovers and runs tests (method, class, or package) from .lathe/ bytecode, with live output and a diagnostic on the failing assertion workspace/executeCommand · lathe.runnables.list, lathe.run.test
Debug conditional breakpoints, stepping, variable inspection, and REPL expression evaluation over DAP workspace/executeCommand · lathe.debug.*, then DAP
Run configuration auto-applied overlays (JVM/program args, env, cwd, class-/module-path) plus named, selectable run configs (:LatheRun {name}, saved from the cursor) lathe.run.named · lathe.runconfigs.list · lathe.runconfig.save

Run, test, and debug are Lathe extensions exposed through workspace/executeCommand (and the Debug Adapter Protocol for debugging), not standard LSP methods. The Neovim commands (:LatheRun, :LatheDebug, save/last/stop, the config picker) are in the Neovim cheatsheet and run configuration.

Scaffolding

Feature What it does Command
New type scaffolds a class / interface / record / enum / annotation / test, plus package-info and module-info — pick the kind, fuzzy-pick the destination package, and name it; the server resolves placement, writes the file, and opens it Neovim :LatheNew

:LatheNew <kind> adds a type in the current package; bare :LatheNew runs a guided module/package picker. The server owns placement — module, source root, package, and skeleton. Walkthrough →.

Workspace freshness

Lathe keeps its model in step with your build. Open files are analysed live as you edit and save; when sources or resources change outside the editor — a branch switch, a git pull, or an AI agent editing files — Lathe reconciles in-process automatically, recompiling the changed files in dependency order and cleaning up deletions with no Maven build. Only POM / module-structure changes prompt a full refresh.

OpenJDK

Lathe also works from the OpenJDK make build, not only Maven. The lathe-openjdk-maven-plugin:sync goal reads the build's per-module javac invocations and the JDK you built, and writes the same .lathe/ the language server consumes — so analysis is build-accurate across all ~66 modules and the make/ build tools, in any LSP client. Code intelligence only for now; test execution (jtreg) is not yet supported.

See the OpenJDK guide.

Editors

Lathe is a standard language server, so any LSP client can drive its build-derived intelligence. How much you get depends on the client:

Editor Integration Reference
Neovim Dedicated client (LSP + run/test/debug, scaffolding) Neovim cheatsheet — install and keymaps
Emacs Built-in Eglot (standard LSP, no plugin) Emacs (Eglot) guide
VS Code Extension — "Lathe for Java" (standard LSP) VS Code guide

The Neovim client adds Lathe-specific commands (:LatheRun, :LatheNew, neotest, format-on-save) on top of LSP. Everything else is plain LSP, so a standard client like Emacs + Eglot — or the VS Code extension — works with no extra configuration.

AI agents (MCP)

Lathe also drives AI coding agents. The same build-derived engine that powers the editor is exposed over the Model Context Protocol by lathe-mcp-server, so an agent gets javac-accurate, cross-module code intelligence instead of guessing from grep: compiler-truth diagnostics, navigation that follows into dependencies and generated sources, safe reactor-wide rename, test replay without a Maven build, and scoped recompile-and-verify of a change set. Works with any MCP client — Claude Code, OpenAI Codex CLI, Gemini CLI.

Like the editor, it reads from a populated .lathe/, so run a build once first.

Tool What it does
get_diagnostics compiler errors/warnings for one file — no Maven
get_definition resolve a symbol to its definition, incl. dependencies/JDK/generated
find_references every real use of a symbol across the reactor
find_implementations implementers of an interface / overrides of a method
call_hierarchy callers or callees of a method, across modules
search_symbols find a type by name (CamelHumps) across reactor, dependencies, JDK
describe_symbol signature, type, and javadoc for a symbol
rename_symbol rename a symbol across the whole reactor, applied to disk
run_test replay a test / class / package from captured bytecode — no build
analyze_change pre-edit impact of a symbol: override family, production/test reference counts, affected modules, relevant tests
verify_change recompile a change set in-process, report new diagnostics per module + the scoped mvn for cross-module impact — no build

Full setup — registering with Claude Code, Codex, and Gemini, the result contract, and the freshness model — is in the AI agents guide.

Requirements

  • Java 21+ — the same JDK your Maven build uses.
  • Maven 3.9+ — earlier 3.x releases are not supported. 3.9.x is verified.

Test run and debug have additional requirements (Surefire and JUnit Platform versions); see test-capture.md.

Setup

Set Lathe up once, in three steps.

1. Register the Lathe extension at your reactor root, as a Maven build extension — in .mvn/extensions.xml, or in your root pom.xml under <build><extensions> (alongside any extensions you already declare):

<extensions>
    <extension>
        <groupId>io.github.ag-libs</groupId>
        <artifactId>lathe-maven-extension</artifactId>
        <version>0.1.16</version>
    </extension>
</extensions>

See installation.md for details.

2. Generate the metadata once, and add .lathe/ to .gitignore:

mvn clean test -Dlathe.capture.only=true

This captures every launch template — compiler params, the workspace manifest, and each module's run/test launch — without running your test suite. -Dlathe.capture.only=true forks each module to snapshot its launch template but skips executing the tests; clean forces a first compile through Lathe.

Tip: the Maven Daemon (mvnd) noticeably speeds up these builds — run it in place of mvn where you can.

Note: if your build uses the Maven build cache extension, disable it for Lathe builds (-Dmaven.build.cache.enabled=false) — a cache hit skips compilation, so Lathe would not see the real build and its captured configuration would go stale.

3. Add the Neovim client. It's the standalone ag-libs/lathe.nvim plugin — install it like any other. With lazy.nvim:

{
  "ag-libs/lathe.nvim",
  ft = "java",
  cmd = "LatheStart",                             -- also start it from a non-Java buffer
  dependencies = { "mfussenegger/nvim-dap" },     -- optional: enables :LatheDebug
  config = function()
    require("lathe").setup()                       -- per-workspace style; `style`/`format_on_save` opts in the cheatsheet
    -- Lathe adds no maps of its own; a starting set for commands with no Neovim default:
    vim.keymap.set("n", "<leader>rr", "<cmd>LatheRun<cr>",          { desc = "Run main under cursor" })
    vim.keymap.set("n", "grN",        "<cmd>LatheInstances<cr>",     { desc = "Instantiation sites" })
    vim.keymap.set("n", "grh",        "<cmd>LatheTypeHierarchy<cr>", { desc = "Full type hierarchy" })
  end,
}

The config function calling setup() is required — without it the LSP server is never registered. Standard LSP actions (go-to-definition, references, rename, …) use Neovim's built-in defaults, so they work without extra maps. Full keymaps, formatting options, and the neotest test-runner integration are in the Neovim cheatsheet. Requires Neovim 0.12+.

After that, it keeps up on its own: every Maven build (mvn test, verify, install) refreshes Lathe's configuration, and Lathe reconciles source changes made outside the editor in-process. The added build cost is marginal — Lathe runs your real javac and just records its parameters (plus a one-time resolve of dependency and JDK sources). See what the build writes for the details.

How it works

Lathe has a few moving parts, each documented in depth. In brief — full mechanics in How Lathe works:

  • Build capture — every build records the exact compiler configuration and mirrors your compiled classes into .lathe/, which the language server reads.
  • Dependency & JDK sources — resolved and unpacked into ~/.cache/lathe/, so go-to-definition steps into library and JDK code.
  • Test capture — the test JVM is captured from inside your Surefire fork by live introspection and replayed against .lathe/ without a Maven rebuild. Details →
  • Run & debug — runs and debug sessions replay the captured launch in a fresh JVM; customize it with overlays and named, selectable run configs. Details →

Files and caches

Lathe writes per-project metadata to .lathe/ (add it to .gitignore) and machine-wide, regenerable data — the server, dependency/JDK sources, and indexes — to ~/.cache/lathe/ (relocate with -Dlathe.cache=<dir>, safe to delete). What each holds: what and where Lathe writes.

Opt-out and CI

Lathe is active by default and skips automatically in CI. To turn it off for a whole team, commit the property in the reactor pom.xml:

<properties>
  <lathe.disabled>true</lathe.disabled>
</properties>

Precedence:

Condition Effect
<lathe.disabled>true</...> in the reactor POM disabled for everyone building the repo
-Dlathe.disabled=true disabled regardless of other settings
-Dlathe.disabled=false enabled, overrides CI
CI environment variable is set Lathe does not run

When the POM opts out, a developer can opt back in by creating an empty .lathe/ at the repo root: it overrides the POM opt-out, so the next build wires Lathe up and repopulates the directory. (A -Dlathe.disabled=true or CI kill stays absolute — .lathe/ does not override it.)

Documentation

Troubleshooting

  • No Lathe features, or a "launcher not found" notice — Lathe isn't active in the build yet. Run a Maven build at the reactor root — mvn process-test-classes is the quickest (it generates Lathe's metadata without running tests). If it still isn't working, confirm the Lathe extension is registered (see installation.md).
  • The server won't attach, or crashes — set LATHE_DEBUG=1 before launching your editor and check its LSP log (Neovim: cheatsheet). An unexpected exit is also surfaced as an editor notification pointing at the log.
  • Another Java language server (jdtls) misbehaves when Lathe is present — a co-running Eclipse JDT LS scans Lathe's generated .lathe/ mirror and can treat it as duplicate projects. Add **/.lathe/** to its java.import.exclusions; see installation.md.
  • A processor or plugin needs extra JVM access in the editor — Lathe analyzes with an in-process javac and drops a forked build's -J options. Restore any it needs (or tune heap/GC) with LATHE_JVM_OPTS; see installation.md.

Feedback & contributions

Feedback, bug reports, and questions are welcome — please open an issue.

If you would like to contribute code, please open an issue to discuss the change before opening a pull request. Thank you for trying Lathe.

Maintainers: see RELEASING.md for the release process.

Built with AI assistance

Lathe is developed with the help of AI coding tools (primarily Claude Code). AI-assisted contributions are reviewed by a human maintainer, who takes responsibility for every change that lands, the same as for any hand-written code. Commits produced with AI assistance carry a Co-Authored-By trailer so the provenance stays visible in the git history.

AI-generated output is used only where it is compatible with this project's Apache-2.0 license, in line with the Apache Software Foundation's generative-tooling guidance.

License

Apache License 2.0 — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

25 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages