Skip to content

Configuration Reference

Complete .wowluarc.json schema. For practical guidance, see the Configuration guide.

Editor support

A JSON Schema is provided for autocompletion and validation. The VS Code extension registers it automatically. For other editors, add a $schema property to your config file:

json
{
  "$schema": "https://raw.githubusercontent.com/TradeSkillMaster/wowlua-ls/main/editors/vscode/wowluarc.schema.json",
  "flavors": ["retail"]
}

Schema

json
{
  "addonRoot": false,
  "ignore": ["string"],
  "library": ["string"],
  "framexml": true,
  "flavors": ["retail", "classic", "classic_era"],
  "globals": {
    "read": ["string"],
    "write": ["string"],
    "allowSlashCommands": true,
    "allowBindingGlobals": true
  },
  "inference": {
    "backwardParamTypes": true,
    "correlatedReturnOverloads": true,
    "implicitProtectedPrefix": false
  },
  "hint": {
    "enable": true,
    "parameterNames": true,
    "variableTypes": true,
    "functionReturnTypes": false,
    "forVariableTypes": true,
    "parameterTypes": false,
    "chainedReturnTypes": false
  },
  "codeLens": {
    "enable": true,
    "references": true,
    "implementations": true,
    "overrides": true
  },
  "editor": {
    "autoInsertEnd": true
  },
  "completion": {
    "snippets": true,
    "callSnippets": true
  },
  "plugins": ["string"],
  "diagnostics": {
    "disable": ["string"],
    "enable": ["string"],
    "severity": {
      "diagnostic-code": "warning | info | hint"
    }
  }
}

Key naming

Multi-word keys are camelCase (e.g. addonRoot, backwardParamTypes). The former snake_case spellings (addon_root, backward_param_types, …) are still accepted for backward compatibility — the JSON Schema marks them deprecated, so editors flag them with a strikethrough rather than as invalid — but the camelCase form is canonical and recommended.

Fields

addonRoot

  • Type: boolean
  • Default: false

Marks this directory as a separate addon root. In multi-addon workspaces, each addon root gets its own isolated addon namespace table (local _, ns = ...), so fields defined in one addon aren't visible in another. Lua globals remain shared across all addon roots.

json
{ "addonRoot": true }

Not needed for single-addon projects.

plugins

  • Type: string[]
  • Default: []

Paths to Lua diagnostic plugin scripts. Relative to the config file's directory. Isolated per file: only the plugins declared by a file's nearest config run against it. Plugin lists are not inherited from parent configs. See the Diagnostic Plugins guide for the full API.

json
{ "plugins": [".wowlua-ls/my-check.lua"] }

ignore

  • Type: string[]
  • Default: []

Path prefixes to exclude from scanning. Relative to the config file's directory. Patterns ending with / match directory prefixes. Entries may also use glob wildcards: * (any characters within a path component), ? (single character), and ** (any number of directory levels).

json
{ "ignore": ["Libs/", "External/*.lua", "Generated/**/*.lua"] }

Built-in default

.github/ directories are always skipped - no configuration needed. They hold GitHub repository metadata and CI/build tooling (build scripts run by the standalone Lua interpreter, where io/os/etc. are real standard-library globals), never addon code that ships in-game, so analyzing them as WoW Lua would only produce spurious diagnostics. Anything you add to ignore is excluded in addition to this default.

library

  • Type: string[]
  • Default: []

Paths to scan for type information but with all diagnostics suppressed. Useful for third-party libraries where you want classes, globals, and type information available to the rest of your project, but can't fix any diagnostic issues in the library code. Relative entries use the same pattern syntax as ignore. Absolute paths, and relative paths that point outside the workspace (for example ../shared), are also supported for libraries that live next to your addon rather than inside it.

json
{ "library": ["Libs/", "../shared-libs", "/home/user/shared-libs/"] }

Unlike ignore (which skips files entirely), library files are fully scanned and analyzed: their @class, @alias, global functions, and other type information are available throughout the workspace. Only diagnostic output is suppressed.

External library directories are automatically added as extra scan directories, so libraries don't need to be inside your workspace. A relative entry that escapes the workspace root (one containing ..) is resolved against the directory holding the .wowluarc.json and scanned as an external directory. Prefer this over an absolute path for libraries checked into version control, since it stays portable across machines.

Marking a directory as a library suppresses diagnostics for the whole subtree, including any nested .wowluarc.json files inside it. A vendored library that ships its own .wowluarc.json (for example one pulled in via a symlink) cannot re-enable diagnostics for itself. The parent's library declaration wins. See Hierarchy behavior for how this differs from other settings.

Symlinked or duplicated library paths are scanned once

If the same physical library is reachable through several workspace paths — for example a library that is both its own workspace folder and symlinked into each addon's Libs/ — the language server detects the shared filesystem identity (device + inode) and analyzes it a single time. Go-to-definition, find-references, and code-lens counts resolve to the real source rather than a vendored alias, and you do not need to ignore the extra copies to avoid duplicate definitions. This applies to symlinks, hardlinks, and Windows junctions; two genuinely separate copies of a library on disk (each a real file with its own inode) are still analyzed independently.

framexml

  • Type: boolean
  • Default: true

Whether FrameXML API globals are available. Set to false to treat FrameXML-specific globals as undefined.

flavors

  • Type: string[]
  • Default: [] (flavor filtering disabled)
  • Values: "retail" (alias "mainline"), "classic", "classic_era"

WoW flavor names the project targets. Enables wrong-flavor-api diagnostic when non-empty.

Note: Flavor filtering can also be derived automatically from .toc file listings - see the Flavor Filtering guide. When both sources are present, the effective flavor for each file is the intersection of the project-level flavors and the TOC-derived per-file flavor.

Note: The deprecated diagnostic is also flavor-aware: a retail-only deprecation (e.g. GetMerchantItemInfo) isn't flagged in an addon that targets Classic, where the API is still live. It derives the addon's targeted flavors from this setting, else a flavor-specific .toc, else the .toc ## Interface: versions (so it works even without a flavors config). See Flavor-aware deprecation.

globals.read

  • Type: string[]
  • Default: []

Global names that may be accessed without triggering undefined-global. Entries may use glob wildcards: * (any characters) and ? (single character).

json
{ "globals": { "read": ["LibStub", "MyAddon*Mixin"] } }

Tip: SavedVariables and SavedVariablesPerCharacter declared in .toc files are automatically added to both globals.read and globals.write - no manual configuration needed.

globals.write

  • Type: string[]
  • Default: []

Global names that may be created/assigned without triggering create-global. Entries may use glob wildcards: * (any characters) and ? (single character).

json
{ "globals": { "write": ["MyAddon*", "SavedVar*"] } }

globals.allowSlashCommands

  • Type: boolean
  • Default: true

Automatically treat globals matching SLASH_* as allowed write/read globals. WoW slash commands are defined by assigning SLASH_COMMANDNAME1, SLASH_COMMANDNAME2, etc. to global variables, so these are always intentional. Set to false to require explicit listing in globals.write.

globals.allowBindingGlobals

  • Type: boolean
  • Default: true

Automatically treat globals matching BINDING_HEADER_* and BINDING_NAME_* as allowed write/read globals. WoW keybinding labels are defined by assigning BINDING_HEADER_ADDON and BINDING_NAME_ACTION to global variables, and the binding system reads them at runtime. Set to false to require explicit listing in globals.write.

Automatic dynamic prefix detection

In addition to the settings above, the language server automatically detects dynamic global creation via _G["PREFIX" .. key] = value (or _G[name .. "SUFFIX"]) patterns in your workspace. When detected, a wildcard glob (e.g. PREFIX*) is registered so reads of those globals don't trigger undefined-global. This requires no configuration. The prefix/suffix must be at least 3 characters to avoid overly broad matching.

inference.backwardParamTypes

  • Type: boolean
  • Default: true

Infer parameter types from body usage (arithmetic, concatenation, typed-function argument calls).

inference.correlatedReturnOverloads

  • Type: boolean
  • Default: true

Infer correlated return patterns (all-set-or-all-nil) for automatic sibling narrowing.

inference.implicitProtectedPrefix

  • Type: boolean
  • Default: false

Treat runtime-discovered data fields starting with _ as implicitly protected. Does not affect explicit @field declarations or methods.

hint.enable

  • Type: boolean
  • Default: true

Master switch for inlay hints. When false, no inlay hints are shown regardless of individual category settings.

hint.parameterNames

  • Type: boolean
  • Default: true

Show parameter name hints at call sites (e.g. foo(/*name:*/ "hello")). Suppressed when the argument text already matches the parameter name, and on a trailing call/... argument that sits before another named parameter (e.g. foo(unpack(t)) where foo takes two params) — its values may fan out across those later params, so a single name label would misrepresent the mapping.

hint.variableTypes

  • Type: boolean
  • Default: true

Show inferred type hints on local variable declarations that have no explicit @type annotation. Suppressed for nil, any, and function-valued variables.

hint.functionReturnTypes

  • Type: boolean
  • Default: false

Show inferred return type hints on function definitions that have no @return annotation.

hint.forVariableTypes

  • Type: boolean
  • Default: true

Show inferred type hints on for ... in loop variables.

hint.parameterTypes

  • Type: boolean
  • Default: false

Show inferred type hints on function parameters that have no @param annotation. Suppressed for self, any, and nil parameters.

hint.chainedReturnTypes

  • Type: boolean
  • Default: false

Show intermediate return type hints in method chains. When a method call's return value is immediately used as the receiver of another method/field access, the return type is shown after the closing ). Suppressed when the return type is any, nil, or ?.

codeLens.enable

  • Type: boolean
  • Default: true

Master switch for code lenses. When false, no code lenses are shown regardless of individual category settings.

codeLens.references

  • Type: boolean
  • Default: true

Show "N usages" lenses on function definitions.

codeLens.implementations

  • Type: boolean
  • Default: true

Show "N implementations" lenses on @class declarations.

codeLens.overrides

  • Type: boolean
  • Default: true

Show "overrides Parent" lenses on methods that override a parent class method. Clicking the lens navigates to the overridden (parent) method's definition.

editor.autoInsertEnd

  • Type: boolean
  • Default: true

Automatically insert end or until when Enter is pressed after a block-opening keyword (if … then, while … do, for … do, function, repeat). The closing keyword is only inserted when the block isn't already closed further down in the file.

json
{ "editor": { "autoInsertEnd": false } }

completion.snippets

  • Type: boolean
  • Default: true

Emit snippet completions (InsertTextFormat.Snippet). This covers both function-call parameter auto-fill and annotation-tag bodies (e.g. @param). Set to false to disable all snippet completions; items then insert plain text only. Requires snippet support from the editor.

completion.callSnippets

  • Type: boolean
  • Default: true

Auto-fill a function's parameters when you complete a function name, e.g. completing strmatch inserts strmatch(${1:s}, ${2:pattern}) with the cursor on the first parameter. Set to false to insert just the function name (strmatch) and let you type the call yourself. This is independent of completion.snippets: annotation-tag snippets still work when only callSnippets is disabled.

json
{ "completion": { "callSnippets": false } }

diagnostics.disable

  • Type: string[]
  • Default: []

Diagnostic codes to suppress.

diagnostics.enable

  • Type: string[]
  • Default: []

Diagnostic codes to enable. Used for default-off diagnostics or to counteract a disable entry in the same config.

diagnostics.severity

  • Type: Record<string, "warning" | "info" | "hint">
  • Default: {}

Override severity for specific diagnostic codes.

Hierarchy behavior

When .wowluarc.json files are nested, settings combine according to one of these policies:

  • Isolated: the single nearest ancestor config fully determines the setting. Keys it does not set fall back to their default, not to a parent config's value. This applies to everything that affects diagnostics, so that running a check from a subdirectory produces the same results as running it from the project root (configs above the scan root are never consulted).
  • Inherited: the deepest config that sets the key wins, falling back to ancestor configs and then the default. This applies to editor-experience settings that do not affect diagnostics.
  • Inherited downward: the library setting alone uses this: once any ancestor config marks a subtree as a library, every file beneath it has diagnostics suppressed, and a nested config inside that subtree cannot opt back in. (Whether a subtree is a library still affects only diagnostics, so this is the one diagnostics setting that is not isolated. Its entire purpose is to silence a whole vendored subtree, including any config files that subtree carries.)
SettingPolicy
diagnostics.disableIsolated: nearest config's disable only
diagnostics.enableIsolated: applied after the nearest config's disable
diagnostics.severityIsolated: nearest config's severity map only
globals.readIsolated: nearest config only (includes that directory's .toc SavedVariables)
globals.writeIsolated: nearest config only
globals.allowSlashCommandsIsolated
globals.allowBindingGlobalsIsolated
framexmlIsolated
flavorsIsolated (then intersected with any TOC-derived per-file mask)
inference.*Isolated
ignoreIsolated: nearest config's patterns, relative to its directory
libraryInherited downward: any ancestor config that marks a subtree as a library suppresses diagnostics for that whole subtree, and a nested config inside it cannot un-mark itself; absolute library directories are scanned workspace-wide
pluginsIsolated: only the nearest config's plugins run against a file
hint.*Inherited
codeLens.*Inherited
editor.*Inherited
completion.*Inherited
addonRootNearest (deepest) addonRoot: true wins (structural)

Isolated settings do not inherit

If a nested directory has its own .wowluarc.json, it only inherits the inherited settings above. Any isolated setting it does not restate reverts to its default. It does not pick up the parent's value. For example, a subdirectory config that only sets diagnostics.enable will lose a parent's flavors and framexml settings unless it repeats them. This also applies to auto-discovered TOC SavedVariables globals. They are merged into the config entry for the directory containing the .toc file, so a child config in a subdirectory will not see them.

The one exception is library, which is inherited downward.