{"id":51099800,"url":"https://github.com/clang-engineer/jvm-env.nvim","last_synced_at":"2026-06-26T07:01:15.577Z","repository":{"id":365580908,"uuid":"1272336804","full_name":"clang-engineer/jvm-env.nvim","owner":"clang-engineer","description":"Auto-detect installed JDKs into Neovim env vars (separate jdtls / Gradle JDKs)","archived":false,"fork":false,"pushed_at":"2026-06-18T00:08:16.000Z","size":38,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-18T02:10:09.810Z","etag":null,"topics":["java","jdtls","lua","neovim","neovim-plugin"],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clang-engineer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-17T14:08:55.000Z","updated_at":"2026-06-18T01:09:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/clang-engineer/jvm-env.nvim","commit_stats":null,"previous_names":["clang-engineer/jvm-env.nvim"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/clang-engineer/jvm-env.nvim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clang-engineer%2Fjvm-env.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clang-engineer%2Fjvm-env.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clang-engineer%2Fjvm-env.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clang-engineer%2Fjvm-env.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clang-engineer","download_url":"https://codeload.github.com/clang-engineer/jvm-env.nvim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clang-engineer%2Fjvm-env.nvim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34806448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","jdtls","lua","neovim","neovim-plugin"],"created_at":"2026-06-24T10:00:24.356Z","updated_at":"2026-06-26T07:01:15.563Z","avatar_url":"https://github.com/clang-engineer.png","language":"Lua","funding_links":[],"categories":["Programming Languages Support"],"sub_categories":["Diagnostics"],"readme":"# jvm-env.nvim\n\n[![lint](https://github.com/clang-engineer/jvm-env.nvim/actions/workflows/lint.yml/badge.svg)](https://github.com/clang-engineer/jvm-env.nvim/actions/workflows/lint.yml)\n[![test](https://github.com/clang-engineer/jvm-env.nvim/actions/workflows/test.yml/badge.svg)](https://github.com/clang-engineer/jvm-env.nvim/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n\u003e **Status**: experimental (v0.1.x) — actively used by the author on macOS,\n\u003e partially tested on Linux/Windows. Feedback and issues welcome.\n\n**Auto-detect installed JDKs by major version and inject their paths into Neovim env vars.**\nConfigure separate JDKs for jdtls (the language server) and Gradle (the build tool).\n\n```lua\nrequire(\"jvm-env\").setup({ jdtls = \"21\", gradle = \"17\" })\n-- → vim.env.JDTLS_JAVA_HOME  = \u003cJDK 21 home\u003e\n-- → vim.env.GRADLE_JAVA_HOME = \u003cJDK 17 home\u003e\n```\n\n## What it does\n\n- Tries `jenv prefix` / `/usr/libexec/java_home` / `~/.sdkman` / `/usr/lib/jvm/*` / standard Windows JDK paths / scoop **per OS, in order** to locate the JDK home for the requested major version.\n- Writes the resolved path into `vim.env.JDTLS_JAVA_HOME` and `vim.env.GRADLE_JAVA_HOME`.\n- If nothing is found, warns via `vim.notify` and keeps going — never blocks other startup.\n- Run `:checkhealth jvm-env` to see which managers are visible and which paths each version resolves to.\n\n## What it does NOT do\n\n- **Does not start any LSP.** Your jdtls spec (nvim-jdtls, LazyVim Java extras, etc.) is still yours to write.\n- **Does not install JDKs.** It only finds JDKs you already installed (via jEnv, SDKMAN, brew, apt, scoop, …).\n- **Does not touch standard `JAVA_HOME`.** Your shell's `JAVA_HOME` is left alone; jvm-env uses two dedicated variables instead, deliberately avoiding conflicts with other tools.\n\n## Why\n\nA common Java setup:\n\n- jdtls wants a recent JDK (e.g. 21) to run reliably.\n- The project itself targets an LTS JDK (e.g. 17) for Spring Boot 3.x, Android, etc.\n\nToggling shell `JAVA_HOME` for every project is awkward, and the `nvim-jdtls` README's hard-coded `cmd = { '/usr/lib/jvm/...' }` does not survive cross-platform / multi-version setups. jvm-env is a **thin helper that takes a major-version string and resolves the right path for the current OS**.\n\n## Adjacent tools\n\n| Tool | Scope |\n|---|---|\n| **mason.nvim** | Installs LSP servers like jdtls. Does not install JDKs. |\n| **nvim-jdtls** | jdtls integration for Neovim. JDK paths are your responsibility. |\n| **nvim-java** | Full stack (LSP + DAP + tests). Can install JDKs via mason. |\n| **LazyVim java extras** | Auto-wires jdtls. Recommends a hard-coded `vim.env.JAVA_HOME = ...`. |\n| **jvm-env (this plugin)** | **OS/manager detection + split jdtls/Gradle env vars.** No LSP. |\n\nThese are complementary. If you already use a full-stack solution like `nvim-java`, you don't need this. jvm-env is for users layering light automation on top of `nvim-jdtls` or LazyVim Java extras.\n\nThe \"auto-detect JDK by major version and inject into env vars\" niche is not covered by the above — see [nvim-jdtls discussion #548](https://github.com/mfussenegger/nvim-jdtls/discussions/548) and [mason.nvim discussion #1893](https://github.com/mason-org/mason.nvim/discussions/1893), where the answer is consistently \"set `JAVA_HOME` yourself.\" jvm-env fills that gap.\n\n## Requirements\n\n- Neovim 0.10+ (uses `vim.uv` and the new `vim.health` API).\n- One JDK manager / install path supported below (jEnv, SDKMAN, Homebrew, apt, scoop, Adoptium, …).\n\n## Install\n\n[lazy.nvim](https://github.com/folke/lazy.nvim):\n\n```lua\n{\n  \"clang-engineer/jvm-env.nvim\",\n  lazy = false,           -- vim.env must be set before jdtls starts\n  priority = 100,         -- load before LSP/jdtls related plugins\n  opts = {\n    jdtls = \"21\",\n    gradle = \"17\",\n  },\n}\n```\n\nLeave `opts` empty to keep the defaults (`jdtls = \"21\"`, `gradle = \"17\"`).\n\n## Usage\n\n### Default\n\n```lua\nrequire(\"jvm-env\").setup()\n-- fills both env vars with the defaults (jdtls=21, gradle=17)\n```\n\n### Custom versions\n\n```lua\nrequire(\"jvm-env\").setup({ jdtls = \"21\", gradle = \"17\" })\n```\n\n### Disabling one side\n\nPass `false` to skip detection for that env var entirely (no warning, no value set):\n\n```lua\nrequire(\"jvm-env\").setup({ jdtls = \"21\", gradle = false })\n-- only JDTLS_JAVA_HOME is touched\n```\n\n### Wiring up jdtls (LazyVim + nvim-jdtls)\n\n`lua/plugins/java.lua`:\n\n```lua\nreturn {\n  {\n    \"mfussenegger/nvim-jdtls\",\n    opts = function(_, opts)\n      local function present(v) return v ~= nil and v ~= \"\" end\n\n      -- jvm-env (loaded eagerly above) has already populated these.\n      local jdtls_home  = vim.env.JDTLS_JAVA_HOME\n      local gradle_home = vim.env.GRADLE_JAVA_HOME\n      if not present(jdtls_home) then return opts end\n\n      local cmd = { vim.fn.exepath(\"jdtls\") }\n      table.insert(cmd, \"--java-executable\")\n      table.insert(cmd, jdtls_home .. \"/bin/java\")\n\n      opts.jdtls = vim.tbl_deep_extend(\"force\", opts.jdtls or {}, {\n        cmd = cmd,\n        cmd_env = present(gradle_home) and {\n          JAVA_HOME   = gradle_home,\n          GRADLE_OPTS = \"-Dorg.gradle.java.home=\" .. gradle_home,\n        } or nil,\n      })\n    end,\n  },\n}\n```\n\nKey points:\n\n- `cmd` uses `--java-executable` to pin the JDK that runs jdtls → reads `JDTLS_JAVA_HOME`.\n- `cmd_env.JAVA_HOME` isolates the JDK used by the Gradle process spawned by jdtls → reads `GRADLE_JAVA_HOME`.\n\n### Per-project versions via `.nvim.lua`\n\n`.nvim.lua` (loaded per directory via Neovim 0.9+ `exrc`):\n\n```lua\nrequire(\"jvm-env\").setup({ jdtls = \"21\", gradle = \"17\" })\n```\n\nOr run `:JvmEnvInit 21 17` (versions optional — falls back to the active config) in the project root to write that line for you.\n\nEnable with `vim.o.exrc = true` and `:trust` the file once. Reopening Neovim inside that directory switches to the project-specific versions automatically.\n\n## Detection order\n\n| OS | Order |\n|---|---|\n| **macOS** | 1. `jenv prefix \u003cver\u003e` (if `jenv` is installed) → 2. `jenv versions --bare` filtered to `\u003cver\u003e.*` (exact fallback) → 3. `/usr/libexec/java_home -v \u003cver\u003e` → 4. Homebrew `/opt/homebrew/opt/openjdk@\u003cver\u003e` / `/usr/local/opt/openjdk@\u003cver\u003e` → 5. `~/.sdkman/candidates/java/\u003cver\u003e.*` |\n| **Linux** | 1. `/usr/lib/jvm/java-\u003cver\u003e-openjdk` → 2. `/usr/lib/jvm/java-\u003cver\u003e-openjdk-amd64` → 3. `/usr/lib/jvm/jdk-\u003cver\u003e` → 4. `/usr/lib/jvm/jdk-\u003cver\u003e.*` (versioned) → 5. `/usr/lib/jvm/java-\u003cver\u003e.*` (versioned) → 6. `~/.sdkman/candidates/java/\u003cver\u003e.*` |\n| **Windows** | 1. `%ProgramW6432%` / `%ProgramFiles%` / `C:\\Program Files` × Eclipse Adoptium / Java / Microsoft, matching `jdk-\u003cver\u003e` then `jdk-\u003cver\u003e.*` → 2. scoop `openjdk\u003cver\u003e/current` |\n\nThe order is: precise version managers first, then standard install paths, then per-manager fallbacks. When multiple patch versions match (e.g. `jdk-21.0.1`, `jdk-21.0.10`), the highest one is selected via natural (numeric) ordering so `21.0.10` outranks `21.0.9`.\n\n`:checkhealth jvm-env` prints the detected manager presence and the path each configured version resolves to.\n\n## Environment variable naming\n\n`JDTLS_JAVA_HOME` / `GRADLE_JAVA_HOME` are **not** standard variables recognized by jdtls or Gradle. They are this plugin's convention, and your jdtls spec must explicitly read them (see the wiring example above).\n\nWhy not reuse `JAVA_HOME`:\n\n- A single `JAVA_HOME` cannot separate jdtls from Gradle.\n- Overriding the shell `JAVA_HOME` affects unrelated tools.\n\n## API\n\n### `setup(opts)`\n\n| Key | Type | Default | Description |\n|---|---|---|---|\n| `jdtls` | string \\| false | `\"21\"` | JDK major version used to run jdtls. Pass `false` to skip. |\n| `gradle` | string \\| false | `\"17\"` | JDK major version used by Gradle. Pass `false` to skip. |\n\nVersions are major-version strings (e.g. `\"21\"`). Exact versions (e.g. `\"21.0.1\"`) also work if `jenv` / `java_home` can match them, but the major version is usually enough.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclang-engineer%2Fjvm-env.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclang-engineer%2Fjvm-env.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclang-engineer%2Fjvm-env.nvim/lists"}