lsp
Embedded Luau language server — check / search / inspect
require("@builtin::modules.api.engine.lsp") -- lsp (also available as global 'lsp')
The lsp namespace: 78 functions.
globals/lsp/analysisFlags
lsp.analysisFlags() -> AnalysisFlags
The feature-flag configuration upstream Luau's analysis library
(the linter and .luaurc parser behind these checks) runs under,
read back from Luau's flag registry. It is upstream's own: every
Luau* flag the analysis sources declare is on unless upstream's
experimental list names it, and every other flag they declare keeps
the value Luau registers it at. The flags the analysis library reads
from the parser and the VM are shared: they run at the value the
engine's runtime gives them, and one that differs from upstream's
carries its deviation. flags lists every flag those sources
declare or read, sorted by name; atUpstream counts the linked ones
whose value is upstream's. available is false, with reason, on a
build that does not link the analysis library.
Returns AnalysisFlags — { available, reason?, declared, shared, linked, atUpstream, flags }.
globals/lsp/check
lsp.check(path: string, opts: CheckFileOpts?) -> DiagnosticsResult
Validate a single .luau file in the VFS and return its
diagnostics. A path the check could not read comes back as one
lsp-check-* error naming the path and the reason, and a file past the
2 MiB analysis bound as one source-too-large warning with
checked = false, so checked with errors == 0 means a code body was
analysed and is clean.
opts.upstream (true, or the options of lsp.upstreamCheck) also
checks the file with upstream Luau's type checker and sets upstream to
what lsp.upstreamCheck returns for it, whose modules say how the
check of each module it reached ended and how long it took; the
diagnostics and counts stay the engine checker's.
Parameters
pathstring— VFS path.optsCheckFileOpts(optional) —{ severity?, limit?, context?, upstream? }.
Returns DiagnosticsResult — Array of diagnostic tables.
local diags = lsp.check("/zero/source/main.luau")
globals/lsp/checkAll
lsp.checkAll(opts: CheckAllOpts?) -> CheckAllResult
Validate the user's Luau scripts and return an aggregate
summary plus diagnostic list. opts.scope = "user" (default)
skips library mounts; "all" includes them. The sweep is
time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if
the budget elapses it returns the partial result gathered so far
with budgetExceeded = true rather than blocking the engine.
opts.budgetMs sets the budget for the call and opts.maxFiles caps
the files it checks. opts.upstream (true, or the options of
lsp.upstreamCheck) also checks each file with upstream Luau's type
checker once the engine's own pass is done, within the same budget, and
sets upstream to what that pass found: every module it reached, once,
with how its check ended ("completed", "timedOut", "internal", or
"notAnalysable" with the reason) and how long it took, reused marking a
module checked before the pass reached it and unchanged since.
The pass starts no file once the budget is spent, so it ends within the
budget plus the check of the one file in progress.
Parameters
optsCheckAllOpts(optional) —{ scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.
Returns CheckAllResult — { filesChecked, errors, warnings, info, hints, budgetExceeded, diagnostics, upstream? }.
globals/lsp/checkCode
lsp.checkCode(source: string, opts: CheckOpts?) -> DiagnosticsResult
Validate inline Luau source without a backing file. Useful
for checking code before writing it to disk. A source past the 2 MiB
analysis bound comes back as one source-too-large warning with
checked = false.
Parameters
sourcestring— Luau source.optsCheckOpts(optional) —{ severity?, limit?, context? }.
Returns DiagnosticsResult — Array of diagnostic tables.
globals/lsp/checkDirty
lsp.checkDirty() -> DiagnosticsResult
Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.
Returns DiagnosticsResult — Array of diagnostic tables.
globals/lsp/definitions
lsp.definitions(opts: DefinitionsOpts?) -> Definitions
The engine's API as a Luau definitions source, upstream Luau's
.d.luau form (declare statements and export type aliases),
generated from the live API registry: the documented members, the
--!global-types aliases and the asset-category shapes lsp.check
types against, bound under the same names. It declares each global the
calling script's globals hold, so a user script, a trusted script and a
worker each get the globals they have; a name upstream's builtin
environment declares keeps upstream's type and is left out.
lsp.upstreamCheck loads the same source into every module it checks,
and an external editor running luau-lsp reads it as a definitions file
(lsp.exportDefinitions writes it out). It is generated again whenever
the registry changes, a library hot reload included. package (@zero)
keys each global's documentation symbol (@zero/global/<name>,
@zero/global/<name>.<member>). Each type string the registry holds is
declared as written, or listed in unrepresented with the reason and
declared any; a declaration upstream refuses to load (an alias that
reaches itself through aliases alone) is listed with upstream's reason,
so the rest of the source loads. opts.documentation = true also returns
documentation, the JSON documentation file luau-lsp reads beside the
definitions: each symbol's signature and description. available is
false, with reason, on a build that does not link the analysis library.
Parameters
optsDefinitionsOpts(optional) —{ documentation? }.
Returns Definitions — { available, reason?, package, source, bytes, globals, types, unrepresented, documentation? }.
local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)
globals/lsp/describe
lsp.describe(path: string, opts: DescribeOpts?) -> DocEntry?
Inspect a single documented entry. Returns the full doc
table (signature, args, returns, examples, level), or nil.
The path is resolved independently of which root the doc is
registered under and of separator style, so the spelling that reads
off the API surface (renderer.texture.create) finds the entry
registered as globals/renderer/texture/create. A path naming a
binding the engine registered internally answers with the entry a Luau
module publishes over it where there is one, so the signature is the
call content makes; opts.includeInternal answers with the internally
registered entry itself. When a path does not resolve,
lsp.describePaths says what the registry holds near it.
Parameters
pathstring— Doc path (e.g."asset/resolve","renderer.texture.create").optsDescribeOpts(optional) — Optional{ includeInternal? }— default prefers the published entry.
Returns DocEntry? — Full doc table or nil.
local doc = lsp.describe("renderer.texture.create")
globals/lsp/describePaths
lsp.describePaths(path: string) -> { string }
List the registered doc paths related to path. A path that names
an entry returns every root it is registered under (the first is what
lsp.describe resolves to); a path that names a namespace returns the
entries registered under it. Empty when the registry holds nothing
near the path — so a lookup that returns nil can always be turned into
the list of what does exist.
Parameters
pathstring— Doc path in any spelling ("renderer.texture","ecs/query").
Returns { string } — Array of registered doc paths, most canonical first.
for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end
globals/lsp/describeTool
lsp.describeTool(path: string) -> string?
Return the full documentation text for a code-mode tool.
Parameters
pathstring— Tool path (e.g."scene/spawnLight").
Returns string? — Full tool docs or nil.
globals/lsp/docsByKind
lsp.docsByKind(kind: string) -> { MethodSummary }
List every doc whose registration kind matches kind.
Valid: "binding", "runtime_tool", "module", "component",
"library", "lua_export".
Parameters
kindstring— Registration kind.
Returns { MethodSummary } — Array of doc summary tables.
globals/lsp/exportDefinitions
lsp.exportDefinitions(dir: string) -> ExportDefinitionsResult
Write the engine's API out for an external editor (VS Code, Neovim, or
any editor running luau-lsp), so it types the engine's
globals: <dir>/zero.d.luau
(the definitions lsp.definitions returns), <dir>/zero.docs.json (the
documentation file naming each global's and member's signature and
description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the
first two (its types.definitionFiles and types.documentationFiles
settings) and it types the engine's globals: entity.spawn("x") checks
against its signature and entity.spwan("x") is an unknown member. The
files hold the globals the calling script's globals hold; call it from
the tier whose globals the editor should know. The files land in the
VFS: userfile.save(path) (the Files panel's "Save to this device")
puts each one on the machine of the person at the screen, and over HTTP
the engine's GET /engine/vfs/<path> reads it. The engine's own checker
reads a *.d.luau file the way upstream does, by loading it, so the
files can stay in a world's content. Writing again replaces them.
globals, types and unrepresented are the lists lsp.definitions
returns; the guide topics/external-editor walks through the editor
setup.
Parameters
dirstring— VFS directory to write into, such as/zero/source/editor.
Returns ExportDefinitionsResult — { available, reason?, files, bytes, globals, types, unrepresented }.
local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)
globals/lsp/getStrictMode
lsp.getStrictMode() -> StrictMode
Return the current strict mode.
Returns StrictMode — "off" | "soft" | "strict".
globals/lsp/isStrict
lsp.isStrict() -> boolean
Is the pre-execute LSP gate fully strict? False when off or in soft mode.
Returns boolean — True when fully strict.
globals/lsp/lastCheckGen
lsp.lastCheckGen() -> number
Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.
Returns number — Generation number.
globals/lsp/methods
lsp.methods(namespace: string, opts: MethodsOpts?) -> { MethodSummary } | { string }
List every documented method / entry under a namespace. A broad
namespace (ui, renderer) returns a large dump by default, so two
options narrow it: opts.filter keeps only methods whose name (or
doc path) contains the substring, case-insensitively; opts.namesOnly
returns a plain list of method-name strings instead of the full
per-method summary tables, which is much smaller and leaves nothing to
unwrap. A name, in either form (a summary's name field or a
namesOnly string), is the member's path below the namespace,
dot-joined (texture.create under renderer, proxy.component.get
under entity), so each one names a single member and
lsp.describe(namespace .. "." .. name) answers for it. The listing
answers with the surface content calls: an entry registered internally
is left out where its signature spells the __ binding or a Luau
module publishes the same member, and opts.includeInternal lists
every registered entry instead.
Parameters
namespacestring— Namespace name (e.g."entity","modules/Transform").optsMethodsOpts(optional) — Optional{ filter?, namesOnly?, includeInternal? }.
Returns { MethodSummary } | { string } — Array of method summary tables, or plain name strings when namesOnly is set (empty when the namespace is unknown or nothing matches the filter).
for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })
globals/lsp/modules
lsp.modules() -> { ModuleEntry }
List every Luau library module the engine currently knows
about — discovered via --!module headers, library scans, and
manually-recorded docs.
Returns { ModuleEntry } — Array of module summary tables.
globals/lsp/namespaces
lsp.namespaces(opts: NamespacesOpts?) -> { NamespaceEntry }
List the documentation namespaces reachable from Luau. By
default only namespaces exposing at least one PUBLIC method are
returned, so the list matches what you can actually call — internal
FFI plumbing (e.g. pause, native_entity), whose public surface
lives elsewhere (engine.paused, the entity proxy, …), is left
out. Pass { includeInternal = true } to list every namespace,
internal ones included.
Parameters
optsNamespacesOpts(optional) — Optional{ includeInternal? }— default lists public only.
Returns { NamespaceEntry } — Array of namespace summary tables.
for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end
globals/lsp/readDirectives
lsp.readDirectives(source: string) -> DirectiveBlock
Parse the leading --! directive block of a Luau source
string. Used by UIs that audit which files have skip directives
and what they suppress.
Parameters
sourcestring— Luau source text.
Returns DirectiveBlock — { mode, codes? }.
globals/lsp/search
lsp.search(query: string, opts: SearchOpts?) -> { MethodSummary }
Case-insensitive substring search across every registered
doc's path, signature, and description. Hits answer with the surface
content calls: an entry registered internally is left out where its
signature spells the __ binding or a Luau module publishes the same
member, and opts.includeInternal searches every registered entry.
Parameters
querystring— Substring to search for.optsSearchOpts(optional) —{ limit? = 50, includeInternal? }.
Returns { MethodSummary } — Array of method summary tables.
globals/lsp/setStrict
lsp.setStrict(enabled: boolean) -> boolean
Toggle the pre-execute LSP gate. Returns true when the change
was persisted to .world_settings, false when the play-mode write
lock blocked the write.
Parameters
enabledboolean— True = strict, false = off.
Returns boolean — Persistence signal.
globals/lsp/setStrictMode
lsp.setStrictMode(mode: StrictMode) -> boolean
Set the pre-execute strict gate's mode. Returns true when the
change was persisted to .world_settings, false when the
play-mode write lock blocked the write.
Parameters
modeStrictMode—"off"|"soft"|"strict".
Returns boolean — Persistence signal.
globals/lsp/summary
lsp.summary() -> Summary
Counts only — does not re-run validation.
Returns Summary — Counts of cached diagnostics by severity.
globals/lsp/symbolAt
lsp.symbolAt(path: string, line: number, col: number, name: string, chain: string?) -> SymbolHit?
The identifier name where it stands in path at line:col
(1-based, the file's own columns), with the static type it resolves to
THERE — a local inside a function body, a parameter, a field, a global —
and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps
to. nil when nothing at that position is a reference to name: a
word in a comment or a string, a keyword.
Parameters
pathstring— VFS path of the file.linenumber— 1-based line.colnumber— 1-based column in the file's own text.namestring— The identifier at that position.chainstring(optional) — The access path the identifier ends —entity.component.addfor a pointer onadd— which is where its API page is filed.
Returns SymbolHit? — The type descriptor of the reference (kind, display, and for a function its params and returns), with name, symbolKind (local, parameter, function, field, global, namespace, module), declaredAt when the file introduced the name, members and memberCount for a table, modulePath and moduleFile for a required module, and doc — the comment block above the declaration (--!desc, --!arg, --!return, --!example), the required module's header, or the API page under chain. nil when nothing there is a reference to name.
local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")
globals/lsp/tools
lsp.tools() -> { ToolEntry }
List every code-mode tool registered in the VFS under
/zero/docs/tools/<category>/<tool>.
Returns { ToolEntry } — Array of tool summary tables.
globals/lsp/typeOf
lsp.typeOf(expr_source: string, context_path: string?) -> TypeDescriptor
Infer the static type of a Luau expression. When
context_path is given, the file is loaded and walked so the
inference env contains every local + alias in scope at its end.
An expression past the 2 MiB analysis bound answers kind unknown,
and a context file past it adds nothing to the env.
Parameters
expr_sourcestring— Luau expression source (no surrounding chunk).context_pathstring(optional) — VFS path whose scope should be visible.
Returns TypeDescriptor — Type descriptor table.
local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")
globals/lsp/upstreamCheck
lsp.upstreamCheck(path: string, opts: UpstreamCheckOpts?) -> UpstreamCheck
Type-check a .luau file with upstream Luau's own type checker (its
Frontend), and every module the file requires, and return what upstream
reports as data. What lsp.check, the execute gate and the play gate
report is unchanged by it. Each require literal resolves to the source
file lsp.check's require-unresolved pass holds it to, which is the
file the runtime loads wherever the runtime loads one, in whichever form
it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b,
.x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder
asset), and
requires lists each literal of the file with the file it reached
(module) and the file the runtime's require loads for it from this
file (runtimeModule), nil where either reaches none.
findings carry upstream's error kind or lint rule, the module they are
in and 1-based positions in that module as written; they cover the file,
and with opts.includeRequired the type errors of every module it
reaches come back as well. Upstream reads every module through its
analysis view: the engine's typed declarations read as the plain Luau
functions they declare, their generic, parameter and return annotations
kept, a return type written after -> read after :, and every line
and column as in the source; view lists where the view of the file
differs from it as { line, col, written, reads }. A module whose
findings come back that upstream cannot parse through its view or past
the 2 MiB analysis bound is listed in notAnalysable with the reason,
and when that is the file itself analysable is false. The checked
modules are kept between calls: checkedModules names the ones this call
type-checked, empty when nothing the file reaches changed, and a write to
a source re-checks that module and the modules depending on it. The
phases of a module's check whose work grows with the types they meet
stop at opts.timeLimitMs, and modules
says of each module the call reached how its check ended ("completed",
"timedOut" or "internal") and how long it took, reused marking one an
earlier call checked that is unchanged since. The modules
held between calls are bounded: once a call returns their sources sum to
at most held.limitBytes, the modules the least recent calls reached
going first, and held and evicted report it. A failure
inside the analysis library comes back as an "internal" finding with
completed false, and the internal errors upstream reports for a
module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every
module it reaches and resolves their requires without checking types. available is false, with reason, on a build that
does not link the analysis library.
Parameters
pathstring— VFS path of a .luau file, a module identity, or a@scope::path.optsUpstreamCheckOpts(optional) —{ solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.
Returns UpstreamCheck — { available, path, analysable, reason?, completed, solver, mode, elapsedMs, findings, notAnalysable, requires, view, checkedModules, timedOut, modules, held, evicted, errors, warnings }.
local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })
modules/lsp/analysisFlags
analysisFlags(): AnalysisFlags
The feature-flag configuration upstream Luau's analysis library
(the linter and .luaurc parser behind these checks) runs under,
read back from Luau's flag registry. It is upstream's own: every
Luau* flag the analysis sources declare is on unless upstream's
experimental list names it, and every other flag they declare keeps
the value Luau registers it at. The flags the analysis library reads
from the parser and the VM are shared: they run at the value the
engine's runtime gives them, and one that differs from upstream's
carries its deviation. flags lists every flag those sources
declare or read, sorted by name; atUpstream counts the linked ones
whose value is upstream's. available is false, with reason, on a
build that does not link the analysis library.
modules/lsp/check
check(path: string, opts: CheckFileOpts?): DiagnosticsResult
Validate a single .luau file in the VFS and return its
diagnostics. A path the check could not read comes back as one
lsp-check-* error naming the path and the reason, and a file past the
2 MiB analysis bound as one source-too-large warning with
checked = false, so checked with errors == 0 means a code body was
analysed and is clean.
opts.upstream (true, or the options of lsp.upstreamCheck) also
checks the file with upstream Luau's type checker and sets upstream to
what lsp.upstreamCheck returns for it, whose modules say how the
check of each module it reached ended and how long it took; the
diagnostics and counts stay the engine checker's.
Parameters
pathstring— VFS path.optsCheckFileOpts?(optional) —{ severity?, limit?, context?, upstream? }.
local diags = lsp.check("/zero/source/main.luau")
modules/lsp/checkAll
checkAll(opts: CheckAllOpts?): CheckAllResult
Validate the user's Luau scripts and return an aggregate
summary plus diagnostic list. opts.scope = "user" (default)
skips library mounts; "all" includes them. The sweep is
time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if
the budget elapses it returns the partial result gathered so far
with budgetExceeded = true rather than blocking the engine.
opts.budgetMs sets the budget for the call and opts.maxFiles caps
the files it checks. opts.upstream (true, or the options of
lsp.upstreamCheck) also checks each file with upstream Luau's type
checker once the engine's own pass is done, within the same budget, and
sets upstream to what that pass found: every module it reached, once,
with how its check ended ("completed", "timedOut", "internal", or
"notAnalysable" with the reason) and how long it took, reused marking a
module checked before the pass reached it and unchanged since.
The pass starts no file once the budget is spent, so it ends within the
budget plus the check of the one file in progress.
Parameters
optsCheckAllOpts?(optional) —{ scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.
modules/lsp/checkCode
checkCode(source: string, opts: CheckOpts?): DiagnosticsResult
Validate inline Luau source without a backing file. Useful
for checking code before writing it to disk. A source past the 2 MiB
analysis bound comes back as one source-too-large warning with
checked = false.
Parameters
sourcestring— Luau source.optsCheckOpts?(optional) —{ severity?, limit?, context? }.
modules/lsp/checkDirty
checkDirty(): DiagnosticsResult
Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.
modules/lsp/definitions
definitions(opts: DefinitionsOpts?): Definitions
The engine's API as a Luau definitions source, upstream Luau's
.d.luau form (declare statements and export type aliases),
generated from the live API registry: the documented members, the
--!global-types aliases and the asset-category shapes lsp.check
types against, bound under the same names. It declares each global the
calling script's globals hold, so a user script, a trusted script and a
worker each get the globals they have; a name upstream's builtin
environment declares keeps upstream's type and is left out.
lsp.upstreamCheck loads the same source into every module it checks,
and an external editor running luau-lsp reads it as a definitions file
(lsp.exportDefinitions writes it out). It is generated again whenever
the registry changes, a library hot reload included. package (@zero)
keys each global's documentation symbol (@zero/global/<name>,
@zero/global/<name>.<member>). Each type string the registry holds is
declared as written, or listed in unrepresented with the reason and
declared any; a declaration upstream refuses to load (an alias that
reaches itself through aliases alone) is listed with upstream's reason,
so the rest of the source loads. opts.documentation = true also returns
documentation, the JSON documentation file luau-lsp reads beside the
definitions: each symbol's signature and description. available is
false, with reason, on a build that does not link the analysis library.
Parameters
optsDefinitionsOpts?(optional) —{ documentation? }.
local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)
modules/lsp/describe
describe(path: string, opts: DescribeOpts?): DocEntry?
Inspect a single documented entry. Returns the full doc
table (signature, args, returns, examples, level), or nil.
The path is resolved independently of which root the doc is
registered under and of separator style, so the spelling that reads
off the API surface (renderer.texture.create) finds the entry
registered as globals/renderer/texture/create. A path naming a
binding the engine registered internally answers with the entry a Luau
module publishes over it where there is one, so the signature is the
call content makes; opts.includeInternal answers with the internally
registered entry itself. When a path does not resolve,
lsp.describePaths says what the registry holds near it.
Parameters
pathstring— Doc path (e.g."asset/resolve","renderer.texture.create").optsDescribeOpts?(optional) — Optional{ includeInternal? }— default prefers the published entry.
local doc = lsp.describe("renderer.texture.create")
modules/lsp/describePaths
describePaths(path: string): { string }
List the registered doc paths related to path. A path that names
an entry returns every root it is registered under (the first is what
lsp.describe resolves to); a path that names a namespace returns the
entries registered under it. Empty when the registry holds nothing
near the path — so a lookup that returns nil can always be turned into
the list of what does exist.
Parameters
pathstring— Doc path in any spelling ("renderer.texture","ecs/query").
for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end
modules/lsp/describeTool
describeTool(path: string): string?
Return the full documentation text for a code-mode tool.
Parameters
pathstring— Tool path (e.g."scene/spawnLight").
modules/lsp/docsByKind
docsByKind(kind: string): { MethodSummary }
List every doc whose registration kind matches kind.
Valid: "binding", "runtime_tool", "module", "component",
"library", "lua_export".
Parameters
kindstring— Registration kind.
modules/lsp/exportDefinitions
exportDefinitions(dir: string): ExportDefinitionsResult
Write the engine's API out for an external editor (VS Code, Neovim, or
any editor running luau-lsp), so it types the engine's
globals: <dir>/zero.d.luau
(the definitions lsp.definitions returns), <dir>/zero.docs.json (the
documentation file naming each global's and member's signature and
description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the
first two (its types.definitionFiles and types.documentationFiles
settings) and it types the engine's globals: entity.spawn("x") checks
against its signature and entity.spwan("x") is an unknown member. The
files hold the globals the calling script's globals hold; call it from
the tier whose globals the editor should know. The files land in the
VFS: userfile.save(path) (the Files panel's "Save to this device")
puts each one on the machine of the person at the screen, and over HTTP
the engine's GET /engine/vfs/<path> reads it. The engine's own checker
reads a *.d.luau file the way upstream does, by loading it, so the
files can stay in a world's content. Writing again replaces them.
globals, types and unrepresented are the lists lsp.definitions
returns; the guide topics/external-editor walks through the editor
setup.
Parameters
dirstring— VFS directory to write into, such as/zero/source/editor.
local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)
modules/lsp/getStrictMode
getStrictMode(): StrictMode
Return the current strict mode.
modules/lsp/isStrict
isStrict(): boolean
Is the pre-execute LSP gate fully strict? False when off or in soft mode.
modules/lsp/lastCheckGen
lastCheckGen(): number
Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.
modules/lsp/methods
methods(namespace: string, opts: MethodsOpts?): { MethodSummary } | { string }
List every documented method / entry under a namespace. A broad
namespace (ui, renderer) returns a large dump by default, so two
options narrow it: opts.filter keeps only methods whose name (or
doc path) contains the substring, case-insensitively; opts.namesOnly
returns a plain list of method-name strings instead of the full
per-method summary tables, which is much smaller and leaves nothing to
unwrap. A name, in either form (a summary's name field or a
namesOnly string), is the member's path below the namespace,
dot-joined (texture.create under renderer, proxy.component.get
under entity), so each one names a single member and
lsp.describe(namespace .. "." .. name) answers for it. The listing
answers with the surface content calls: an entry registered internally
is left out where its signature spells the __ binding or a Luau
module publishes the same member, and opts.includeInternal lists
every registered entry instead.
Parameters
namespacestring— Namespace name (e.g."entity","modules/Transform").optsMethodsOpts?(optional) — Optional{ filter?, namesOnly?, includeInternal? }.
for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })
modules/lsp/modules
modules(): { ModuleEntry }
List every Luau library module the engine currently knows
about — discovered via --!module headers, library scans, and
manually-recorded docs.
modules/lsp/namespaces
namespaces(opts: NamespacesOpts?): { NamespaceEntry }
List the documentation namespaces reachable from Luau. By
default only namespaces exposing at least one PUBLIC method are
returned, so the list matches what you can actually call — internal
FFI plumbing (e.g. pause, native_entity), whose public surface
lives elsewhere (engine.paused, the entity proxy, …), is left
out. Pass { includeInternal = true } to list every namespace,
internal ones included.
Parameters
optsNamespacesOpts?(optional) — Optional{ includeInternal? }— default lists public only.
for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end
modules/lsp/readDirectives
readDirectives(source: string): DirectiveBlock
Parse the leading --! directive block of a Luau source
string. Used by UIs that audit which files have skip directives
and what they suppress.
Parameters
sourcestring— Luau source text.
modules/lsp/search
search(query: string, opts: SearchOpts?): { MethodSummary }
Case-insensitive substring search across every registered
doc's path, signature, and description. Hits answer with the surface
content calls: an entry registered internally is left out where its
signature spells the __ binding or a Luau module publishes the same
member, and opts.includeInternal searches every registered entry.
Parameters
querystring— Substring to search for.optsSearchOpts?(optional) —{ limit? = 50, includeInternal? }.
modules/lsp/setStrict
setStrict(enabled: boolean): boolean
Toggle the pre-execute LSP gate. Returns true when the change
was persisted to .world_settings, false when the play-mode write
lock blocked the write.
Parameters
enabledboolean— True = strict, false = off.
modules/lsp/setStrictMode
setStrictMode(mode: StrictMode): boolean
Set the pre-execute strict gate's mode. Returns true when the
change was persisted to .world_settings, false when the
play-mode write lock blocked the write.
Parameters
modeStrictMode—"off"|"soft"|"strict".
modules/lsp/summary
summary(): Summary
Counts only — does not re-run validation.
modules/lsp/symbolAt
symbolAt(path: string, line: number, col: number, name: string, chain: string?): SymbolHit?
The identifier name where it stands in path at line:col
(1-based, the file's own columns), with the static type it resolves to
THERE — a local inside a function body, a parameter, a field, a global —
and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps
to. nil when nothing at that position is a reference to name: a
word in a comment or a string, a keyword.
Parameters
pathstring— VFS path of the file.linenumber— 1-based line.colnumber— 1-based column in the file's own text.namestring— The identifier at that position.chainstring?(optional) — The access path the identifier ends —entity.component.addfor a pointer onadd— which is where its API page is filed.
local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")
modules/lsp/tools
tools(): { ToolEntry }
List every code-mode tool registered in the VFS under
/zero/docs/tools/<category>/<tool>.
modules/lsp/typeOf
typeOf(expr_source: string, context_path: string?): TypeDescriptor
Infer the static type of a Luau expression. When
context_path is given, the file is loaded and walked so the
inference env contains every local + alias in scope at its end.
An expression past the 2 MiB analysis bound answers kind unknown,
and a context file past it adds nothing to the env.
Parameters
expr_sourcestring— Luau expression source (no surrounding chunk).context_pathstring?(optional) — VFS path whose scope should be visible.
local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")
modules/lsp/upstreamCheck
upstreamCheck(path: string, opts: UpstreamCheckOpts?): UpstreamCheck
Type-check a .luau file with upstream Luau's own type checker (its
Frontend), and every module the file requires, and return what upstream
reports as data. What lsp.check, the execute gate and the play gate
report is unchanged by it. Each require literal resolves to the source
file lsp.check's require-unresolved pass holds it to, which is the
file the runtime loads wherever the runtime loads one, in whichever form
it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b,
.x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder
asset), and
requires lists each literal of the file with the file it reached
(module) and the file the runtime's require loads for it from this
file (runtimeModule), nil where either reaches none.
findings carry upstream's error kind or lint rule, the module they are
in and 1-based positions in that module as written; they cover the file,
and with opts.includeRequired the type errors of every module it
reaches come back as well. Upstream reads every module through its
analysis view: the engine's typed declarations read as the plain Luau
functions they declare, their generic, parameter and return annotations
kept, a return type written after -> read after :, and every line
and column as in the source; view lists where the view of the file
differs from it as { line, col, written, reads }. A module whose
findings come back that upstream cannot parse through its view or past
the 2 MiB analysis bound is listed in notAnalysable with the reason,
and when that is the file itself analysable is false. The checked
modules are kept between calls: checkedModules names the ones this call
type-checked, empty when nothing the file reaches changed, and a write to
a source re-checks that module and the modules depending on it. The
phases of a module's check whose work grows with the types they meet
stop at opts.timeLimitMs, and modules
says of each module the call reached how its check ended ("completed",
"timedOut" or "internal") and how long it took, reused marking one an
earlier call checked that is unchanged since. The modules
held between calls are bounded: once a call returns their sources sum to
at most held.limitBytes, the modules the least recent calls reached
going first, and held and evicted report it. A failure
inside the analysis library comes back as an "internal" finding with
completed false, and the internal errors upstream reports for a
module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every
module it reaches and resolves their requires without checking types. available is false, with reason, on a build that
does not link the analysis library.
Parameters
pathstring— VFS path of a .luau file, a module identity, or a@scope::path.optsUpstreamCheckOpts?(optional) —{ solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.
local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })
typed/builtin//modules/api/engine/lsp/lsp/analysisFlags
lsp.analysisFlags() -> AnalysisFlags
The feature-flag configuration upstream Luau's analysis library
(the linter and .luaurc parser behind these checks) runs under,
read back from Luau's flag registry. It is upstream's own: every
Luau* flag the analysis sources declare is on unless upstream's
experimental list names it, and every other flag they declare keeps
the value Luau registers it at. The flags the analysis library reads
from the parser and the VM are shared: they run at the value the
engine's runtime gives them, and one that differs from upstream's
carries its deviation. flags lists every flag those sources
declare or read, sorted by name; atUpstream counts the linked ones
whose value is upstream's. available is false, with reason, on a
build that does not link the analysis library.
Returns AnalysisFlags — { available, reason?, declared, shared, linked, atUpstream, flags }.
typed/builtin//modules/api/engine/lsp/lsp/check
lsp.check(path: string, opts: CheckFileOpts?) -> DiagnosticsResult
Validate a single .luau file in the VFS and return its
diagnostics. A path the check could not read comes back as one
lsp-check-* error naming the path and the reason, and a file past the
2 MiB analysis bound as one source-too-large warning with
checked = false, so checked with errors == 0 means a code body was
analysed and is clean.
opts.upstream (true, or the options of lsp.upstreamCheck) also
checks the file with upstream Luau's type checker and sets upstream to
what lsp.upstreamCheck returns for it, whose modules say how the
check of each module it reached ended and how long it took; the
diagnostics and counts stay the engine checker's.
Parameters
pathstring— VFS path.optsCheckFileOpts(optional) —{ severity?, limit?, context?, upstream? }.
Returns DiagnosticsResult — Array of diagnostic tables.
local diags = lsp.check("/zero/source/main.luau")
typed/builtin//modules/api/engine/lsp/lsp/checkAll
lsp.checkAll(opts: CheckAllOpts?) -> CheckAllResult
Validate the user's Luau scripts and return an aggregate
summary plus diagnostic list. opts.scope = "user" (default)
skips library mounts; "all" includes them. The sweep is
time-budgeted (ZERO_EXECUTE_INLINE_BUDGET_MS, ~20s by default): if
the budget elapses it returns the partial result gathered so far
with budgetExceeded = true rather than blocking the engine.
opts.budgetMs sets the budget for the call and opts.maxFiles caps
the files it checks. opts.upstream (true, or the options of
lsp.upstreamCheck) also checks each file with upstream Luau's type
checker once the engine's own pass is done, within the same budget, and
sets upstream to what that pass found: every module it reached, once,
with how its check ended ("completed", "timedOut", "internal", or
"notAnalysable" with the reason) and how long it took, reused marking a
module checked before the pass reached it and unchanged since.
The pass starts no file once the budget is spent, so it ends within the
budget plus the check of the one file in progress.
Parameters
optsCheckAllOpts(optional) —{ scope?, severity?, limit?, budgetMs?, maxFiles?, upstream? }.
Returns CheckAllResult — { filesChecked, errors, warnings, info, hints, budgetExceeded, diagnostics, upstream? }.
typed/builtin//modules/api/engine/lsp/lsp/checkCode
lsp.checkCode(source: string, opts: CheckOpts?) -> DiagnosticsResult
Validate inline Luau source without a backing file. Useful
for checking code before writing it to disk. A source past the 2 MiB
analysis bound comes back as one source-too-large warning with
checked = false.
Parameters
sourcestring— Luau source.optsCheckOpts(optional) —{ severity?, limit?, context? }.
Returns DiagnosticsResult — Array of diagnostic tables.
typed/builtin//modules/api/engine/lsp/lsp/checkDirty
lsp.checkDirty() -> DiagnosticsResult
Drain the dirty-file set populated by the hot-reload hook, validate each, and return the combined diagnostic list.
Returns DiagnosticsResult — Array of diagnostic tables.
typed/builtin//modules/api/engine/lsp/lsp/definitions
lsp.definitions(opts: DefinitionsOpts?) -> Definitions
The engine's API as a Luau definitions source, upstream Luau's
.d.luau form (declare statements and export type aliases),
generated from the live API registry: the documented members, the
--!global-types aliases and the asset-category shapes lsp.check
types against, bound under the same names. It declares each global the
calling script's globals hold, so a user script, a trusted script and a
worker each get the globals they have; a name upstream's builtin
environment declares keeps upstream's type and is left out.
lsp.upstreamCheck loads the same source into every module it checks,
and an external editor running luau-lsp reads it as a definitions file
(lsp.exportDefinitions writes it out). It is generated again whenever
the registry changes, a library hot reload included. package (@zero)
keys each global's documentation symbol (@zero/global/<name>,
@zero/global/<name>.<member>). Each type string the registry holds is
declared as written, or listed in unrepresented with the reason and
declared any; a declaration upstream refuses to load (an alias that
reaches itself through aliases alone) is listed with upstream's reason,
so the rest of the source loads. opts.documentation = true also returns
documentation, the JSON documentation file luau-lsp reads beside the
definitions: each symbol's signature and description. available is
false, with reason, on a build that does not link the analysis library.
Parameters
optsDefinitionsOpts(optional) —{ documentation? }.
Returns Definitions — { available, reason?, package, source, bytes, globals, types, unrepresented, documentation? }.
local d = lsp.definitions()
print(d.bytes, #d.globals, #d.unrepresented)
typed/builtin//modules/api/engine/lsp/lsp/describe
lsp.describe(path: string, opts: DescribeOpts?) -> DocEntry?
Inspect a single documented entry. Returns the full doc
table (signature, args, returns, examples, level), or nil.
The path is resolved independently of which root the doc is
registered under and of separator style, so the spelling that reads
off the API surface (renderer.texture.create) finds the entry
registered as globals/renderer/texture/create. A path naming a
binding the engine registered internally answers with the entry a Luau
module publishes over it where there is one, so the signature is the
call content makes; opts.includeInternal answers with the internally
registered entry itself. When a path does not resolve,
lsp.describePaths says what the registry holds near it.
Parameters
pathstring— Doc path (e.g."asset/resolve","renderer.texture.create").optsDescribeOpts(optional) — Optional{ includeInternal? }— default prefers the published entry.
Returns DocEntry? — Full doc table or nil.
local doc = lsp.describe("renderer.texture.create")
typed/builtin//modules/api/engine/lsp/lsp/describePaths
lsp.describePaths(path: string) -> { string }
List the registered doc paths related to path. A path that names
an entry returns every root it is registered under (the first is what
lsp.describe resolves to); a path that names a namespace returns the
entries registered under it. Empty when the registry holds nothing
near the path — so a lookup that returns nil can always be turned into
the list of what does exist.
Parameters
pathstring— Doc path in any spelling ("renderer.texture","ecs/query").
Returns { string } — Array of registered doc paths, most canonical first.
for _, p in ipairs(lsp.describePaths("renderer.texture")) do print(p) end
typed/builtin//modules/api/engine/lsp/lsp/describeTool
lsp.describeTool(path: string) -> string?
Return the full documentation text for a code-mode tool.
Parameters
pathstring— Tool path (e.g."scene/spawnLight").
Returns string? — Full tool docs or nil.
typed/builtin//modules/api/engine/lsp/lsp/docsByKind
lsp.docsByKind(kind: string) -> { MethodSummary }
List every doc whose registration kind matches kind.
Valid: "binding", "runtime_tool", "module", "component",
"library", "lua_export".
Parameters
kindstring— Registration kind.
Returns { MethodSummary } — Array of doc summary tables.
typed/builtin//modules/api/engine/lsp/lsp/exportDefinitions
lsp.exportDefinitions(dir: string) -> ExportDefinitionsResult
Write the engine's API out for an external editor (VS Code, Neovim, or
any editor running luau-lsp), so it types the engine's
globals: <dir>/zero.d.luau
(the definitions lsp.definitions returns), <dir>/zero.docs.json (the
documentation file naming each global's and member's signature and
description) and <dir>/.luaurc (nonstrict mode). Point luau-lsp at the
first two (its types.definitionFiles and types.documentationFiles
settings) and it types the engine's globals: entity.spawn("x") checks
against its signature and entity.spwan("x") is an unknown member. The
files hold the globals the calling script's globals hold; call it from
the tier whose globals the editor should know. The files land in the
VFS: userfile.save(path) (the Files panel's "Save to this device")
puts each one on the machine of the person at the screen, and over HTTP
the engine's GET /engine/vfs/<path> reads it. The engine's own checker
reads a *.d.luau file the way upstream does, by loading it, so the
files can stay in a world's content. Writing again replaces them.
globals, types and unrepresented are the lists lsp.definitions
returns; the guide topics/external-editor walks through the editor
setup.
Parameters
dirstring— VFS directory to write into, such as/zero/source/editor.
Returns ExportDefinitionsResult — { available, reason?, files, bytes, globals, types, unrepresented }.
local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals)
typed/builtin//modules/api/engine/lsp/lsp/getStrictMode
lsp.getStrictMode() -> StrictMode
Return the current strict mode.
Returns StrictMode — "off" | "soft" | "strict".
typed/builtin//modules/api/engine/lsp/lsp/isStrict
lsp.isStrict() -> boolean
Is the pre-execute LSP gate fully strict? False when off or in soft mode.
Returns boolean — True when fully strict.
typed/builtin//modules/api/engine/lsp/lsp/lastCheckGen
lsp.lastCheckGen() -> number
Generation counter — bumped each time the cache is rebuilt. UI polls this to know when to redraw.
Returns number — Generation number.
typed/builtin//modules/api/engine/lsp/lsp/methods
lsp.methods(namespace: string, opts: MethodsOpts?) -> { MethodSummary } | { string }
List every documented method / entry under a namespace. A broad
namespace (ui, renderer) returns a large dump by default, so two
options narrow it: opts.filter keeps only methods whose name (or
doc path) contains the substring, case-insensitively; opts.namesOnly
returns a plain list of method-name strings instead of the full
per-method summary tables, which is much smaller and leaves nothing to
unwrap. A name, in either form (a summary's name field or a
namesOnly string), is the member's path below the namespace,
dot-joined (texture.create under renderer, proxy.component.get
under entity), so each one names a single member and
lsp.describe(namespace .. "." .. name) answers for it. The listing
answers with the surface content calls: an entry registered internally
is left out where its signature spells the __ binding or a Luau
module publishes the same member, and opts.includeInternal lists
every registered entry instead.
Parameters
namespacestring— Namespace name (e.g."entity","modules/Transform").optsMethodsOpts(optional) — Optional{ filter?, namesOnly?, includeInternal? }.
Returns { MethodSummary } | { string } — Array of method summary tables, or plain name strings when namesOnly is set (empty when the namespace is unknown or nothing matches the filter).
for _, name in ipairs(lsp.methods("ui", { namesOnly = true })) do print(name) end
lsp.methods("renderer", { filter = "shadow" })
typed/builtin//modules/api/engine/lsp/lsp/modules
lsp.modules() -> { ModuleEntry }
List every Luau library module the engine currently knows
about — discovered via --!module headers, library scans, and
manually-recorded docs.
Returns { ModuleEntry } — Array of module summary tables.
typed/builtin//modules/api/engine/lsp/lsp/namespaces
lsp.namespaces(opts: NamespacesOpts?) -> { NamespaceEntry }
List the documentation namespaces reachable from Luau. By
default only namespaces exposing at least one PUBLIC method are
returned, so the list matches what you can actually call — internal
FFI plumbing (e.g. pause, native_entity), whose public surface
lives elsewhere (engine.paused, the entity proxy, …), is left
out. Pass { includeInternal = true } to list every namespace,
internal ones included.
Parameters
optsNamespacesOpts(optional) — Optional{ includeInternal? }— default lists public only.
Returns { NamespaceEntry } — Array of namespace summary tables.
for _, ns in ipairs(lsp.namespaces()) do print(ns.name) end
typed/builtin//modules/api/engine/lsp/lsp/readDirectives
lsp.readDirectives(source: string) -> DirectiveBlock
Parse the leading --! directive block of a Luau source
string. Used by UIs that audit which files have skip directives
and what they suppress.
Parameters
sourcestring— Luau source text.
Returns DirectiveBlock — { mode, codes? }.
typed/builtin//modules/api/engine/lsp/lsp/search
lsp.search(query: string, opts: SearchOpts?) -> { MethodSummary }
Case-insensitive substring search across every registered
doc's path, signature, and description. Hits answer with the surface
content calls: an entry registered internally is left out where its
signature spells the __ binding or a Luau module publishes the same
member, and opts.includeInternal searches every registered entry.
Parameters
querystring— Substring to search for.optsSearchOpts(optional) —{ limit? = 50, includeInternal? }.
Returns { MethodSummary } — Array of method summary tables.
typed/builtin//modules/api/engine/lsp/lsp/setStrict
lsp.setStrict(enabled: boolean) -> boolean
Toggle the pre-execute LSP gate. Returns true when the change
was persisted to .world_settings, false when the play-mode write
lock blocked the write.
Parameters
enabledboolean— True = strict, false = off.
Returns boolean — Persistence signal.
typed/builtin//modules/api/engine/lsp/lsp/setStrictMode
lsp.setStrictMode(mode: StrictMode) -> boolean
Set the pre-execute strict gate's mode. Returns true when the
change was persisted to .world_settings, false when the
play-mode write lock blocked the write.
Parameters
modeStrictMode—"off"|"soft"|"strict".
Returns boolean — Persistence signal.
typed/builtin//modules/api/engine/lsp/lsp/summary
lsp.summary() -> Summary
Counts only — does not re-run validation.
Returns Summary — Counts of cached diagnostics by severity.
typed/builtin//modules/api/engine/lsp/lsp/symbolAt
lsp.symbolAt(path: string, line: number, col: number, name: string, chain: string?) -> SymbolHit?
The identifier name where it stands in path at line:col
(1-based, the file's own columns), with the static type it resolves to
THERE — a local inside a function body, a parameter, a field, a global —
and, for a name a statement in the file introduced, declaredAt = {line, col}. The answer a hover shows and a go-to-definition jumps
to. nil when nothing at that position is a reference to name: a
word in a comment or a string, a keyword.
Parameters
pathstring— VFS path of the file.linenumber— 1-based line.colnumber— 1-based column in the file's own text.namestring— The identifier at that position.chainstring(optional) — The access path the identifier ends —entity.component.addfor a pointer onadd— which is where its API page is filed.
Returns SymbolHit? — The type descriptor of the reference (kind, display, and for a function its params and returns), with name, symbolKind (local, parameter, function, field, global, namespace, module), declaredAt when the file introduced the name, members and memberCount for a table, modulePath and moduleFile for a required module, and doc — the comment block above the declaration (--!desc, --!arg, --!return, --!example), the required module's header, or the API page under chain. nil when nothing there is a reference to name.
local hit = lsp.symbolAt("/zero/source/main.luau", 12, 9, "add", "entity.component.add")
typed/builtin//modules/api/engine/lsp/lsp/tools
lsp.tools() -> { ToolEntry }
List every code-mode tool registered in the VFS under
/zero/docs/tools/<category>/<tool>.
Returns { ToolEntry } — Array of tool summary tables.
typed/builtin//modules/api/engine/lsp/lsp/typeOf
lsp.typeOf(expr_source: string, context_path: string?) -> TypeDescriptor
Infer the static type of a Luau expression. When
context_path is given, the file is loaded and walked so the
inference env contains every local + alias in scope at its end.
An expression past the 2 MiB analysis bound answers kind unknown,
and a context file past it adds nothing to the env.
Parameters
expr_sourcestring— Luau expression source (no surrounding chunk).context_pathstring(optional) — VFS path whose scope should be visible.
Returns TypeDescriptor — Type descriptor table.
local td = lsp.typeOf("Transform.lookAt", "/zero/source/main.luau")
typed/builtin//modules/api/engine/lsp/lsp/upstreamCheck
lsp.upstreamCheck(path: string, opts: UpstreamCheckOpts?) -> UpstreamCheck
Type-check a .luau file with upstream Luau's own type checker (its
Frontend), and every module the file requires, and return what upstream
reports as data. What lsp.check, the execute gate and the play gate
report is unchanged by it. Each require literal resolves to the source
file lsp.check's require-unresolved pass holds it to, which is the
file the runtime loads wherever the runtime loads one, in whichever form
it is written (@builtin::a.b, @ns::, @root::, a bare dotted a.b,
.x, ..x, ./x, ../x, ~.x, ~pkg.x, an absolute path, a folder
asset), and
requires lists each literal of the file with the file it reached
(module) and the file the runtime's require loads for it from this
file (runtimeModule), nil where either reaches none.
findings carry upstream's error kind or lint rule, the module they are
in and 1-based positions in that module as written; they cover the file,
and with opts.includeRequired the type errors of every module it
reaches come back as well. Upstream reads every module through its
analysis view: the engine's typed declarations read as the plain Luau
functions they declare, their generic, parameter and return annotations
kept, a return type written after -> read after :, and every line
and column as in the source; view lists where the view of the file
differs from it as { line, col, written, reads }. A module whose
findings come back that upstream cannot parse through its view or past
the 2 MiB analysis bound is listed in notAnalysable with the reason,
and when that is the file itself analysable is false. The checked
modules are kept between calls: checkedModules names the ones this call
type-checked, empty when nothing the file reaches changed, and a write to
a source re-checks that module and the modules depending on it. The
phases of a module's check whose work grows with the types they meet
stop at opts.timeLimitMs, and modules
says of each module the call reached how its check ended ("completed",
"timedOut" or "internal") and how long it took, reused marking one an
earlier call checked that is unchanged since. The modules
held between calls are bounded: once a call returns their sources sum to
at most held.limitBytes, the modules the least recent calls reached
going first, and held and evicted report it. A failure
inside the analysis library comes back as an "internal" finding with
completed false, and the internal errors upstream reports for a
module come back as one "internal" finding whose count says how many. opts.typeCheck = false reads the file and every
module it reaches and resolves their requires without checking types. available is false, with reason, on a build that
does not link the analysis library.
Parameters
pathstring— VFS path of a .luau file, a module identity, or a@scope::path.optsUpstreamCheckOpts(optional) —{ solver?, mode?, timeLimitMs?, includeRequired?, typeCheck? }.
Returns UpstreamCheck — { available, path, analysable, reason?, completed, solver, mode, elapsedMs, findings, notAnalysable, requires, view, checkedModules, timedOut, modules, held, evicted, errors, warnings }.
local r = lsp.upstreamCheck("@builtin::modules.boids", { mode = "strict" })