A simple Lua debugger which requires no additional dependencies.
- Debug Lua using stand-alone interpretor or a custom executable
- Supports Lua versions 5.1, 5.2, 5.3 and LuaJIT
- Basic debugging features (stepping, inspecting, breakpoints, etc...)
- Conditional breakpoints
- Debug coroutines as separate threads
- Basic support for source maps, such as those generated by TypescriptToLua
To debug a Lua program using a stand-alone interpreter, set lua-local.interpreter
in your user or workspace settings:
Alternatively, you can set the interpreter and file to run in launch.json
:
{
"configurations": [
{
"type": "lua-local",
"request": "launch",
"name": "Debug",
"program": {
"lua": "lua5.1",
"file": "main.lua"
}
}
]
}
To debug using a custom Lua executable, you must set up your launch.json
with the name/path of the executable and any additional arguments that may be needed.
{
"configurations": [
{
"type": "lua-local",
"request": "launch",
"name": "Debug Custom Executable",
"program": {
"command": "executable"
},
"args": [
"${workspaceFolder}"
]
}
]
}
You must then manually start the debugger in your Lua code:
require("lldebugger").start()
Note that the path to lldebugger
will automatically be appended to the LUA_PATH
environment variable, so it can be found by Lua.
- The Lua environment must support communication via stdio.
- Some enviroments may require command line options to support this (ex. Corona requires
/no-console
flag) - Use of
io.read
or other calls that require user input will cause problems
- Some enviroments may require command line options to support this (ex. Corona requires
- The Lua environment must be built with the
debug
library, and no other code should attempt to set debug hooks. - You cannot manually pause debugging while the program is running.
- In Lua 5.1 and LuaJIT, the main thread cannot be accessed while stopped inside of a coroutine.
- You can detect that the debugger extension is attached by inspecting the environment variable
LOCAL_LUA_DEBUGGER_VSCODE
. This is useful for conditionally starting the debugger in custom environments.if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then require("lldebugger").start() end
- Some custom environments will not break on uncaught runtime errors. To catch a runtime error, you can wrap the code with
lldebugger.call()
:require("lldebugger").call(function() --code causing runtime error end)
scriptRoots
- A list of alternate paths to find lua scripts. This is useful for environments like LÖVE, which use custom resolvers to find scripts in other locations than what is in
package.config
.
- A list of alternate paths to find lua scripts. This is useful for environments like LÖVE, which use custom resolvers to find scripts in other locations than what is in
stopOnEntry
- Automatically break on first line after debug hook is set.
cwd
- Specify working directory to launch executable in. Default is the project directory.
args
- List of arguments to pass to Lua script or custom environment when launching.
env
- Specify environment variables to set when launching executable.
verbose
- Enable verbose output from debugger. Only useful when trying to identify problems with the debugger itself.
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Love",
"type": "lua-local",
"request": "launch",
"program": {
"command": "love"
},
"args": [
"game"
],
"scriptRoots": [
"game"
]
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
require("lldebugger").start()
end
function love.load()
...
Note that even when using busted via a lua interpreter, it must be set up as a custom environment to work correctly.
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Busted CLI",
"type": "lua-local",
"request": "launch",
"program": {
"command": "busted"
},
"args": [
"test/start-cli.lua"
]
},
{
"name": "Debug Busted via Lua Interpreter",
"type": "lua-local",
"request": "launch",
"program": {
"command": "lua"
},
"args": [
"test/start-interpreter.lua"
]
}
]
}
test/start-cli.lua
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
require("lldebugger").start()
end
describe("a test", function()
...
end)
test/start-interpreter.lua
--busted should be required before hooking debugger to avoid double-hooking
require("busted.runner")()
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
require("lldebugger").start()
end
describe("a test", function()
...
end)