Configuration
wowlua-ls is configured via .wowluarc.json files placed in your project directories. No configuration is required to get started — the defaults work for most addons.
File placement
Config files are hierarchical, like .gitignore. Place one at your workspace root for project-wide settings:
MyAddon/
├── .wowluarc.json ← project-wide config
├── Core/
│ └── Core.lua
├── Libs/
│ ├── .wowluarc.json ← directory override (e.g., ignore everything)
│ └── LibStub/
└── Tests/
├── .wowluarc.json ← enable strict diagnostics for tests
└── TestSuite.luaSettings merge across the hierarchy:
ignorepatterns are relative to the config file's directory- Disabled diagnostics and allowed globals are unioned across ancestors
diagnostics.enableapplies afterdiagnostics.disableat each level (a child can re-enable what a parent disabled)- Severity overrides from deeper configs take precedence
framexmluses the nearest (deepest) config value
Full reference
{
"ignore": ["Libs/", "External/"],
"framexml": false,
"flavors": ["retail", "classic"],
"globals": {
"read": ["LibStub", "AceDB"],
"write": ["MyAddonDB", "SLASH_MYADDON1"]
},
"inference": {
"backward_param_types": true,
"correlated_return_overloads": true,
"implicit_protected_prefix": false
},
"diagnostics": {
"disable": ["unused-local", "inject-field"],
"enable": ["need-check-nil"],
"severity": {
"unused-local": "warning",
"unused-function": "warning"
}
}
}ignore
Array of path prefixes to exclude from scanning. Relative to the config file's directory. Patterns ending with / match directory prefixes.
{ "ignore": ["Libs/", "External/", "scratch.lua"] }Use this for vendored libraries, generated code, or anything you don't want diagnostics on.
Files starting with a shebang (#!/usr/bin/lua) are always skipped automatically — no ignore entry needed.
framexml
Whether FrameXML API globals are available. Default: true.
{ "framexml": false }Set to false to treat FrameXML-specific globals (e.g. SetUIPanelAttribute) as undefined. Useful for library code that shouldn't depend on FrameXML.
flavors
Array of WoW flavor names the project targets. Enables the wrong-flavor-api diagnostic. See Flavor Filtering.
{ "flavors": ["retail", "classic"] }When omitted or empty, flavor filtering is disabled.
globals
Declare external globals that shouldn't trigger diagnostics:
{
"globals": {
"read": ["LibStub", "AceDB", "AceLocale"],
"write": ["MyAddonDB", "SLASH_MYADDON1"]
}
}read— global names that may be accessed withoutundefined-globalwrite— global names that may be created/assigned withoutcreate-global
Use read for globals provided by other addons or libraries not in stubs. Use write for globals your addon intentionally exports.
SavedVariables and SavedVariablesPerCharacter from .toc files are automatically added to both lists — you don't need to configure them manually.
inference
Control the LS's type inference behavior:
| Setting | Default | Description |
|---|---|---|
backward_param_types | true | Infer parameter types from body usage (arithmetic, concatenation, typed-function args) |
correlated_return_overloads | true | Infer correlated return patterns for sibling narrowing |
implicit_protected_prefix | false | Treat _-prefixed data fields as implicitly protected |
{
"inference": {
"backward_param_types": false
}
}Set backward_param_types to false in strict-typing projects where you want unannotated parameters to stay visible as unknown types.
Set correlated_return_overloads to false if the inferred narrowing suppresses need-check-nil warnings you actually want.
Set implicit_protected_prefix to true if your project follows the _-prefix convention for internal fields and you want access-protected diagnostics on external access. See Implicit protected for _ prefixes.
diagnostics
Fine-grained control over which diagnostics fire and at what severity:
{
"diagnostics": {
"disable": ["unused-local", "inject-field"],
"enable": ["need-check-nil", "implicit-nil-return"],
"severity": {
"unused-local": "warning",
"unused-function": "warning"
}
}
}disable— suppress these diagnostic codesenable— opt into diagnostics that are off by default, or override a parent'sdisableseverity— override severity:"warning","info","hint"
Diagnostics disabled by default
These diagnostics are off unless you explicitly enable them:
| Code | Why it's off by default |
|---|---|
need-check-nil | Noisy on unannotated codebases |
implicit-nil-return | Stylistic — bare return in optional-return functions |
unused-vararg | Many Lua functions accept ... by convention |
incomplete-signature-doc | Only useful for teams enforcing documentation standards |
unknown-param-type | Requires thorough annotation coverage |
unknown-return-type | Requires thorough annotation coverage |
unknown-local-type | Requires thorough annotation coverage |
unknown-field-type | Requires thorough annotation coverage |
Recommended starting config
For a typical WoW addon:
{
"ignore": ["Libs/"],
"diagnostics": {
"enable": ["need-check-nil"]
}
}For a multi-flavor addon:
{
"ignore": ["Libs/"],
"flavors": ["retail", "classic"],
"diagnostics": {
"enable": ["need-check-nil"]
}
}For strict typing:
{
"ignore": ["Libs/"],
"diagnostics": {
"enable": [
"need-check-nil",
"unknown-param-type",
"unknown-return-type",
"unknown-local-type"
]
}
}Inline suppression
Any diagnostic can be suppressed on a per-line basis with @diagnostic:
---@diagnostic disable-next-line: unused-local
local unused = computeSomething()Or for a block:
---@diagnostic disable: undefined-global
MY_GLOBAL = true
OTHER_GLOBAL = false
---@diagnostic enable: undefined-globalAuto-reload
Config files are automatically reloaded when saved. No need to restart the language server.
