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 --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 staticimport(...)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:
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.
Navigation And Symbols¶
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:
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:
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
objectPoolhelper.
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:
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¶
- Open the folder containing
tua.toml, then confirm the file's language mode is Tua. - Check the Tua language status item for server state, path, version, and workspace.
- Run Tua: Focus Output for startup, build, protocol, and runtime messages.
- Run Tua: Explain Type / Why Is This Any? when completion disappears because a value became dynamic.
- Run Tua: Show Language Server Health when requests feel stale or slow.
- Use Tua: Restart Language Server after changing a development binary.
- Use Tua: Select Server or set
tua.server.pathif the bundled binary is unavailable or a local build is required. - 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:
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.