Skip to content

Language Server

Tua's language server provides compiler-backed editing for .tua projects. Diagnostics, completion, hover, navigation, refactors, formatting, and project inspectors read the same semantic analysis used by tua check and tua build.

Use the VS Code extension

The recommended setup is the Tua VS Code extension. Published packages include the matching server binary and start it automatically, so a separate CLI installation is not required.

The standalone server runs over standard input and output:

tua lsp

tua lsp --stdio is also accepted for editor-client compatibility. The server speaks JSON-RPC/LSP on stdout, so clients must not treat it as an interactive terminal command.

Find A Feature

Goal Feature
Understand a type or any result Hover, inlay hints, and Tua: Explain Type / Why Is This Any?
Complete Lua, Tua, LOVE, modules, or assets Contextual completion, snippets, and completion resolve
Move through a project Definition, type definition, implementation, references, symbols, call hierarchy, and module explorer
Change code safely Rename, quick fixes, source actions, and file-rename import updates
Inspect a LOVE project Asset, Signal, Module, ECS, Object Pool, and Sprite tools
Inspect generated output Tua: Open Generated Lua Beside Tua and source-map navigation
Diagnose server problems Tua output, language status, server health, traces, and debug information

Everyday Editing

Diagnostics And Incomplete Code

The server publishes parser, type, project, lint, asset, LOVE, and domain diagnostics with stable TLxxxx codes. They match tua check for the same project snapshot. See Diagnostic Codes for code definitions and Configuration for project settings.

Nested TL1003 errors identify the failing contract path, such as config.animations.green.row: expected number, found string, and highlight the corresponding table value when it is statically available. Root-level failures retain concise declared names such as Player.

Standard lint rules are published by default. Set diagnostics.rules = false (or use the tua.diagnostics.rules override) to disable them. Formatting differences are not diagnostics; editor formatting and tua fmt --check use the separate formatter.

During typing, Tua reports fast current-file diagnostics first and follows with project-aware results after a short debounce. Requests are tied to document versions; work from an older edit is cancelled or discarded instead of being published over newer text.

Open documents retain their last valid semantic facts while syntax is temporarily incomplete. Completion uses the current cursor context with those facts, while hover and inlay hints remain useful until valid project analysis is available again.

Exact-code inline suppressions use the same syntax as tua check and are enabled by default:

-- tua-ignore-file TL4048

-- tua-ignore-next-line TL4018
player.setFilter("nearest")

local legacy: number = "soon" -- tua-ignore-line TL1003

The file form must be the first nonblank line. Empty directives and broad category suppressions are invalid. Projects can disable source suppressions with [diagnostics].inline_ignores = false in tua.toml or tua.diagnostics.inlineIgnores in VS Code. Invalid codes and suppressions that match no diagnostic still produce warnings. A suppression hides a diagnostic; it does not change inference or generated Lua.

For diagnostics with a suppressible exact code, the editor offers Suppress TLxxxx for this line and Suppress TLxxxx for this file quick fixes. The line action uses a same-line comment when possible and preserves an existing trailing comment by inserting a next-line directive instead. The file action extends an existing header directive when present. These actions follow the project's enabled scopes and are absent when inline ignores are disabled.

Completion, Hover, And Signature Help

Completion combines syntax context with local and project semantics. It covers:

  • locals, parameters, functions, classes, fields, aliases, arrays, and module exports;
  • Lua builtins and supported LuaDoc tags, types, and generated annotations;
  • require(...) and static import(...) paths, module members, and auto-import candidates;
  • LOVE globals, object methods, enums, callbacks, and asset paths;
  • expected-type table constructors, including union discriminators, recursive array/table members, and contextually typed callbacks from configured declaration libraries;
  • table configuration for sprites, state machines, stores, ECS, object pools, Signals, and other Tua helpers;
  • statement snippets for if/then/end, loops, repeat/until, do/end, functions, classes, and common game-helper workflows.

Normal require(...) and non-relative static-import path completion use the same ordered workspace require roots as module resolution. Child-relative imports continue to complete from the current file's directory.

Function and method items can insert argument placeholders. Known literal choices become snippet choices, and LOVE object methods prefer colon-call syntax. Completion resolve adds documentation, signatures, source context, and LOVE wiki links when available.

Configured declaration-only libraries are indexed during workspace reindexing and refreshed for file changes reported by the editor. Their ---@meta modules can describe the runtime module returned by a different require(...) prefix, without copying the declarations into a build or fetching packages from the editor. See Declaration-Only Type Libraries.

Hover reports inferred or declared types, documentation, module context, asset metadata, and LOVE documentation. Short types stay inline, while complex tables, unions, callbacks, tuples, and dictionaries use width-aware multiline formatting. For an indexed assignment, hover reports the collection, index, and assigned value independently; the assignment operator itself is not presented as a comparison expression. Nested fields and callback parameters inside built-in helper configurations such as spriteSheet({...}), stateMachine({...}), and objectPool({...}) use their expected schema instead of collapsing to the enclosing call's result type. Signature help appears after ( and ,, re-evaluates at ), and supports overloads, optional parameters, typed varargs, multiple returns, callback aliases, and parameter documentation.

Static stateMachine({...}) calls share one derived model across completion, hover, signature help, checking, inlay hints, inspection, and the experimental source-linked graph preview. See State Machines for the generated event and callback surface.

Static store({...}) calls likewise share one inferred state model across completion, hover, signature help, checking, and module exports. Keyed watch signatures are derived from that model. See State Stores.

Static input({...}) calls derive Baton control and pair names once for completion, hover, signature help, checking, inlay hints, and module exports. Source strings complete LOVE keys, scancodes, gamepad axes, buttons, and hat directions. See Input.

Experimental completion stays local

Tua: Toggle Experimental Completions controls contextual sequence ranking and compiler-generated multi-token suggestions for the workspace. Both are off by default.

Tua: Toggle Local Ollama Completions separately opts into model-backed inline ghost text. The extension sends a bounded prefix, suffix, language contract, and visible type facts directly to a configured loopback Ollama server, then asks Tua to validate the candidate in a cloned analysis host. Normal completion-menu results never go through Ollama.

Local Ollama Inline Completion

Install Ollama separately, then pull the default model:

ollama pull qwen2.5-coder:1.5b-base

Run Tua: Test and Warm Ollama Model to preload it. Enable the feature with Tua: Toggle Local Ollama Completions after reviewing the source-sharing consent message. Tua never installs or pulls a model.

The first release accepts unauthenticated http:// endpoints only on localhost, 127.0.0.0/8, or IPv6 loopback. It keeps one Ollama request in flight and cancels it on edits, cursor movement, configuration changes, provider cancellation, or a newer request. Requests are skipped in comments, strings, multi-cursor sessions, and while a normal completion item is selected. These expected cancellations and document-version races are counted locally but do not produce warnings. Completion status distinguishes Tua language-server availability from Ollama transport, missing-model, and timeout failures.

Tua classifies each request as a local or function completion. Local expression and statement candidates remain bounded to eight lines and 1 KiB. Inside an unfinished function or method, Ollama may complete the remaining body and its closing end or } with up to 64 lines and 8 KiB. The default generation ceiling is 256 tokens, with a 5-second timeout; the function lane can use up to 512 configured tokens.

Tua removes FIM/end tokens and duplicated suffix text, then rejects Markdown, empty output, stale fingerprints, obvious foreign-language constructs such as TypeScript/JavaScript function syntax, and every newly introduced error. The model prompt is itself valid commented Tua source. It teaches that ordinary Lua/LuaJIT is the base, prefers inferred locals over redundant annotations, includes native LOVE 11.x guidance, and carries a bounded inventory of the implemented Tua type, class, module, interop, and game-helper features. Cursor-specific expected types and visible bindings appear before that inventory. Existing unrelated diagnostics and gradual any are preserved. The live document and analysis host are never mutated.

The extension stores only source-free local counters: request/cancellation/ timeout/empty/rejection counts, suggestions shown and accepted, and accepted character totals. They appear in copied debug information and are cleared by Tua: Reset Completion Learning Stats.

The feature remains experimental and disabled by default. It should not be considered for a different default until local dogfooding records at least 100 shown suggestions, at least 15% acceptance, and no regression in ordinary completion latency.

The custom semantic requests are tua/inlineCompletionContext and tua/validateInlineCompletion. Responses bind the document version, analysis revision, UTF-16 position, context ID, and source fingerprint.

Tua supports definition, type definition, implementation, references, prepare rename, rename, document highlights, document symbols, workspace symbols, document links, call hierarchy, and reference code lenses.

Cross-file navigation uses the project module graph for .tua and indexed .lua modules. Known aliases, exported types, class members, LuaDoc symbols, LOVE builtins, and static asset strings navigate to their source or generated stub. Dynamic dispatch through any, runtime-built tables, or unresolved modules returns no speculative location.

Built-in helper configuration fields navigate to generated virtual schema documents. Names derived from static source retain their source identity: store watcher keys navigate to the initial state field, sprite animation members navigate to the named animation entry, and state-machine event methods and callback keys navigate to the event or state string that created them.

Native class names, fields, and methods support definition, references, prepare rename, and project-wide rename. The same identity follows locally inferred instances and nominal instances exported through a static module, so navigating Module.player.health reaches the original class field rather than generated Lua or the module's player export. Resolved named table-shape fields are also renameable when Tua can prove their declaration identity.

Renaming a source or asset file can update unambiguous static require(...), import(...), and LOVE asset paths through LSP file-operation edits.

Inlay Hints And Highlighting

The server supplies inferred type hints and parameter-name hints. Use Tua: Select Inlay Hint Mode to choose:

  • Full for all server hints;
  • Compact for VS Code-truncated hints;
  • Types Only or Parameters Only;
  • Off for Tua files.

The extension combines immediate TextMate highlighting with compiler semantic tokens. Semantic tokens distinguish types, classes, enums, functions, methods, parameters, variables, properties, namespaces, events, labels, and annotations, including declaration, readonly, deprecated, modification, and default-library modifiers.

Resolved ---@deprecated declarations and uses receive the deprecated modifier. Required-module completion items use the standard LSP deprecated tag, and hover shows any replacement guidance carried by the annotation.

Full-document, range, and delta token requests use the same classification. Lossy semantic highlighting keeps useful categories during temporary syntax errors. The active theme controls colors; use Developer: Inspect Editor Tokens and Scopes to inspect the token at the cursor.

Themes that render variables and properties identically can override only Tua properties:

"editor.semanticTokenColorCustomizations": {
  "rules": {
    "property:tua": "#4EC9B0"
  }
}

Formatting

The server provides full-document, range, and on-type formatting. Invalid documents are not reformatted. Range formatting keeps edits inside the selected range, while on-type formatting limits edits to stable local changes after newlines and common closing syntax.

Project defaults come from tua.toml:

[format]
indent_width = 2
line_width = 100
quote_style = "preserve"
final_newline = true

VS Code can override them with tua.format.indentWidth, tua.format.lineWidth, tua.format.quoteStyle, and tua.format.finalNewline. A null editor value inherits the project setting.

Refactors And Code Actions

Quick fixes and source actions include:

  • fixing safe diagnostics in the current file;
  • suppressing one exact diagnostic code for its line or for the file; these explicit actions are excluded from safe fix-all;
  • manually renaming a duplicate table key when the intended replacement is known; this intent-dependent edit is excluded from safe fix-all;
  • removing unused local values and function declarations when their complete source range is compiler-proven;
  • organizing, sorting, and removing compiler-resolved unused static imports while preserving dynamic and shadowed calls;
  • changing known LOVE object calls from . to :;
  • generating missing core LOVE callbacks;
  • adding inferred local annotations;
  • extracting expressions to locals or parameterized local functions;
  • inlining stable local bindings;
  • generating named table types from stable local tables;
  • wrapping repeated zero-argument constructors in an objectPool helper.

Tua only offers a transformation when the compiler can establish a safe source range and identity. Dynamic cases remain unchanged.

Editing tua.toml

The VS Code client sends tua.toml documents to the same server. Configuration files receive section and key completion, enum and default-value completion, hover documentation, diagnostics, and unknown-key quick fixes. They are handled as project configuration rather than parsed as Tua source.

See Configuration for every option and its default.

LOVE And Project Tools

LOVE Authoring

The built-in LOVE catalog provides API completion, hover, signatures, overloads, object return types, enum values, and callback declaration snippets. Callbacks are checked against their known signatures while still allowing unused trailing Lua parameters to be omitted. Close callback-name typos can offer a rename fix.

Known object methods prefer :. Calling one with . reports TL4018 because the receiver would not be passed as self:

local player = image "assets/player.png"
player.setFilter("nearest")
local player = image "assets/player.png"
player:setFilter("nearest")

love.conf(t) completion includes known window and module fields plus literal choices such as fullscreen modes.

Assets And Sprites

Static asset paths in Tua helpers and supported native LOVE constructors receive completion, validation, hover metadata, definition, references, and rename. Diagnostics cover missing files, unsupported formats, incompatible loader types, and filesystem casing mismatches. Image metadata such as PNG dimensions is shown when it can be read cheaply.

The Tua Assets explorer groups indexed files by kind and provides preview, open, references, rename, and reveal actions. Unused-asset TL2009 hints are conservative because dynamically loaded files remain valid.

spriteSheet({...}) receives nested configuration completion and shared validation for image paths, grid geometry, frame ranges, durations, and loop callbacks. Set tua.preview.enabled to use the experimental Tua: Preview Sprite Animations webview.

Static state-machine configurations use the same preview gate for Tua: Preview State Machine. The graph highlights the declared initial state, expands multi-source and wildcard event routes, follows source edits, and navigates back to state and event declarations. Sprite sheets and state machines link directly to their previews from hover; recognized asset-path hovers likewise link to the native asset preview. Preview links trust only their registered Tua commands. The graph never runs the project or claims to show the machine's live runtime state.

Project Explorers

The extension renders project facts returned by compiler-backed requests. It does not parse Tua or resolve modules and assets independently.

Tool Shows
Tua Assets Assets grouped by kind, metadata, static references, and conservative unused hints
Tua: Show Signal Graph Typed events, buses, payloads, listeners, emitters, conflicts, and source navigation
Tua: Show Module Dependency Graph Tua/Lua modules, imports, missing or duplicate resolutions, cycles, reverse dependencies, exports, and inclusion paths
Tua: Show ECS Explorer Components, schemas, systems, queries, access patterns, and non-diagnostic performance insights
Tua: Show Object Pool Explorer Pool declarations, inferred item types, lifecycle calls, and conservative unreleased-acquire insights
Tua: Preview Sprite Animations Resolved sprite frames, timing, loop behavior, and the synchronized source configuration
Tua: Preview State Machine Initial state, statically derived states, source-linked event routes, wildcard transitions, and cycles

Module, ECS, object-pool, and asset inventory requests run on the background lane so completion and document edits remain responsive. Signal, sprite, and state-machine inspection still use synchronized compiler snapshots rather than extension-side source scans.

See Entity Component Systems and Object Pooling for the corresponding runtime APIs.

Generated Lua And Running LOVE

Tua: Open Generated Lua Beside Tua saves the active source, runs tua build with the extension's selected Tua executable from the nearest project root, and opens or reloads the emitted file beside it. The command uses the nearest tua.toml source, output, and main settings. A failed build does not open stale output, and an already-open generated file with unsaved manual edits must be saved or discarded before rebuilding.

Tua: Jump Between Tua and Generated Lua uses the emitted *.lua.map.json sidecar to move between source and output lines. Current source maps provide line provenance, so target columns begin at column zero.

Tua: Run LOVE Project builds and launches LOVE from the output directory in a visible, cancellable VS Code task. Tua: Restart LOVE Project replaces the tracked task. print(...), stdout, stderr, and mapped runtime traceback locations appear in the task terminal and the tua output channel. Configure a non-standard LOVE executable with tua.love.command.

Tua: Debug LOVE Project provides source-mapped LOVE debugging through an external Lua adapter. See Debugging for setup, runtime behavior, and launch guidance.

VS Code Commands

Open the command palette and type Tua. The most useful commands are:

Command Purpose
Tua: Initialize Tua Project Create the minimum tua.toml and source layout without overwriting existing files
Tua: Explain Type / Why Is This Any? Trace annotations, inference, narrowing, module resolution, and dynamic fallback points
Tua: Select Inlay Hint Mode Choose full, compact, filtered, or disabled hints
Tua: Open Generated Lua Beside Tua Rebuild and inspect emitted Lua for the active source
Tua: Jump Between Tua and Generated Lua Navigate through the generated source map
Tua: Run LOVE Project / Restart LOVE Project Build and run LOVE in a VS Code task
Tua: Debug LOVE Project Build and debug generated Lua with breakpoints mapped to Tua
Tua: Workspace Symbols by Module Browse compiler symbols grouped by module
Tua: Why Is This Module Included? Show the active module's shortest root-to-module path
Tua: Organize Imports / Sort Imports / Remove Unused Imports Apply compiler-backed source actions
Tua: Fix All Safe Diagnostics Apply safe fixes in the active file
Tua: Toggle Experimental Completions Toggle the two top-level experimental completion lanes
Tua: Focus Output Open server, build, and runtime logs
Tua: Show Language Server Health Inspect revisions, queues, cache rates, warming, stale results, and request latency
Tua: Restart Language Server Restart the current bundled or selected server
Tua: Select Server Choose the bundled server, a local executable, or a configured path
Tua: Copy Debug Info / Report Issue Collect version and workspace context for a bug report

Context menus and explorer views add symbol, hover, asset, and graph-specific actions when applicable.

Settings

Project behavior belongs in tua.toml; editor-specific behavior belongs in VS Code settings. Nullable language settings override the project only when they contain a value.

Setting Purpose
tua.server.path Select the tua executable used by the extension
tua.love.command Select the LOVE executable used by run tasks
tua.trace.server Show off, messages, or verbose protocol traces
tua.log.verbosity Show off, error, info, debug, or trace server logs
tua.format.* Override formatter settings from tua.toml
tua.diagnostics.* Override individual diagnostic, inline-ignore, and accepted-global settings
tua.jit.enabled Enable conservative LuaJIT shape guidance; disabled by default
tua.ecs.enabled Enable or disable built-in ECS analysis; enabled by default
tua.completion.sequenceRanking.* Configure experimental contextual completion ranking
tua.completion.multiToken.* Configure experimental validated multi-token suggestions
tua.fileAssociations Index additional glob patterns as Tua or Lua source
tua.ignoredPaths Exclude matching paths from indexing and diagnostics
tua.requirePathSeparator Suggest module paths with . or / separators
tua.preview.enabled Enable experimental visual previews; disabled by default

Trace and log settings update without a restart when supported. Changing the server path restarts the client with the selected executable.

Other LSP Clients

Point an LSP client at tua lsp and use the workspace containing tua.toml as the root. A client should send incremental document synchronization, workspace folder changes, configuration changes, and watched-file events for .tua, indexed .lua, assets, and tua.toml when supported.

Standard Protocol Surface

Area Supported requests
Synchronization Open, incremental change, save with text, and close
Diagnostics Push diagnostics and pull diagnostics when the client advertises support
Writing Completion with resolve, hover, and signature help
Navigation Definition, type definition, implementation, references, document highlight, links, symbols, workspace symbols, and call hierarchy
Editing Prepare rename, rename, quick fixes, source actions, code lenses, selection ranges, and folding ranges
Presentation Inlay hints and full, range, and delta semantic tokens
Formatting Full-document, range, and on-type formatting
Workspace Workspace folders, watched files, and will-rename file edits

Tua Extension Requests

The bundled VS Code extension also uses version-matched Tua requests:

Method Result
tua/explainType Source-linked type inference explanation
tua/inspect Structured document inspector data, including sprite sheets and static state machines
tua/assetInventory Project assets, metadata, references, and hints
tua/signalGraph Typed SignalBus project graph
tua/moduleGraph Module dependency and export graph
tua/ecsGraph ECS components, systems, queries, and insights
tua/objectPoolGraph Object-pool declarations and lifecycle facts
tua/builtinDocument Generated Lua and LOVE builtin stub source
tua/cacheStats Analysis cache and workspace-warming counters
tua/health Server revision, queue, cache, stale-result, and latency health
tua/resetCompletionStats Clear local adaptive completion statistics

tua/setTrace updates runtime trace configuration. These requests are an extension integration surface rather than a general stable external API; keep custom clients matched to the server version. The constraints for a future versioned tool API are documented in External API Bridge.

Troubleshooting

  1. Open the folder containing tua.toml, then confirm the file's language mode is Tua.
  2. Check the Tua language status item for server state, path, version, and workspace.
  3. Run Tua: Focus Output for startup, build, protocol, and runtime messages.
  4. Run Tua: Explain Type / Why Is This Any? when completion disappears because a value became dynamic.
  5. Run Tua: Show Language Server Health when requests feel stale or slow.
  6. Use Tua: Restart Language Server after changing a development binary.
  7. Use Tua: Select Server or set tua.server.path if the bundled binary is unavailable or a local build is required.
  8. Use Tua: Copy Debug Info before filing an issue.

Set tua.trace.server to messages or verbose for protocol traces and tua.log.verbosity to debug or trace for server diagnostics. Avoid leaving verbose tracing enabled during normal use because the output is intentionally high volume.

If generated Lua cannot be opened, inspect Tua: Focus Output for build diagnostics. If sprite preview is unavailable, enable tua.preview.enabled; ordinary sprite diagnostics and completion do not depend on the preview setting.

Contributor Checks

Use focused checks for language-server changes:

cargo test -p tua-lsp
make lsp-smoke
make lsp-features-smoke
npm --prefix editors/vscode run test:vscode

The extension-host suite covers live completion freshness, cancellation, navigation, signature help, inlay hints, UTF-16 positions, file-renames, project explorers, asset actions, and sprite preview behavior.

For performance work, start with:

make profile-lsp
make profile-completion
make profile-representative

See Completion Benchmarks for benchmark selection, latency metrics, baselines, cache interpretation, and freshness checks. Set TUA_PROFILE=1 when internal analysis spans are needed; normal server output remains clean when profiling is disabled.