A simple Lua debugger which requires no additional dependencies.
Beginning in version 0.3.0, projects which use sourcemaps to debug code transpiled from another language (such as TypescriptToLua), must specify the scriptFiles launch configuration option in order to use breakpoints in the original source files. This allows these to be resolved at startup instead of at runtime which allows for a significant performance increase.
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.
/no-console flag)io.read or other calls that require user input will cause problems in stdio mode. Set program.communication to pipe to work around this.debug library, and no other code should attempt to set debug hooks.lldebugger.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
lldebugger.call():
lldebugger.call(function()
--code causing runtime error
end)
LOCAL_LUA_DEBUGGER_FILEPATH:
package.loaded["lldebugger"] = assert(loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH")))()
require("lldebugger").start()
scriptRootsA 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.
scriptFilesA list of glob patterns identifying where to find Lua scripts in the workspace when debugging. This is required for placing breakpoints in sourcemapped files (ex. 'ts' scripts when using TypescriptToLua), as the source files must be looked up ahead of time so that breakpoints can be resolved.
Example: scriptFiles: ["**/*.lua"]
ignorePatternsA list of Lua patterns that specifies files to skip when stepping through code.
Example: ignorePatterns: ["^/usr"]
stepUnmappedLinesStep into Lua when stepping through source-mapped code and no mapping is available for the current line.
breakInCoroutinesBreak into the debugger when errors occur inside coroutines.
coroutine.wrap will always break, regardless of this option.stopOnEntryAutomatically break on first line after debug hook is set.
cwdSpecify working directory to launch executable in. Default is the project directory.
argsList of arguments to pass to Lua script or custom environment when launching.
envSpecify environment variables to set when launching executable.
program.communicationSpecifies how the extension communicates with the debugger.
Possible values:
stdio (default): Messages are embeded in stdin and stdout.pipe: Pipes are created for passing messages (named pipes on Windows, fifos on Linux). Use this if your environment has issues with stdio communication.verboseEnable 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 console must be set to false (the default value) in conf.lua, or the debugger will not be able to communicate with the running program.
game/conf.lua
function love.conf(t)
t.console = false
end
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"
],
"ignorePatterns": "^/usr"
},
{
"name": "Debug Busted via Lua Interpreter",
"type": "lua-local",
"request": "launch",
"program": {
"command": "lua"
},
"args": [
"test/start-interpreter.lua"
],
"ignorePatterns": "^/usr"
}
]
}
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)
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "lua-local",
"request": "launch",
"program": {
"command": "dmengine"
},
"args": ["./build/default/game.projectc"],
"scriptRoots": ["."] // Required for debugger to find scripts
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
local lldebugger = loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH"))()
lldebugger.start()
end
function init(self)
...
end
Information on downloading dmengine for your platform can be found here.
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "lua-local",
"request": "launch",
"windows": {
"program": {
"command": "C:\\Program Files (x86)\\Corona Labs\\Corona\\Corona Simulator.exe",
},
"args": [
"/no-console",
"/debug",
"${workspaceFolder}\\main.lua"
]
},
"osx": {
"program": {
"command": "/Applications/Corona/CoronaSimulator.app/Contents/MacOS/CoronaSimulator",
},
"args": [
"-no-console"
"YES"
"-debug"
"1"
"-project"
"${workspaceFolder}/main.lua"
]
}
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
local lldebugger = loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH"))()
lldebugger.start()
end
...
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug TSTL",
"type": "lua-local",
"request": "launch",
"program": {
"command": "my_custom_environment"
},
"args": [
...
],
"scriptFiles": ["**/*.lua"] // Required for breakpoints in ts files to work
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
require("lldebugger").start()
end
...
tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
...
},
"tstl": {
"noResolvePaths": ["lldebugger"] // Required so TSTL ignores the missing dependency
}
}
257 commits
3 commits
TypeScript
93.8%
Lua
6.2%
A simple Lua debugger which requires no additional dependencies.
Beginning in version 0.3.0, projects which use sourcemaps to debug code transpiled from another language (such as TypescriptToLua), must specify the scriptFiles launch configuration option in order to use breakpoints in the original source files. This allows these to be resolved at startup instead of at runtime which allows for a significant performance increase.
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.
/no-console flag)io.read or other calls that require user input will cause problems in stdio mode. Set program.communication to pipe to work around this.debug library, and no other code should attempt to set debug hooks.lldebugger.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
lldebugger.call():
lldebugger.call(function()
--code causing runtime error
end)
LOCAL_LUA_DEBUGGER_FILEPATH:
package.loaded["lldebugger"] = assert(loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH")))()
require("lldebugger").start()
scriptRootsA 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.
scriptFilesA list of glob patterns identifying where to find Lua scripts in the workspace when debugging. This is required for placing breakpoints in sourcemapped files (ex. 'ts' scripts when using TypescriptToLua), as the source files must be looked up ahead of time so that breakpoints can be resolved.
Example: scriptFiles: ["**/*.lua"]
ignorePatternsA list of Lua patterns that specifies files to skip when stepping through code.
Example: ignorePatterns: ["^/usr"]
stepUnmappedLinesStep into Lua when stepping through source-mapped code and no mapping is available for the current line.
breakInCoroutinesBreak into the debugger when errors occur inside coroutines.
coroutine.wrap will always break, regardless of this option.stopOnEntryAutomatically break on first line after debug hook is set.
cwdSpecify working directory to launch executable in. Default is the project directory.
argsList of arguments to pass to Lua script or custom environment when launching.
envSpecify environment variables to set when launching executable.
program.communicationSpecifies how the extension communicates with the debugger.
Possible values:
stdio (default): Messages are embeded in stdin and stdout.pipe: Pipes are created for passing messages (named pipes on Windows, fifos on Linux). Use this if your environment has issues with stdio communication.verboseEnable 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 console must be set to false (the default value) in conf.lua, or the debugger will not be able to communicate with the running program.
game/conf.lua
function love.conf(t)
t.console = false
end
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"
],
"ignorePatterns": "^/usr"
},
{
"name": "Debug Busted via Lua Interpreter",
"type": "lua-local",
"request": "launch",
"program": {
"command": "lua"
},
"args": [
"test/start-interpreter.lua"
],
"ignorePatterns": "^/usr"
}
]
}
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)
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "lua-local",
"request": "launch",
"program": {
"command": "dmengine"
},
"args": ["./build/default/game.projectc"],
"scriptRoots": ["."] // Required for debugger to find scripts
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
local lldebugger = loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH"))()
lldebugger.start()
end
function init(self)
...
end
Information on downloading dmengine for your platform can be found here.
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "lua-local",
"request": "launch",
"windows": {
"program": {
"command": "C:\\Program Files (x86)\\Corona Labs\\Corona\\Corona Simulator.exe",
},
"args": [
"/no-console",
"/debug",
"${workspaceFolder}\\main.lua"
]
},
"osx": {
"program": {
"command": "/Applications/Corona/CoronaSimulator.app/Contents/MacOS/CoronaSimulator",
},
"args": [
"-no-console"
"YES"
"-debug"
"1"
"-project"
"${workspaceFolder}/main.lua"
]
}
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
local lldebugger = loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH"))()
lldebugger.start()
end
...
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug TSTL",
"type": "lua-local",
"request": "launch",
"program": {
"command": "my_custom_environment"
},
"args": [
...
],
"scriptFiles": ["**/*.lua"] // Required for breakpoints in ts files to work
}
]
}
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
require("lldebugger").start()
end
...
tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
...
},
"tstl": {
"noResolvePaths": ["lldebugger"] // Required so TSTL ignores the missing dependency
}
}
257 commits
3 commits
TypeScript
93.8%
Lua
6.2%