#!d debugger settings dialog. Generates or reads from .ini file.
F12 Breaks if there is no splashtext-break; useful if no Line# Return is specified.
RCtrl continue from break; hold down for continuous advance.
Note1: At break-points, hotkeys and timers will still run.
Note2: The presence of timers other than the debug timer will prevent straightforward, line-by-line monitoring of non-timer code.
settings (in settings dialog):
use debug = allows toggling of debug mode.
debug line-by-line = if not checked, the debugger will intercept and display commands which have an execution time of at least .01 sec.
skip 400+ lines-passed alert = is displayed when each of ~400 code lines in LinesHistory execute in less than .01 sec, meaning the debugger will miss monitoring some lines.
sleep to maintain active windows (millisec) = allows adjustment in combination with A_WinDelay, for active windows seen by target code.
Line# Return of target code = used to alert on thread completed (optional).
which Lines to check = if this is used, turn off line-by-line for faster debugging. Specified lines should be Sleep 10 or higher, used as break-points.
---
To debug, place the following 2 lines at the top of the target code to be debugged, after the subroutine label:
if debug_On GoSub Debug_Init
If you have an OnClipboardChange routine, place the following immediately after the label:
if OnClipboardChangeDeactivated OR debugRun ; or just: if debugRun RETURN
Along with the Break/Exit-debug options in the debugger, you may also Reload from the File menu of the Ahk main window.
Here is the debugger code (an example for debugging is given at the end):
SetTitleMatchMode, 2 GoSub Load_DebuggerSettings ; place in auto-execute section return Load_DebuggerSettings: if FileExist(A_ScriptDir . "\Ahk debugger settings.ini") { IniRead, Debug_On, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_On IniRead, Debug_LineByLine, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_LineByLine IniRead, Debug_skip400alert, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_skip400alert IniRead, Debug_sleepToActive, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_sleepToActive IniRead, Debug_line#_Return, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_line#_Return IniRead, Debug_LinesToCheck, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_LinesToCheck } else { Debug_On = 1 Debug_LineByLine = 1 Debug_skip400alert = 0 Debug_sleepToActive = 100 Debug_line#_Return = 0 Debug_LinesToCheck = 0 FileAppend, ( LTrim [preferences] Debug_On = 1 Debug_LineByLine = 1 Debug_skip400alert = 0 Debug_sleepToActive = 100 Debug_line#_Return = 0 Debug_LinesToCheck = 0 ), %A_ScriptDir%\Ahk debugger settings.ini } ;msgbox,,, loaded, .5 return #!d:: GoSub Load_DebuggerSettings Gui 31:Destroy Gui, 31: +AlwaysOnTop Gui, 31: Add, Checkbox, x16 y7 h20 vDebug_On Checked%Debug_On%, use debug Gui, 31: Add, Button, Default Hidden x153 y7, OK Gui, 31: Add, Checkbox, x16 y29 h20 vDebug_LineByLine Checked%Debug_LineByLine%, debug line-by-line Gui, 31: Add, Checkbox, x16 y51 h20 vDebug_skip400alert Checked%Debug_skip400alert%, skip 400+ lines-passed alert Gui, 31: Add, Edit, x16 y76 w42 h20 vDebug_sleepToActive, %Debug_sleepToActive% ;100 Gui, 31: Add, Text, x59 y76 w164,sleep to maintain active windows seen by target code (millisec) Gui, 31: Add, Edit, x16 y108 w42 h20 vDebug_line#_Return, %Debug_line#_Return% ;0 Gui, 31: Add, Text, x60 y108 w190, Line# Return of target code (optional).`nused to alert on thread completed Gui, 31: Add, Edit, x16 y140 w210 h20 vDebug_LinesToCheck, %Debug_LinesToCheck% ;0 Gui, 31: Add, Text, x16 y162 w210, which Lines to check (0 or blank = all)`nuse commas for 2+ , spaces optional.`nSet specified Lines as Sleep 10 or greater. Gui, 31: Show, h210 w250, Debug settings return 31ButtonOK: 31GuiClose: 31GuiEscape: Gui, 31:Submit Gui, 31:Destroy IniWrite, %Debug_On%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_On IniWrite, %Debug_LineByLine%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_LineByLine IniWrite, %Debug_skip400alert%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_skip400alert IniWrite, %Debug_sleepToActive%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_sleepToActive IniWrite, %Debug_line#_Return%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_line#_Return IniWrite, %Debug_LinesToCheck%, %A_ScriptDir%\Ahk debugger settings.ini, preferences, Debug_LinesToCheck return Debug_Init: WinShow, - AutoHotkey v ahk_class AutoHotkey ; in case DetectHiddenWindows is off. exclude tooltips WinRestore, - AutoHotkey v ahk_class AutoHotkey ; prepare for WinMove WinMove, - AutoHotkey v ahk_class AutoHotkey,, 0, 590, 1024, 150 ; set window to debug-style shape WinActivate, - AutoHotkey v ahk_class AutoHotkey Thread, Interrupt, 0 ; always allow debug timer if Debug_LineByLine SetBatchLines 1 ; allows line-by-line debug SetTimer Debug, 1 sleep 300 ; SetBatchLines=1 requires sufficient sleep to properly take effect return Debug: Debug_line#_Return2 := A_LineNumber + 2 Debug() return ; adding/deleting lines in this code block requires adjusting Debug_line#_Close Debug() { Debug_line#_Close := A_LineNumber + 110 ; Line of closing brace ******* global Debug_line#_Return ; return of target code being debugged global Debug_line#_Return2 ; return of debug timer routine global Debug_LinesToCheck global debugRun ; for OnClipboardChange handling global Debug_On, Debug_skip400alert, Debug_sleepToActive ; settings static Debug_line#_Return3 debugRun ++ sleep Debug_sleepToActive ; wait for window activated by target code winA := WinActive("A") ; store ID of active window clipSaved_debug := clipboard clipboard = ListLines WinActivate, - AutoHotkey v ahk_class AutoHotkey ; exclude Ahk tooltips ControlSend,, +^{end}^c, - AutoHotkey v ahk_class AutoHotkey ; call main window codeToDebug := clipboard ControlSend,, ^{end}{up 11}+{end}^c, - AutoHotkey v ahk_class AutoHotkey ; up # to last line before debug call Loop { code_Line := clipboard code_Line := StringL( code_Line, InStr(code_Line, ":")-1 ) if code_Line is number BREAK else ControlSend,, {up}{home}+{end}^c, - AutoHotkey v ahk_class AutoHotkey } clipboard := clipSaved_debug if (code_Line = Debug_line#_Return2) ; dont display Return of debug timer { Loop 400 a=1 WinActivate, ahk_id %winA% WinWaitActive, ahk_id %winA% ; restore active window seen by target code Debug_line#_Return3 := A_LineNumber + 2 if ! GetKeyState("F12", "p") RETURN } Loop 4 SendMessage, 0x115, 0, 0, Edit1, - AutoHotkey v ahk_class AutoHotkey If !Debug_skip400alert & (debugRun >1) && !InStr(codeToDebug, "`n" . Debug_line#_Close . ": }") && !InStr(codeToDebug, "`n" . Debug_line#_Return3 . ": ") { msgbox, 4353, debug, ( LTrim More than 400 lines were executed in 0.00 sec. Some lines of code being debugged have scrolled past in the Line history buffer.`n 1) Verify that Debug_line#_Close matches the line # of the closing } in Debug()`n 2) Use Thread, Interrupt, 0 before SetTimer, Debug, 1`n 3) A sleep of 10 or greater may be needed logically prior to Line %code_Line%`n [This alert can be deactivated in the debug settings dialog.]`n Continue debug? ) IfMsgBox, Cancel { debugRun = SetTimer Debug, off WinMove, - AutoHotkey v ahk_class AutoHotkey,, 153, 153, 768, 558 Loop 400 ; for code tracing readability a=1 RETURN } } If (debugRun > 1) AND (InStr(codeToDebug, "`n" . Debug_line#_Return . ": " ) ) { SplashText("*thread completed - press Space to exit", 0, 350) Keywait Space, D Keywait Space debugRun = SetTimer Debug, off SplashTextOff WinMove, - AutoHotkey v ahk_class AutoHotkey,, 153, 153, 768, 558 WinMinimize - AutoHotkey v ahk_class AutoHotkey Loop 400 a=1 RETURN } /* ; display lines in preferred editor ControlSend,, ^g, <editor_caption> sleep 50 ; use higher Priority to prevent interruption ControlSend,, %code_Line%{Enter}, <editor_caption> */ if ( ! Debug_LinesToCheck ) OR In(code_Line, Debug_LinesToCheck) OR GetKeyState("F12", "p") { SplashText("variables current to Line " . code_Line . " press RCtrl to continue. RCtrl.RShift to exit debug", 0, 500) WinSet, Enable,, exit debug Keywait RCtrl, D sleep 150 if GetKeyState("RShift", "p") AND GetKeyState("RCtrl", "p") { ; needed to exit. using before Keywait above disallows other threads Thread, Priority, 999 debugRun = SetTimer, Debug, off WinRestore, - AutoHotkey v ahk_class AutoHotkey WinMove, - AutoHotkey v ahk_class AutoHotkey,, 153, 153, 768, 558 WinMinimize - AutoHotkey v ahk_class AutoHotkey } SplashTextOff } Loop 400 ; for code tracing readability & to prevent false thread-complete alert a=1 WinActivate, ahk_id %winA% WinWaitActive, ahk_id %winA%,, 1.2 ; restore active window seen by target code } ; this line number must match Debug_line#_Close ******* StringL(in, charsToKeep, charsToTrim="") { StringLeft, out, in, %charsToKeep% if charsToTrim StringTrimLeft, out, out, %charsToTrim% return out } In( a, b ) ; If a in b list { Loop, Parse, b, `,, %A_Space%%A_Tab% if a = %A_LoopField% Return 1 } SplashText(title="", timeout="", width="", height="", text="") ; default timeout 700 { SplashTextOn, %Width%, %Height%, %Title%, %Text% if timeout = sleep 700 else if timeout = 0 ; leave on until replaced or turned off later RETURN else sleep %timeout% SplashTextOff return } !-:: ; example code for debug if debug_On GoSub Debug_Init ; the lines above prepare the debug ; the lines below are the code to debug (example) Loop 3 { sleep 10 a=1 b=2 c=3 d=4 } Return
i will probably improve the code to optionally show last 10 lines executed, or last N lines specified by the user. Also, it would be good to have a display of target variables, selected by the user beforehand. Currently, ^v to display all the script's variables, will have to suffice. I will also look into removing the timer-limitation in line-by-line mode.