Created
September 19, 2012 02:58
-
-
Save JustAPerson/3747403 to your computer and use it in GitHub Desktop.
Debugger attempt for Textadept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ~/.textadept/modules/lua/debugger2.lua | |
-- My derived work, based on ~/.textadept/modules/lua/debugger.lua | |
require 'textadept.debugger' | |
local mobdebug = require 'lua.mobdebug' | |
local handle = mobdebug.handle; | |
local debugger = _M.textadept.debugger.new('lua'); | |
_M.lua.debugger = debugger; | |
function debugger.get_stacklevel() | |
end | |
function debugger.get_env() | |
end | |
function debugger.debug_hook(event, line, thread, level) | |
mobdebug.handle(event); | |
end | |
function debugger:handle_start(filename) | |
filename = filename or buffer.filename; | |
os.execute("gnome-terminal -x lua %s " .. filename .. " read read") | |
end | |
function debugger:handle_stop() | |
error("FIXME HANDLE_STOP") | |
end | |
function debugger:handle_continue() | |
handle('run'); | |
end | |
function debugger:handle_step_into() | |
handle('step'); | |
end | |
function debugger:handle_step_over() | |
handle('over'); | |
end | |
function debugger:handle_step_out() | |
handle('out'); | |
end | |
function debugger:handle_set_watch(expr) | |
handle('setw ' .. expr); | |
self.watches[expr] = #self.watches + 1; | |
end | |
function debugger:handle_delete_watch() | |
handle('delw ' .. self.watches[expr]) | |
self.watches[expr] = nil; | |
end | |
function debugger:get_call_stack() | |
error("FIXME GET_CALL_STACK") | |
end | |
function debugger:set_stack() | |
error("FIXME SET_STACK") | |
end | |
function debugger:handle_inspect(symbol, pos) | |
error("FIXME HANDLE_INSPECT") | |
end | |
function debugger:handle_command(text) | |
error("FIXME HANDLE_COMMAND"); | |
end | |
-- Key commands. | |
keys.lua[keys.LANGUAGE_MODULE_PREFIX].d = { | |
d = debugger.start, | |
q = debugger.stop, | |
c = debugger.continue, | |
n = debugger.step_over, | |
s = debugger.step_into, | |
o = debugger.step_out, | |
i = debugger.inspect, | |
l = debugger.call_stack, | |
b = debugger.toggle_breakpoint, | |
B = debugger.delete_breakpoint, | |
w = debugger.set_watch, | |
W = debugger.delete_watch | |
} | |
-- Context menu. | |
local SEPARATOR = { '' } | |
_M.lua.context_menu = { | |
{ 'Undo', buffer.undo }, | |
{ 'Redo', buffer.redo }, | |
SEPARATOR, | |
{ 'Cut', buffer.cut }, | |
{ 'Copy', buffer.copy }, | |
{ 'Paste', buffer.paste }, | |
{ 'Delete', buffer.clear }, | |
SEPARATOR, | |
{ 'Select All', buffer.select_all }, | |
SEPARATOR, | |
{ title = 'De_bug', | |
{ 'Start _Debugging', debugger.start }, | |
{ 'Sto_p Debugging', debugger.stop }, | |
SEPARATOR, | |
{ 'Debug _Continue', debugger.continue }, | |
{ 'Debug Step _Over', debugger.step_over }, | |
{ 'Debug Step _Into', debugger.step_into }, | |
{ 'Debug Step Ou_t', debugger.step_out }, | |
SEPARATOR, | |
{ 'Debug I_nspect', debugger.inspect }, | |
{ 'Debug Call Stac_k...', debugger.call_stack }, | |
SEPARATOR, | |
{ 'Toggle _Breakpoint', debugger.toggle_breakpoint }, | |
{ '_Delete Breakpoint...', debugger.delete_breakpoint }, | |
{ 'Set _Watch Expression', debugger.set_watch }, | |
{ 'D_elete Watch Expression...', debugger.delete_watch }, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ~/.textadept/modules/lua/mobdebug.lua | |
-- Get the official file | |
-- https://github.com/pkulchenko/MobDebug/blob/master/src/mobdebug.lua |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ~/.textadept/modules/textadept/debugger.lua | |
-- Copyright 2007-2011 Mitchell mitchell<att>caladbolg.net. See LICENSE. | |
local _m = _M; | |
local L = _L; | |
local events = events | |
--- | |
-- Language debugging support for the textadept module. | |
module('_M.textadept.debugger', package.seeall) | |
-- Markdown: | |
-- ## Overview | |
-- | |
-- The debugger interface allows Textadept to communicate with a debugger for | |
-- stepping through code graphically. It supports many common actions such as | |
-- setting breakpoints and watch expressions, continuing, and stepping into, | |
-- over, and out of functions. More actions can be added for specific debugger | |
-- instances. This document provides the information necessary in order to | |
-- implement a new debugger for a language. For illustrative purposes, a | |
-- debugger for C/C++ that uses the GNU Debugger (gdb) will be created. | |
-- | |
-- ## Implementing a Debugger | |
-- | |
-- Debugger instances exist per-languge and are typically defined in a | |
-- [language-specific module](../manual/7_Modules.html#language_specific). | |
-- First check to see if the module for your language has a debugger. If not, | |
-- you will need to implement one. The language modules included with Textadept | |
-- have debuggers so they can be used for reference. If your language uses a | |
-- debugger like gdb, you can copy and modify C/C++'s debugger, saving some time | |
-- and effort. | |
-- | |
-- #### Introduction | |
-- | |
-- Open the languge-specific module for editing and create a new instance of a | |
-- debugger. | |
-- | |
-- $> # from either _HOME or _USERHOME: | |
-- $> cd modules/cpp/ | |
-- $> textadept init.lua | |
-- | |
-- debugger = _m.textadept.debugger.new('cpp') | |
-- | |
-- Where 'cpp' is replaced by your language's name. | |
-- | |
-- #### Syntax Options | |
-- | |
-- The syntax of different languages varies so the debugger must be configured | |
-- for your language in order to function properly. | |
-- | |
-- ##### symbol_chars | |
-- | |
-- In addition to the usual `[%w_%.]` symbol characters, C/C++ also allows | |
-- symbols to contain `->`. | |
-- | |
-- debugger.symbol_chars = '[%w_%.%->]' | |
-- | |
-- #### Debugger Functions | |
-- | |
-- The debugger interface provides the framework for a debugger, but the | |
-- instance-specific functions still have to be defined. | |
-- | |
-- ##### handle_start | |
-- | |
-- ##### handle_set_breakpoint | |
-- | |
-- ##### handle_delete_breakpoint | |
-- | |
-- ##### handle_set_watch | |
-- | |
-- ##### handle_delete_watch | |
-- | |
-- ##### handle_stop | |
-- | |
-- ##### handle_continue | |
-- | |
-- ##### handle_step_into | |
-- | |
-- ##### handle_step_over | |
-- | |
-- ##### handle_step_out | |
-- | |
-- ##### handle_inspect | |
-- | |
-- ##### handle_command | |
-- | |
-- ##### Other Functions | |
-- | |
-- It is your responsibility to handle any errors that occur and stop the | |
-- debugger (`debugger:stop()`) if necessary to avoid unexpected behavior. | |
-- | |
-- #### Summary | |
-- | |
-- The above method of setting syntax options and defining debugger functions is | |
-- sufficient for most language debuggers. The rest of this document is devoted | |
-- to more complex techniques. | |
-- | |
-- ## Settings | |
-- | |
-- * `MARK_BREAKPOINT_COLOR` [number]: The color used for a breakpoint line in | |
-- `0xBBGGRR` format. | |
-- * `MARK_BREAKPOINT_ALPHA` [number]: The alpha used for a breakpoint line. The | |
-- default is `128`. | |
-- * `MARK_DEBUGLINE_COLOR` [number]: The color used for the current debug line | |
-- in `0xBBGGRR` format. | |
-- * `MARK_DEBUGLINE_ALPHA` [number]: The alpha used for the current debug line. | |
-- The default is `128`. | |
-- Settings. | |
MARK_BREAKPOINT_COLOR = 0x6D6DD9 | |
MARK_BREAKPOINT_ALPHA = 0x80 | |
MARK_DEBUGLINE_COLOR = 0x6DD96D | |
MARK_DEBUGLINE_ALPHA = 0x80 | |
-- End settings. | |
local debuggers = {} | |
local MARK_BREAKPOINT = _SCINTILLA.next_marker_number() | |
local MARK_DEBUGLINE = _SCINTILLA.next_marker_number() | |
-- Gets the debugger for the current language, if any. | |
-- This is necessary for menu items and key commands assigned to generic | |
-- debugger functions without regard to the current language. | |
-- @return debugger or `nil` | |
local function get_debugger() | |
local m = _m[buffer:get_lexer()] | |
return m and m.debugger | |
end | |
--- | |
-- Sets a breakpoint on the given line of the current file. | |
-- The debugger will break when that point in execution is reached. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param file The file to set the breakpoint in. Defaults to the current file. | |
-- @param line The line number to break on. Line numbers start at 1. | |
function set_breakpoint(debugger, file, line) | |
debugger = debugger or get_debugger() | |
if not debugger or not line then return end | |
if not file then file = buffer.filename end | |
print("set_breakpoint", debugger, file, line) | |
if debugger.debugging then | |
if not debugger:handle_set_breakpoint(file, line) then return end | |
end | |
if not debugger.breakpoints[file] then debugger.breakpoints[file] = {} end | |
debugger.breakpoints[file][line] = true | |
print("set_breakpoint2", file, buffer.filename); | |
if file == buffer.filename then | |
print("YOOOOO", buffer:marker_add(line - 1, MARK_BREAKPOINT)); | |
end | |
end | |
--- | |
-- Called to add a breakpoint to the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. You do not have to modify the `debugger.breakpoints` table; you | |
-- only have to add the breakpoint to the debugger instance. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param file The file to add the breakpoint to. | |
-- @param line The line number to add the breakpoint at. Line numbers start at | |
-- 1. | |
-- @return `true` if successful; `false` otherwise | |
function handle_set_breakpoint(debugger, file, line) return true end | |
-- Returns a sorted list of breakpoints for a filtered list. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param file Optional file to list breakpoints from. Otherwise lists all | |
-- breakpoints in all files. | |
local function get_breakpoints(debugger, file) | |
local breakpoints = {} | |
if not file then | |
for file, file_breakpoints in pairs(debugger.breakpoints) do | |
for line, breakpoint in pairs(file_breakpoints) do | |
if breakpoint then breakpoints[#breakpoints + 1] = file..':'..line end | |
end | |
end | |
else | |
for line, breakpoint in pairs(debugger.breakpoints[file] or {}) do | |
if breakpoint then breakpoints[#breakpoints + 1] = file..':'..line end | |
end | |
end | |
table.sort(breakpoints) | |
return breakpoints | |
end | |
--- | |
-- Deletes a breakpoint on the given line of the current file. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param file The file to delete the breakpoint from. Defaults to the current | |
-- file. | |
-- @param line The line number to break on. Line numbers start at 1. | |
function delete_breakpoint(debugger, file, line) | |
debugger = debugger or get_debugger() | |
if not debugger then return end | |
if not file or not line then | |
local result = gui.filteredlist('Delete Breakpoint', 'Breakpoint:', | |
get_breakpoints(debugger, file), false, | |
'--select-multiple') | |
if not result then return end | |
for breakpoint in result:gmatch('[^\n]+') do | |
file, line = breakpoint:match('^(.+):(%d+)$') | |
line = tonumber(line) | |
debugger:delete_breakpoint(file, line) | |
end | |
return | |
end | |
if debugger.breakpoints[file] then | |
if debugger.debugging then debugger:handle_delete_breakpoint() end | |
debugger.breakpoints[file][line] = nil | |
end | |
if file == buffer.filename then | |
buffer:marker_delete(line - 1, MARK_BREAKPOINT) | |
end | |
end | |
--- | |
-- Toggles a breakpoint on the given line of the current file. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param file The file to toggle the breakpoint in. Defaults to the current | |
-- file. | |
-- @param line The line number to toggle the break on. Defaults to the current | |
-- line. Line numbers start at 1. | |
function toggle_breakpoint(debugger, file, line) | |
print("toggle_breakpoint", debugger, file, line) | |
debugger = debugger or get_debugger(); | |
if not debugger then return end | |
if not file then file = buffer.filename end | |
if not line then line = buffer:line_from_position(buffer.current_pos) + 1 end | |
if debugger.breakpoints[file] and debugger.breakpoints[file][line] then | |
delete_breakpoint(debugger, file, line) | |
else | |
set_breakpoint(debugger, file, line) | |
end | |
end | |
--- | |
-- Called to delete a breakpoint from the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. You do not have to modify the `debugger.breakpoints` table; you | |
-- only have to remove the breakpoint from the debugger instance. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param file The file to delete the breakpoint from. | |
-- @param line The line number to delete the breakpoint at. Line numbers start | |
-- at 1. | |
function handle_delete_breakpoint(debugger, file, line) end | |
--- | |
-- Sets a watch expression. | |
-- The debugger will break when the expression evaluates to `true`. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param expr The expression to watch. If `nil`, prompts the user for one. | |
-- @return watch expression ID number | |
function set_watch(debugger, expr) | |
debugger = debugger or get_debugger() | |
if not debugger then return end | |
if not expr then | |
local out = gui.dialog('standard-inputbox', | |
'--title', 'Set Watch', | |
'--text', 'Expression:', | |
'--no-newline') | |
local response, value = out:match('^([^\n]+)\n(.-)$') | |
if response ~= '1' or value == '' then return end | |
expr = value | |
end | |
if debugger.debugging and not debugger:handle_set_watch(expr) then return end | |
debugger.watches[#debugger.watches + 1] = expr | |
debugger.watches[expr] = #debugger.watches | |
return #debugger.watches | |
end | |
--- | |
-- Called to add a watch expression to the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. You do not have to modify the `debugger.watches` table; you only | |
-- have to add the watch to the debugger instance. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param expr The expression to watch. | |
-- @return `true` if successful; `false` otherwise | |
function handle_set_watch(debugger, expr) return true end | |
--- | |
-- Deletes a watch expression. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param i The ID number of the watch expression. If `nil`, prompts the user | |
-- for one. | |
function delete_watch(debugger, i) | |
debugger = debugger or get_debugger() | |
if not debugger then return end | |
if not i then | |
local w = {} | |
for i, watch in ipairs(debugger.watches) do w[i] = watch end | |
i = gui.filteredlist('Delete Watch', 'Expression:', w, true) | |
if not i then return end | |
i = i + 1 | |
end | |
if debugger.watches[i] then | |
if debugger.debugging then debugger:handle_delete_watch(i) end | |
debugger.watches[debugger.watches[i]] = nil | |
table.remove(debugger.watches, i) | |
end | |
end | |
--- | |
-- Called to delete a watch from the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. You do not have to modify the `debugger.watches` table; you only | |
-- have to remove the watch from the debugger instance. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param i The ID number of the watch expression. | |
function handle_delete_watch(debugger, i) end | |
--- | |
-- Start the debugger. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_start()`. | |
-- @see handle_start | |
function start(debugger, ...) | |
print("debugger.start", ...) | |
debugger = debugger or get_debugger() | |
if not debugger or debugger.debugging then return end | |
debugger.debugging = true | |
local ok, err = pcall(debugger.handle_start, debugger, ...) | |
if not ok then | |
debugger:stop() | |
error(err) | |
end | |
-- Load breakpoints and watches. | |
for file, breakpoints in pairs(debugger.breakpoints) do | |
if type(breakpoints) == 'table' then | |
for line, breakpoint in pairs(breakpoints) do | |
if breakpoint then debugger:handle_set_breakpoint(file, line) end | |
end | |
end | |
end | |
for _, expr in ipairs(debugger.watches) do debugger:handle_set_watch(expr) end | |
debugger:continue() -- start executing immediately | |
end | |
--- | |
-- Called when starting the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. `debugger:stop()` is called automatically if an error occurs. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `start()`. | |
function handle_start(debugger, ...) end | |
--- | |
-- Stop the debugger. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_stop()`. | |
-- @see handle_stop | |
function stop(debugger, ...) | |
debugger = debugger or get_debugger() | |
if not debugger or not debugger.debugging then return end | |
debugger.debugging = false | |
pcall(debugger.handle_stop, debugger, ...) | |
buffer:marker_delete_all(MARK_DEBUGLINE) | |
if debugger.state and debugger.state.error then | |
buffer:annotation_clear_all() | |
end | |
debugger.state = nil | |
end | |
--- | |
-- Called when stopping the debugger. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `stop()`. | |
function handle_stop(debugger, ...) end | |
-- Perform a debugger function. | |
-- If an error occurs, the debugger is stopped. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param f The string function name. | |
-- @param ... Any additional parameters to pass to the function. | |
local function perform(debugger, f, ...) | |
debugger = debugger or get_debugger() | |
if not debugger or not debugger.debugging then return end | |
local ok, err = pcall(debugger[f], debugger, ...) | |
if not ok then | |
debugger:stop() | |
error(err .. debug.traceback()) | |
end | |
end | |
--- | |
-- Continue debugger execution until the next breakpoint. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_continue()`. | |
-- @see handle_continue | |
function continue(debugger, ...) perform(debugger, 'handle_continue', ...) end | |
--- | |
-- Called when continuing execution until the next breakpoint. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. `debugger:stop()` is called automatically if an error occurs. | |
-- Call `debugger:update_state()` after handling the continue. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `continue()`. | |
-- @see update_state | |
function handle_continue(debugger, ...) end | |
--- | |
-- Continue debugger execution by one line, stepping into functions. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_step_into()`. | |
-- @see handle_step_into | |
function step_into(debugger, ...) perform(debugger, 'handle_step_into', ...) end | |
--- | |
-- Called when continuing execution by one line, stepping into functions. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. `debugger:stop()` is called automatically if an error occurs. | |
-- Call `debugger:update_state()` after handling the step into. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `step_into()`. | |
-- @see update_state | |
function handle_step_into(debugger, ...) end | |
--- | |
-- Continue debugger execution by one line, stepping over functions. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_step_over()`. | |
-- @see handle_step_over | |
function step_over(debugger, ...) perform(debugger, 'handle_step_over', ...) end | |
--- | |
-- Called when continuing execution by one line, stepping over functions. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. `debugger:stop()` is called automatically if an error occurs. | |
-- Call `debugger:update_state()` after handling the step over. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `step_over()`. | |
-- @see update_state | |
function handle_step_over(debugger, ...) end | |
--- | |
-- Continue debugger execution, stepping out of the current function. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `handle_step_out()`. | |
-- @see handle_step_out | |
function step_out(debugger, ...) perform(debugger, 'handle_step_out', ...) end | |
--- | |
-- Called when continuing execution, stepping out of the current function. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. `debugger:stop()` is called automatically if an error occurs. | |
-- Call `debugger:update_state()` after handling the step out. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `step_out()1. | |
-- @see update_state | |
function handle_step_out(debugger, ...) end | |
--- | |
-- Updates the debugger's state and marks the current debug line. | |
-- This method should be called whenever the debugger's state has changed, | |
-- typically in the set of `handle_*` functions. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param state A table with at least two fields: `file` and `line`, indicating | |
-- the debugger's current position. It will be assigned to the `debugger.state` | |
-- field and may also contain other information useful to the debugger | |
-- implementation. If an `error` field is present, the error message is shown in | |
-- an annotation. If state is `nil` or not a table, `debugger:stop()` is called. | |
-- @see state | |
function update_state(debugger, state) | |
debugger.state = state | |
if type(state) ~= 'table' then | |
debugger:stop() | |
if state then error(state) end | |
return | |
end | |
buffer:marker_delete_all(MARK_DEBUGLINE) | |
local file = state.file:iconv('UTF-8', _CHARSET) | |
if state.file ~= buffer.filename then io.open_file(file) end | |
buffer:marker_add(state.line - 1, MARK_DEBUGLINE) | |
buffer:goto_line(state.line - 1) | |
if state.error then | |
buffer:annotation_set_text(state.line - 1, state.error) | |
buffer.annotation_style[state.line - 1] = 8 -- error style number | |
end | |
end | |
--- | |
-- Show the current call stack in a dropdown box in order to move between | |
-- frames. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param ... Any additional parameters passed to `get_call_stack()`. | |
-- @see get_call_stack | |
-- @see set_stack | |
function call_stack(debugger, ...) | |
debugger = debugger or get_debugger() | |
if not debugger or not debugger.debugging then return end | |
local stack, pos = debugger:get_call_stack(...) | |
local out = gui.dialog('standard-dropdown', | |
'--title', 'Call Stack', | |
'--items', stack, | |
'--selected', pos or 0) | |
local response, level = out:match('^(%d+)\n(%d+)') | |
if response == '1' then debugger:set_stack(tonumber(level)) end | |
end | |
--- | |
-- Called when showing the current call stack. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. It is your responsibility to handle any errors that occur and stop | |
-- the debugger if necessary to avoid unexpected behavior. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param ... Any additional parameters passed from `call_stack()`. | |
-- @return a table of string stack positions and a number indicating the current | |
-- stack position. The number defaults to `0`, the first table value. | |
function get_call_stack(debugger, ...) end | |
--- | |
-- Called when changing stack frames. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. It is your responsibility to handle any errors that occur and stop | |
-- the debugger if necessary to avoid unexpected behavior. | |
-- Call `debugger:update_state()` after changing stack frames. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param level The level of the stack frame to change to. | |
function set_stack(debugger, level) end | |
--- | |
-- Inspects a symbol at the given position. | |
-- Symbols can have any character in the `debugger.symbol_chars` pattern and | |
-- must have a style defined in the `debugger.inspect_styles` table. | |
-- @param debugger The debugger returned by `debugger.new()`. Defaults to the | |
-- debugger for the current language. | |
-- @param pos The buffer position to inspect at. | |
-- @see handle_inspect | |
function inspect(debugger, pos) | |
local buffer = buffer | |
debugger = debugger or get_debugger() | |
if not pos then pos = buffer.current_pos end | |
if debugger and debugger.debugging and pos > 0 and | |
debugger.inspect_styles[buffer:get_style_name(buffer.style_at[pos])] then | |
local s = buffer:position_from_line(buffer:line_from_position(pos)) | |
local e = buffer:word_end_position(pos, true) | |
local line = buffer:text_range(s, e) | |
debugger:handle_inspect(line:match(debugger.symbol_chars..'+$'), pos) | |
end | |
end | |
--- | |
-- Called when inspecting a symbol during a debug session. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. Usually a call tip is displayed with the symbol's value. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param symbol The symbol being inspected. | |
-- @param position The buffer position at inspection. This is useful for | |
-- displaying a call tip. | |
-- @see buffer.call_tip_show | |
function handle_inspect(debugger, symbol, position) end | |
--- | |
-- Called when a command in the command entry is entered during a debug session. | |
-- This method should be replaced with your own that is specific to the debugger | |
-- instance. It is your responsibility to handle any errors that occur and stop | |
-- the debugger if necessary to avoid unexpected behavior. | |
-- If you want, call `gui.command_entry.focus()` to hide the command entry while | |
-- it still has focus. | |
-- @param debugger The debugger returned by `debugger.new()`. | |
-- @param text The command text. | |
-- @return `true` if the command was consumed | |
function handle_command(debugger, text) end | |
--- | |
-- Creates a new debugger for the given lexer language. | |
-- Only one debugger can exist per language. | |
-- @param lang The lexer language to create a debugger for. | |
-- @return debugger | |
-- @usage local debugger = _m.textadept.debugger.new('lua') | |
function new(lang) | |
local debugger = debuggers[lang] | |
if debugger then | |
if debugger.debugging then error('Debugger running') end | |
debugger.breakpoints = nil | |
debugger.watches = nil | |
end | |
debugger = setmetatable({ | |
lexer = lang, | |
debugging = false, | |
symbol_chars = '[%w_%.]', | |
--- | |
-- The set of breakpoints for the debugger. | |
-- Each key is a filename that contains a table of line numbers with boolean | |
-- values indicating whether or not breakpoints are set on those lines. When | |
-- the debugger reaches a line that has a breakpoint, it breaks. | |
-- @class table | |
-- @name breakpoints | |
breakpoints = {}, | |
--- | |
-- The set of watches for the debugger. | |
-- This table contains a list of watch expressions and also watch expression | |
-- keys with values indicating the index of that expression in the table. | |
-- @class table | |
-- @name watches | |
watches = {}, | |
--- | |
-- The current state of the debugger. | |
-- It is guaranteed to contain `file` and `line` fields, but can also contain | |
-- other fields useful to the debugger implementation. | |
-- @class table | |
-- @name state | |
-- @field file The file (encoded in _CHARSET, not UTF-8) the debugger is in. | |
-- @field line The line the debugger is on. | |
-- @field error If an error occured in the program being debugged, this is an | |
-- error message that will be displayed as an annotation. | |
-- @see update_state | |
state = {}, | |
--- | |
-- The styles a symbol can have in order to determine and show the symbol's | |
-- value during a debug session. | |
-- Each key is a style name with a boolean value indicating whether or not the | |
-- style can contain a symbol. The default contains the `identifier` style. | |
-- @field identifier Identifiers contain symbols. | |
inspect_styles = { identifier = true }, | |
super = setmetatable({}, { __index = _M }) | |
}, { __index = _M }) | |
debuggers[lang] = debugger | |
return debugger | |
end | |
-- Sets view properties for debug markers. | |
local function set_marker_properties() | |
local buffer = buffer | |
buffer.marker_back[MARK_BREAKPOINT] = MARK_BREAKPOINT_COLOR; | |
buffer.marker_alpha[MARK_BREAKPOINT] = MARK_BREAKPOINT_ALPHA; | |
buffer.marker_back[MARK_DEBUGLINE] = MARK_DEBUGLINE_COLOR; | |
buffer.marker_alpha[MARK_DEBUGLINE] = MARK_DEBUGLINE_ALPHA; | |
end | |
if buffer then set_marker_properties() end | |
events.connect(events.VIEW_NEW, set_marker_properties) | |
-- Set breakpoint on margin-click. | |
events.connect(events.MARGIN_CLICK, function(margin, position, modifiers) | |
print("MARGIN_CLICK", margin, position, modifiers); | |
if margin == 1 and modifiers == 0 then | |
toggle_breakpoint(nil, nil, buffer:line_from_position(position) + 1) | |
end | |
end) | |
-- Update breakpoints after switching buffers. | |
events.connect(events.BUFFER_AFTER_SWITCH, function() | |
local debugger = get_debugger() | |
if not debugger then return end | |
local file = buffer.filename | |
if not debugger.breakpoints[file] then return end | |
local buffer = buffer | |
-- Delete markers for breakpoints that have been removed. | |
local line = buffer:marker_next(0, 2^MARK_BREAKPOINT) | |
while line >= 0 do | |
if not debugger.breakpoints[file][line + 1] then | |
buffer:marker_delete(line, MARK_BREAKPOINT) | |
end | |
line = buffer:marker_next(line + 1, 2^MARK_BREAKPOINT) | |
end | |
-- Add markers for breakpoints that have been added. | |
for line, v in pairs(debugger.breakpoints[file]) do | |
if v and buffer:marker_next(line - 1, 2^MARK_BREAKPOINT) ~= line - 1 then | |
buffer:marker_add(line - 1, MARK_BREAKPOINT) | |
end | |
end | |
end) | |
-- Inspect symbols and show call tips during mouse dwell events. | |
events.connect(events.DWELL_START, function(pos) inspect(nil, pos) end) | |
events.connect(events.DWELL_END, buffer.call_tip_cancel) | |
-- Handle command entry commands. | |
events.connect(events.COMMAND_ENTRY_COMMAND, function(text) | |
local debugger = get_debugger() | |
if debugger and debugger.debugging and debugger:handle_command(text) then | |
return true | |
end | |
end, 1) -- place before command_entry.lua's handler (if necessary) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ~/.textadept/modules/lua/debugger.lua | |
-- Original (broken) debugger, by Mitchell | |
-- Copyright 2007-2011 Mitchell mitchell<att>caladbolg.net. See LICENSE. | |
-- Lua Debugger extension for the Lua module. | |
-- Markdown: | |
-- ## Key Commands | |
-- | |
-- + `Ctrl+L, D, S` (`⌘L, D, S` on Mac OSX): Start debugging. | |
-- + `Ctrl+L, D, Q` (`⌘L, D, Q` on Mac OSX): Stop debugging. | |
-- + `Ctrl+L, D, C` (`⌘L, D, C` on Mac OSX): Continue. | |
-- + `Ctrl+L, D, N` (`⌘L, D, N` on Mac OSX): Step over. | |
-- + `Ctrl+L, D, S` (`⌘L, D, S` on Mac OSX): Step into. | |
-- + `Ctrl+L, D, O` (`⌘L, D, O` on Mac OSX): Step out. | |
-- + `Ctrl+L, D, I` (`⌘L, D, I` on Mac OSX): Inspect. | |
-- + `Ctrl+L, D, L` (`⌘L, D, L` on Mac OSX): Show call stack. | |
-- + `Ctrl+L, D, B` (`⌘L, D, B` on Mac OSX): Toggle breakpoint. | |
-- + `Ctrl+L, D, Shift+B` (`⌘L, D, ⇧B` on Mac OSX): Delete breakpoint. | |
-- + `Ctrl+L, D, W` (`⌘L, D, W` on Mac OSX): Set watch. | |
-- + `Ctrl+L, D, Shift+W` (`⌘L, D, ⇧W` on Mac OSX): Delete watch. | |
require 'textadept.debugger' | |
local _m = _M; | |
local debugger = _m.textadept.debugger.new('lua') | |
_m.lua.debugger = debugger | |
-- For debugging coroutines within this debugger's coroutines, the current debug | |
-- hook needs to be propagated so `coroutine.create` and `coroutine.wrap` need | |
-- to be modified. | |
local coroutine_create, coroutine_wrap = coroutine.create, coroutine.wrap | |
function coroutine.create(f) | |
local hook, mask, count = debug.gethook() | |
if not hook then return coroutine_create(f) end | |
local thread | |
local function thread_hook(event, line) hook(event, line, thread, 3) end | |
thread = coroutine_create(function(...) | |
step_level[thread], stack_level[thread] = 0, 0 | |
debug.sethook(thread_hook, mask, count) | |
return f(...) | |
end) | |
return thread | |
end | |
function coroutine.wrap(f) | |
local hook, mask, count = debug.gethook() | |
if not hook then return coroutine_wrap(f) end | |
local function thread_hook(event, line) hook(event, line, thread, 3) end | |
thread = coroutine_wrap(function(...) | |
step_level[thread], stack_level[thread] = 0, 0 | |
debug.sethook(thread_hook, mask, count) | |
return f(...) | |
end) | |
return thread | |
end | |
-- Get the stack at a given stack level for the debugger. | |
-- @param level The stack level to get the stack at. | |
-- @return table stack levels | |
function debugger.get_stack(level) | |
local stack = {} | |
while true do | |
local info = debug.getinfo(level) | |
if not info then break end | |
stack[#stack + 1] = info | |
level = level + 1 | |
end | |
-- Ignore the last two stack levels which are calls from this debugger. | |
stack[#stack], stack[#stack - 1] = nil, nil | |
return stack | |
end | |
-- Get the environment at a given stack level for the debugger. | |
-- The returned table is suitable for setting as a function environment. | |
-- @param level The stack level to get the environment at. | |
-- @return table of name-value variable pairs. Also contains _LOCALS, _UPVALUES, | |
-- _GLOBALS, and _ENV tables containing their respective variable types. | |
function debugger.get_env(level) | |
local env = { _LOCALS = {}, _UPVALUES = {}, _GLOBALS = getfenv(0) } | |
-- Upvalues. | |
local func = debug.getinfo(level, 'f').func | |
local i = 1 | |
while true do | |
local name, value = debug.getupvalue(func, i) | |
if not name then break end | |
if name:sub(1, 1) ~= '(' then | |
env[name], env._UPVALUES[name] = value, value | |
end | |
i = i + 1 | |
end | |
env._ENV = getfenv(func) | |
-- Local variables (override upvalues as necessary). | |
i = 1 | |
while true do | |
local name, value = debug.getlocal(level, i) | |
if not name then break end | |
if name:sub(1, 1) ~= '(' then | |
env[name], env._LOCALS[name] = value, value | |
end | |
i = i + 1 | |
end | |
setmetatable(env, { __index = env._ENV, __newindex = env._ENV }) | |
return env | |
end | |
-- Hook called by the Lua debug library used for debugging. | |
-- @param event The event, either 'call', 'return', 'tail return', 'line', or | |
-- 'count'. | |
-- @param line The line number. | |
-- @param thread The currently running thread. This is non-nil when a coroutine | |
-- is being run from within the debug coroutine. | |
-- @param level The level of the currently running thread. This is non-nil when | |
-- a coroutine is being run from within the debug coroutine. | |
function debugger.debug_hook(event, line, thread, level) | |
if not debugger.debugging then return end | |
debugger.current_thread = thread or 'main' | |
level = level or 2 | |
local step_level, stack_level = debugger.step_level, debugger.stack_level | |
local current_thread = debugger.current_thread | |
if event == 'call' then | |
stack_level[current_thread] = stack_level[current_thread] + 1 | |
elseif event == 'return' then | |
stack_level[current_thread] = stack_level[current_thread] - 1 | |
if stack_level[current_thread] < 0 then stack_level[current_thread] = 0 end | |
if stack_level['main'] == 1 then debugger:stop() end -- finished xpcall() | |
else | |
-- Get the filename. | |
local file = debug.getinfo(level, 'S').source:match('^@?(.+)$') | |
if file:find('modules[/\\]lua[/\\]debugger%.lua$') then return end | |
-- Get the stack trace. | |
local stack = debugger.get_stack(level + 1) | |
-- Get the current environment. | |
local env = debugger.get_env(level + 1) | |
-- Check watches. | |
local watch_id | |
for i = 1, #debugger.watches do | |
local f = debugger.watches[debugger.watches[i]] | |
if type(f) == 'function' then | |
local ok, result = pcall(setfenv(f, env)) | |
if ok and result then watch_id = i break end | |
end | |
end | |
-- If at a breakpoint or watch, stepping into, or stepping over, resume the | |
-- debugger coroutine to get the next instruction. | |
if debugger.breakpoints[file] and debugger.breakpoints[file][line] or | |
watch_id or debugger.stepping_into or debugger.stepping_over and | |
(stack_level[current_thread] <= step_level[current_thread] or | |
stack_level[current_thread] == 0) then | |
local command = coroutine.yield { | |
file = file, line = line, stack = stack, env = env, watch_id = watch_id | |
} | |
repeat | |
local continue = true | |
if not command then | |
debugger:stop() | |
elseif command == 'continue' then | |
debugger.stepping_into, debugger.stepping_over = false, false | |
elseif command == 'step_into' then | |
debugger.stepping_into, debugger.stepping_over = true, false | |
elseif command == 'step_over' then | |
debugger.stepping_into, debugger.stepping_over = false, true | |
step_level[current_thread] = stack_level[current_thread] | |
elseif command == 'step_out' then | |
debugger.stepping_into, debugger.stepping_over = false, true | |
step_level[current_thread] = stack_level[current_thread] - 1 | |
elseif command:find('^set_stack') then | |
local i = tonumber(command:match('^set_stack (%d+)')) | |
local info = debug.getinfo(level + i, 'Sl') | |
if info.what ~= 'C' then | |
command = coroutine.yield { | |
file = info.source:match('^@?(.+)$'), line = info.currentline, | |
stack = stack, stack_pos = i, | |
env = debugger.get_env(level + i + 1) | |
} | |
else | |
command = coroutine.yield { C = true } | |
end | |
continue = false | |
end | |
until continue | |
end | |
end | |
end | |
-- Environment for debug scripts. | |
-- @class table | |
-- @name ENV | |
local ENV = { | |
'assert', 'collectgarbage', 'dofile', 'error', 'getfenv', 'getmetatable', | |
'ipairs', 'load', 'loadfile', 'loadstring', 'next', 'pairs', 'pcall', 'print', | |
'rawequal', 'rawget', 'rawset', 'select', 'setfenv', 'setmetatable', | |
'tonumber', 'tostring', 'type', 'unpack', 'xpcall', 'coroutine', 'module', | |
'require', 'table', 'math', 'os', 'debug', 'lpeg', 'lfs', | |
'_VERSION', | |
-- Some functions and fields in the following libraries need to be excluded | |
-- either because they belong to Textadept or they are data references that | |
-- Textadept's state shares and could cause problems if modified. | |
string = { | |
'byte', 'char', 'dump', 'find', 'format', 'gmatch', 'gsub', 'len', 'lower', | |
'match', 'rep', 'reverse', 'sub', 'upper' | |
}, | |
io = { | |
'close', 'flush', 'lines', 'open', 'popen', 'read', 'tmpfile', 'write', | |
'type' | |
}, | |
package = { 'cpath', 'loaders', 'loadlib', 'path', 'seeall' } | |
} | |
-- Creates the environment for debug scripts. | |
-- @see ENV | |
local function create_env() | |
local env = {} | |
-- Create the env from ENV. | |
for k, v in pairs(ENV) do | |
if type(k) == 'number' then | |
env[v] = _G[v] | |
else | |
env[k] = {} | |
for k2, v2 in ipairs(v) do env[k][v2] = _G[k][v2] end | |
end | |
end | |
-- Create new references that do not interfere with Textadepts'. | |
env._G, env.package.loaded, env.package.preload = env, {}, {} | |
-- Modify input/output functions to interface with Textadept. | |
env.print = function(...) | |
debugger.debugging = false -- do not debug the following function calls | |
local prev_view = #_VIEWS == 1 and 1 or _VIEWS[view] | |
gui.print(...) | |
gui.goto_view(prev_view) | |
debugger.debugging = true -- resume debugging normally | |
end | |
env.io.stdin = { read = function(_, ...) | |
local input = gui.dialog('inputbox', '--title', 'stdin', '--width', 400) | |
return input:match('([^\n]+)\n$') | |
end } | |
env.io.stdout = { write = function(_, ...) env.print(...) end } | |
env.io.stderr = { write = function(_, ...) env.print('STDERR:', ...) end } | |
env.io.input = function(f) | |
if not f then return env.io.stdin end | |
env.io.stdin = f | |
end | |
env.io.output = function(f) | |
if not f then return env.io.stdout end | |
env.io.stdout = f | |
end | |
env.io.read = function(...) return env.io.input():read(...) end | |
env.io.write = function(...) return env.io.output():write(...) end | |
return env | |
end | |
-- Implementation for debugger:start(). | |
-- @param filename The file to debug. Defaults to buffer.filename. | |
function debugger:handle_start(filename) | |
print("debugger.handle_start", filename) | |
if not filename then filename = buffer.filename end | |
local f, err = loadfile(filename) | |
if not f then error(err) end | |
self.co = coroutine_create(function(f) | |
self.current_thread = 'main' | |
self.stepping_into, self.stepping_over = false, false | |
self.step_level = { [self.current_thread] = 0 } | |
self.stack_level = { [self.current_thread] = 1 } | |
coroutine.yield() | |
setfenv(f, create_env()) | |
debug.sethook(self.debug_hook, 'clr') | |
xpcall(f, function(err) | |
local info = debug.getinfo(2, 'Sl') | |
-- If the error occurs in C (e.g. via Lua's 'error' function), go up the | |
-- stack to where the error in Lua occurred. | |
if info.what == 'C' then info = debug.getinfo(3, 'Sl') end | |
local file, line = info.source:match('^@?(.+)$'), info.currentline | |
local stack, env = self.get_stack(3), self.get_env(3) | |
coroutine.yield { | |
file = file, line = line, stack = stack, env = env, error = err | |
} | |
end) | |
self:stop() | |
end) | |
coroutine.resume(self.co, f) | |
end | |
-- Implementation for debugger:stop(). | |
function debugger:handle_stop() | |
debug.sethook() | |
coroutine.resume(self.co, false) | |
end | |
-- Performs a debugger action. | |
-- @param action The action to perform: 'continue', 'step_into', 'step_over', or | |
-- 'step_out'. | |
local function handle(debugger, action) | |
print("handle", action); | |
local ok, state = coroutine.resume(debugger.co, action) | |
debugger:update_state(state) | |
end | |
-- Implementation for debugger:continue(). | |
function debugger:handle_continue() handle(self, 'continue') end | |
-- Implementation for debugger:step_into(). | |
function debugger:handle_step_into() handle(self, 'step_into') end | |
-- Implementation for debugger:step_over(). | |
function debugger:handle_step_over() handle(self, 'step_over') end | |
-- Implementation for debugger:step_out(). | |
function debugger:handle_step_out() handle(self, 'step_out') end | |
-- Implementation for debugger:set_watch(). | |
-- Loads the given expression as a Lua chunk so it can be evaluated by the debug | |
-- hook. | |
function debugger:handle_set_watch(expr) | |
local f, err = loadstring('return ('..expr..')') | |
if not f then error(err) end | |
self.watches[expr] = f | |
return true | |
end | |
-- Implementation for debugger:delete_watch(). | |
function debugger:handle_delete_watch(expr) self.watches[expr] = nil end | |
-- Implementation for debugger:get_call_stack(). | |
function debugger:get_call_stack() | |
if not self.state then return end | |
local stack = self.state.stack | |
local t = {} | |
for _, info in ipairs(stack) do | |
t[#t + 1] = ('(%s) %s:%d'):format(info.name or info.what, info.short_src, | |
info.currentline) | |
end | |
return t, self.state.stack_pos | |
end | |
-- Implementation for debugger:set_stack(). | |
function debugger:set_stack(level) | |
local ok, state = coroutine.resume(debugger.co, 'set_stack '..level) | |
if not state.C then self:update_state(state) end | |
end | |
-- Lua reserved words. | |
-- Used when displaying table keys in table_tostring(). | |
-- @class table | |
-- @name reserved | |
local reserved = { | |
['and'] = 1, ['break'] = 1, ['do'] = 1, ['else'] = 1, ['elseif'] = 1, | |
['end'] = 1, ['false'] = 1, ['for'] = 1, ['function'] = 1, ['if'] = 1, | |
['in'] = 1, ['local'] = 1, ['nil'] = 1, ['not'] = 1, ['or'] = 1, | |
['repeat'] = 1, ['return'] = 1, ['then'] = 1, ['true'] = 1, ['until'] = 1, | |
['while'] = 1 | |
} | |
local truncate_len = 25 | |
-- Prints a value to a string as it might look in Lua syntax. | |
-- @param value The value to print. | |
-- @param truncate Flag indicating whether or not to truncate long strings. | |
-- @param level The table level (non-zero for tables inside tables). | |
-- @param visited Table of visited tables so recursion does not happen. | |
local function tostringi(value, truncate, level, visited) | |
local truncate_len = truncate and truncate_len or math.huge | |
if type(value) == 'string' then | |
local v = ('%q'):format(value) | |
if #v > truncate_len then v = v:sub(1, truncate_len)..' ..."' end | |
return v | |
elseif type(value) == 'table' then | |
if not visited then visited = {} end | |
local indent = (' '):rep(2 * (level or 0)) | |
local s = { '{ -- '..tostring(value) } | |
for k, v in pairs(value) do | |
if type(k) == 'string' then | |
if #k > truncate_len then k = k:sub(1, truncate_len)..' ...' end | |
if not k:find('^[%w_]+$') or reserved[k] then k = ('[%q]'):format(k) end | |
else | |
k = '['..tostring(k)..']' | |
end | |
if type(v) == 'string' then | |
v = ('%q'):format(v) | |
if #v > truncate_len then v = v:sub(1, truncate_len)..' ..."' end | |
elseif type(v) == 'table' and not visited[v] then | |
visited[v] = true | |
v = tostringi(v, truncate, (level or 0) + 1, visited) | |
else | |
v = tostring(v) | |
end | |
s[#s + 1] = ('%s %s = %s,'):format(indent, k, v) | |
end | |
s[#s + 1] = ('%s}'):format(indent) | |
return table.concat(s, '\n') | |
else | |
return tostring(value) | |
end | |
end | |
-- Implementation for debugger:inspect(). | |
-- If a table value contains more than 20 lines, it is truncated. | |
function debugger:handle_inspect(symbol, pos) | |
if self.state and self.state.env then | |
local f = loadstring('return '..symbol) | |
local ok, value = pcall(setfenv(f, self.state.env)) | |
if not ok then return end | |
value = tostringi(value, true) | |
local lines, s = 1, value:find('\n') | |
while s and lines < 20 do | |
lines = lines + 1 | |
s = value:find('\n', s + 1) | |
end | |
if lines >= 20 then value = value:sub(1, s)..'...' end | |
buffer:call_tip_show(pos, symbol..' = '..value) | |
end | |
end | |
-- Implementation for debugger:command(). | |
-- Handle commands from the command entry during a debug session. | |
function debugger:handle_command(text) | |
if not self.state or not self.state.env then return end | |
gui.command_entry.focus() -- hide | |
gui.print(text) | |
if text:find('^%s*=') then | |
local f, err = loadstring('return '..text:match('^%s*=(.+)$')) | |
if not f then error(err) end | |
local values = { setfenv(f, self.state.env)() } | |
local n = select('#', unpack(values)) | |
for i = 1, n do values[i] = tostringi(values[i]) end | |
gui.print(table.concat(values, '\t')) | |
else | |
local f, err = loadstring(text) | |
if not f then error(err) end | |
setfenv(f, self.state.env)() | |
end | |
return true | |
end | |
-- Adeptsense. | |
_m.lua.sense.syntax.type_assignments['^(_m%.textadept%.debugger)%.new'] = '%1' | |
-- Key commands. | |
keys.lua[keys.LANGUAGE_MODULE_PREFIX].d = { | |
d = debugger.start, | |
q = debugger.stop, | |
c = debugger.continue, | |
n = debugger.step_over, | |
s = debugger.step_into, | |
o = debugger.step_out, | |
i = debugger.inspect, | |
l = debugger.call_stack, | |
b = debugger.toggle_breakpoint, | |
B = debugger.delete_breakpoint, | |
w = debugger.set_watch, | |
W = debugger.delete_watch | |
} | |
-- Context menu. | |
local SEPARATOR = { '' } | |
_m.lua.context_menu = { | |
{ 'Undo', buffer.undo }, | |
{ 'Redo', buffer.redo }, | |
SEPARATOR, | |
{ 'Cut', buffer.cut }, | |
{ 'Copy', buffer.copy }, | |
{ 'Paste', buffer.paste }, | |
{ 'Delete', buffer.clear }, | |
SEPARATOR, | |
{ 'Select All', buffer.select_all }, | |
SEPARATOR, | |
{ title = 'De_bug', | |
{ 'Start _Debugging', debugger.start }, | |
{ 'Sto_p Debugging', debugger.stop }, | |
SEPARATOR, | |
{ 'Debug _Continue', debugger.continue }, | |
{ 'Debug Step _Over', debugger.step_over }, | |
{ 'Debug Step _Into', debugger.step_into }, | |
{ 'Debug Step Ou_t', debugger.step_out }, | |
SEPARATOR, | |
{ 'Debug I_nspect', debugger.inspect }, | |
{ 'Debug Call Stac_k...', debugger.call_stack }, | |
SEPARATOR, | |
{ 'Toggle _Breakpoint', debugger.toggle_breakpoint }, | |
{ '_Delete Breakpoint...', debugger.delete_breakpoint }, | |
{ 'Set _Watch Expression', debugger.set_watch }, | |
{ 'D_elete Watch Expression...', debugger.delete_watch }, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment