Skip to main content

Editor Setup

Manifests and specs are Lua, so any editor with a lua-language-server client gives you completion, hover documentation, and diagnostics on envy's API. envy ships the type definitions and writes the configuration that points the server at them.

What envy sets up​

envy init creates .luarc.json next to the manifest:

$ envy init . bin
Created bin/envy
Created ./envy.lua
Created ./.luarc.json
Updated ./.gitignore

Initialized envy project.
.luarc.json
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "Lua 5.4",
"workspace.library": [
".envy/cache/envy/0.3.0",
"~/Library/Caches/envy/envy/0.3.0",
"~/.cache/envy/envy/0.3.0",
"${env:USERPROFILE}/AppData/Local/envy/envy/0.3.0"
],
"diagnostics.globals": [
"envy", "IDENTITY", "PACKAGES", "DEPENDENCIES", "PRODUCTS",
"FETCH", "STAGE", "BUILD", "INSTALL", "SETUP", "ENVY_IMPORTER"
],
"completion.enable": true
}

Four library paths: the project-local cache tree first, then one per platform's default cache location, with ${env:USERPROFILE}/AppData/Local/envy covering Windows. It is a union rather than a choice, because .luarc.json is committed while the cache root is per-user state: the same manifest is local for whoever ran envy cache --local and shared for everyone else. Only one path exists on any given machine and the language server ignores the rest, which is why this file can be committed and shared. If your project sets @envy cache-local, that path replaces the default .envy/cache entry, relative rather than absolute so the file names no machine.

${env:VAR} is the language server's own placeholder syntax, so it is expanded by the server rather than by a shell, and forward slashes are correct on Windows too. Each path points at envy/<version> in the cache, where envy extracts envy.lua, a few hundred lines of LuaCATS annotations covering envy.run, envy.path, envy.commit_fetch, the phase globals, and the rest of the API.

Commit .luarc.json. It is machine-independent and version-matched to the envy the project pins.

Keeping it current​

The library paths name an exact envy version, so they go stale when the project moves. sync and deploy fix them:

$ envy use 0.3.0
$ envy sync
Updated .luarc.json types paths

The rewrite removes any workspace.library entry that ends in envy/<semver> and appends the current four. Everything else in the file is left alone, so your own settings and library paths survive:

{
"workspace.library": [
"/my/other/lua/lib",
".envy/cache/envy/0.3.0",
"~/Library/Caches/envy/envy/0.3.0",
"~/.cache/envy/envy/0.3.0",
"${env:USERPROFILE}/AppData/Local/envy/envy/0.3.0"
],
"custom.setting": "preserved"
}

Two opt-outs, both by omission:

  • Delete .luarc.json and envy stops creating or touching it. sync and deploy only update a file that already exists.
  • Remove the workspace.library key and the rewrite skips the file entirely.

init will not overwrite an existing .luarc.json either. It prints the paths instead:

$ envy init . bin
.luarc.json already exists at ./.luarc.json
To enable envy autocompletion, add the following to workspace.library:
".envy/cache/envy/0.3.0"
"~/Library/Caches/envy/envy/0.3.0"
"~/.cache/envy/envy/0.3.0"
"${env:USERPROFILE}/AppData/Local/envy/envy/0.3.0"

It prints the same four paths it would have written, so pasting them in gets you the same result.

Globals​

diagnostics.globals is what stops the language server from reporting undefined-global on the names envy assigns meaning to:

GlobalWhere
envyeverywhere, the API table
IDENTITY, DEPENDENCIES, PRODUCTS, PLATFORMSspecs
FETCH, STAGE, BUILD, INSTALL, SETUPspecs, the phase verbs
OPTIONS, USER_MANAGED, EXPORTABLE, VENDOR, DISPLAYspecs
PACKAGES, DEFAULT_SHELL, PACKAGE_DEPOTS, VENDOR_ROOTmanifests
ENVY_IMPORTERan imported manifest, set by envy.import
ENVY_BUNDLEa module loaded out of a bundle, from envy 0.4.7. See ENVY_BUNDLE.
ENVY_SHELLboth, the shell constants

The list init writes covers the common ones. Add what your project uses, for example BUNDLES in a manifest with a bundle, or BUNDLE and SPECS in a bundle repo:

"diagnostics.globals": [
"envy", "IDENTITY", "PACKAGES", "BUNDLES", "DEPENDENCIES", "PRODUCTS",
"FETCH", "STAGE", "BUILD", "INSTALL", "SETUP", "ENVY_IMPORTER",
"ENVY_BUNDLE"
]

Nothing breaks without them. The names are still resolved at run time by envy, and the only cost is a warning squiggle.

Per editor​

The language server reads .luarc.json from the workspace root, so open the directory holding the manifest.

  • VS Code: install the Lua extension. It picks up .luarc.json with no further configuration.
  • Neovim: enable lua_ls, through nvim-lspconfig or vim.lsp.config on 0.11 and later. It finds .luarc.json by root marker.
  • Zed: Lua support is built in and uses the same server.
  • Anything else: run lua-language-server over the workspace. The configuration is in the file, not in the editor.

In a monorepo, each manifest directory gets its own .luarc.json from init, but the language server only reads the one at the workspace root. Open a component directory directly when editing its specs, or keep the root file authoritative and let the others be redundant.

See also​