Thank you so much for this program, programming ahk is much more professional and practical now.
I think it would be great to have a hotkey that can toggle iIncludeMode, what do you think?
Alternatively, possibly even better, there could be a RegExMatch option.
So you could use ^[^-]+$ to not show included files or [^:]:$ to show only labels and no hotkeys and much more.
I still prefer AnimateWindow, so here a little modified version
WindowAnimate effect settings wrote:
bEffectOpen = SR ;CENTER=C, BLEND=B, SLIDE=S (Right to left=L || Left to right=R || Top to bottom=D || Bottom to top=U)
bEffectTimeOpen := 100 ;Time for the effect when showing GUI
bEffectClose = SL ;Same as for EffectOpten, for example SU or C or B
bEffectTimeClose := 50 ;Time for the effect when closing Gui
Code:
/* TheGood
TillaGoto - Go to functions and labels in your script
*/
;______________________________________
Configuration:
;Appearance
iGUIWidth := 330 ;Specify the width of the GUI.
iGUIHeight := 32 ;Specify, in number of rows, the height of the listbox.
iMargin := 0 ;Specify the width of the GUI's margins.
iTransparency := 255 ;Specify the transparency of the GUI. Preferably divisible by 15. Put 255 for no
;transparency at all (consumes less resources). Put 0 to disable fade-in effect.
bPosLeft := True ;Set to True to position the GUI on the left side instead of the right side.
;Behaviour
bFilterComments := True ;Set to True to filter out the functions/labels/hotkeys found in comments.
;Note: this feature greatly reduces performance in large scripts. Therefore, if
;you start experiencing lag, you might want to turn it off (at the expense of
;not filtering out functions/labels/hotkeys found in comments).
;Warning: if turned off, functions must not have comments between the closing
;parenthesis of the declaration and the opening curly bracket. Similarly, labels
;must not have comments on the same line.
bQuickMode := True ;Set to True to make TillaGoto go straight to showing the GUI and exit on close.
uHotkey := "F1" ;Specify the hotkey you want to use to call up the GUI
uGoBack := "!Left" ;Specify the keyboard hotkey for going to the previous view
uGoForward := "!Right" ;Specify the keyboard hotkey for going to the next view
bMatchEverywhere := True ;Set to False for matching to occur only at the beginning of the label/function
;name. Set to True for matching to occur anywhere in the label/function name. As
;well, multiple words can be specified. For example, typing "Dog Cat" will match
;any label/function containing those words anywhere in their name. This is useful
;to search for functions (or labels) only by typing "() FunctionName".
bDirectives := True ;Set to True to scan the script for TillaGoto directives. Directives allow you to
;change settings of TillaGoto for the script you are working on. You put them in
;your script (anywhere) as a line comment. The syntax is (without quotes):
;";TillaGoto.<setting> = <value>". Supported settings are bFilterComments, and
;iIncludeMode. Example, to turn off comment filtering only for one script (and if
;the value you put here is True), use (w/o quotes) ";TillaGoto.bFilterComments=0"
;You can even omit the "0" at the end to turn it off. You can do the same thing
;for iIncludeMode. For example: ";TillaGoto.iIncludeMode=0x00111111".
;WindowAnimate effect settings
bEffectOpen = SR ;CENTER=C, BLEND=B, SLIDE=S (HOR_POSITIVE=L || HOR_NEGATIVE=R || VER_POSITIVE=D || VER_NEGATIVE=U)
bEffectTimeOpen := 100 ;Time for the effect when showing GUI
bEffectClose = SL ;Same as for EffectOpten, for example SU or C or B
bEffectTimeClose := 50 ;Time for the effect when closing Gui
;Mouse configuration
bUseMButton := True ;Set to True to enable the middle mouse button as a hotkey to call up the GUI.
;More importantly, this also allows you to go to a function/label simply by
;pressing the middle mouse button on the function/label name.
;Set to False to disable it.
iCancelWait := 300 ;Specify in milliseconds the amount of time the middle mouse button should be
;depressed while the GUI is showing for the GUI to close without selection.
;If the middle mouse button is pressed for any amount shorter than that, the
;selection is validated.
;Include files scanning feature
iIncludeMode := 0x11111101 ;This setting affects the behaviour of TillaGoto when scanning for script
;labels, functions and hotkeys in #Include files and library files. To turn
;off the feature completely, specify 0. Otherwise, iIncludeMode can be any
;combination of the following values:
;0x00000001 - Scan #Include files
;0x00000010 - Scan library directories files
;0x00000100 - Retrieve functions upon scanning
;0x00001000 - Retrieve labels upon scanning
;0x00010000 - Retrieve hotkeys upon scanning
;0x00100000 - Filter comments upon scanning (similar to bFilterComments)
;0x01000000 - Recurse (ie. include #Include files of #Include files and so on)
;0x10000000 - Append the name of the file to the functions/labels/hotkeys name
sPathMatching := "\*?\K.*(?= (-|\*) (Notepad\+\+|SciTE))" ;Regular expression which should match the path of
;the currently edited file. Necessary only if iIncludeMode <> 0.
;Window matching configuration
sActiveWindow := "\.ahk (-|\*) (Notepad\+\+|SciTE(4AutoHotkey( \[.*?\])?)?)$" ;Regular expression which
;should match the window of the editor containing the Scintilla control. It is
;preconfigured for Notepad++ and SciTE4AutoHotkey.
sScintillaClass := "Scintilla" ;Class name of the Scintilla control. Exclude instance number.
;______________________________________
;DO NOT CHANGE ANYTHING BELOW THIS LINE
If bPastConfig
Return
bPastConfig := True
#Include %A_ScriptDir%\RemoteBuf.ahk
#EscapeChar @
#SingleInstance Force
SetTitleMatchMode, RegEx
;Create GUI
Gui, +AlwaysOnTop +Border +ToolWindow +LastFound -Caption
Gui, Font, s8, Courier New
Gui, Margin, %iMargin%, %iMargin%
Gui, Add, Edit, w%iGUIWidth% h20 vtxtSearch gtxtSearch_Event hwndhtxtsearch,
Gui, Add, ListBox, Sort wp r%iGUIHeight% vlblList glblList_Event hwndhlblList +HScroll,
hGui := WinExist()
;Get scrollbar width
SysGet, SM_CXVSCROLL, 2
;Catch WM_INPUT, WM_KEYDOWN and WM_MOUSEWHEEL
OnMessage(255, "GUIInteract")
OnMessage(256, "GUIInteract")
OnMessage(522, "GUIInteract")
;Register the mouse with RIDEV_INPUTSINK
HID_Register(1, 2, hGui, 0x00000100)
;Check if we're in quick mode
If bQuickMode {
;Check if Notepad++ is active
hNPP := WinActive(sActiveWindow)
;Get the process path
sEditorPath := GetProcessPath(hNPP)
;Check if it's SciTE4AutoHotkey's toolbar (thanks HotKeyIt)
If Not hNPP And WinActive("AHKToolbar4SciTE") {
hNPP := WinExist(sActiveWindow)
WinActivate, ahk_id %hNPP%
} Else If Not hNPP
ExitApp
bExitOnClose := True
Gosub SummonGUI
} Else bExitOnClose := False
;Main monitoring loop
Loop {
Sleep, 200
;Check if Notepad++ is active
h := WinActive(sActiveWindow)
;Skip the loop if Notepad++ is not active
If Not h And Not WinActive("ahk_id " hGui) {
;Turn off hotkeys
Hotkey, %uHotkey%, SummonGUI, Off
Hotkey, %uGoBack%, PreviousView, Off
Hotkey, %uGoForward%, NextView, Off
;Hide GUI if showing
If bShowing
Gosub, GuiEscape
hNPP := 0 ;Reset value
Continue
;Check if we just found a new window
} Else If (h <> hNPP) And Not WinActive("ahk_id " hGui) {
;Remember the newfound Notepad++ window and script
hNPP := h
;Get the process path
sEditorPath := GetProcessPath(hNPP)
;Turn on hotkeys
Hotkey, %uHotkey%, SummonGUI, On
Hotkey, %uGoBack%, PreviousView, On
Hotkey, %uGoForward%, NextView, On
}
}
;------------\
;GUI related |
;------------/
;User summoned the GUI
SummonGUI:
;Switch thread to full speed
SetBatchLines, -1
;Check if we're already showing
If bShowing {
If bWheeled
Goto SelectItem
bWheeled := True ;So that if the user presses the hotkey again,
;the first item selected will be validated
ControlFocus,, ahk_id %htxtSearch%
Return
}
;Get handle to focused control
ControlGetFocus, cSci, ahk_id %hNPP%
;Check if it fits the class name
If InStr(cSci, sScintillaClass)
ControlGet, hSci, Hwnd,, %cSci%, ahk_id %hNPP%
Else Return
;Get the filename
WinGetActiveTitle, t
RegExMatch(t, sPathMatching, sScriptPath)
Gosub, AnalyseScript
;Check if we're doing CheckOnClick
If bCheckClick {
clickText := CheckTextClick(clickX, clickY)
If clickText {
Gosub SelectItem
If Not bCheckClick
Return
}
bCheckClick := False
}
;Check if text is selected
s := Sci_GetSelText(hSci)
If (s <> "") And Not InStr(s, "@n") {
;Copy the selected text in the textbox
GuiControl,, txtSearch, %s%
;Create a list based on sel
CreateList(s)
;Check if it's just one match. If so, go to it. LB_GETCOUNT.
SendMessage, 395, 0, 0,, ahk_id %hlblList%
If (ErrorLevel = 1)
Goto SelectItem
;Select all. EM_SETSEL
SendMessage, 177, 0, -1,, ahk_id %htxtSearch%
} Else { ;Otherwise, empty the textbox and show the whole list
GuiControl,, txtSearch,
CreateList()
}
ControlFocus,, ahk_id %htxtSearch%
;Get window info
WinGetPos, iX, iY,,, ahk_id %hNPP%
ControlGetPos, sX, sY, sW, sH, %cSci%, ahk_id %hNPP%
iX += sX + (Not bPosLeft ? sW - (iGUIWidth + (iMargin * 2)) - SM_CXVSCROLL - 2 : 2)
iY += sY + 2
Gui, Show, Hide AutoSize x%iX% y%iY%
bShowing := True
AnimateWindow(hGui,bEffectTimeOpen,"A" . bEffectOpen)
If Not iTransparency Or (iTransparency = 255) ;Turn off if opaque
WinSet, Transparent, OFF, ahk_id %hGui%
Else
WinSet, Transparent, %iTransparency%, ahk_id %hGui%
WinActivate, ahk_id %hGui%
ControlFocus,, ahk_id %htxtSearch%
Return
GuiEscape:
bShowing := False
bWheeled := False
AnimateWindow(hGui,bEffectTimeClose,"H". bEffectClose)
Gui, Show, w0 h0
Gui,Cancel
Gui,Hide
WinActivate, ahk_id %hNPP%
If bExitOnClose
ExitApp
EmptyMem()
Return
;Incremental searching
txtSearch_Event:
If bShowing {
GuiControlGet, s,, txtSearch
CreateList(s)
}
Return
PreviousView:
LineHistory(False)
Return
NextView:
LineHistory(True)
Return
lblList_Event:
If (A_GuiEvent <> "DoubleClick")
Return
SelectItem:
;Check if we're doing CheckTextClick
If bCheckClick {
;Try with functions first (internal first)
clickText .= "()"
i := GetIndex(clickText, True)
If Not i And (iIncludeMode & 0x10000000)
i := GetIndex(clickText, True, True)
;Check if we found something
If Not i {
StringTrimRight, clickText, clickText, 2
clickText .= ":"
i := GetIndex(clickText, False, False)
If Not i And (iIncludeMode & 0x10000000)
i := GetIndex(clickText, False, True)
If Not i
Return
bIsFunc := False
} Else bIsFunc := True
bCheckClick := False
} Else {
;Get selected item index. LB_GETCURSEL
SendMessage, 0x188, 0, 0,, ahk_id %hlblList%
s := GetListBoxItem(hlblList, ErrorLevel)
;Get type and index
bIsFunc := RegExMatch(s, "\(\)( -.*)?$")
i := GetIndex(s, bIsFunc)
}
If bIsFunc {
;Check if it's external
If sFuncs%i%_File {
;It's external. Check if it's Notepad++ or SciTE
If RegExMatch(sEditorPath, "i)scite\.exe$")
s := """-open:%f"" -goto:%l"
Else If RegExMatch(sEditorPath, "i)notepad\+\+\.exe$")
s := "-n%l ""%f"""
;Prep the parameters
SendMessage, 2370, 0, 0,, ahk_id %hSci%
StringReplace t, s, @%l, % sFuncs%i%_Line + ErrorLevel + 1
StringReplace s, s, @%l, % sFuncs%i%_Line
u := GetFile(sFuncs%i%_File, True)
;Check if backslash is necessary (for SciTE)
If RegExMatch(sEditorPath, "i)scite\.exe$")
StringReplace u, u, \, \\, All
StringReplace t, t, @%f, %u%
StringReplace s, s, @%f, %u%
t = "%sEditorPath%" %t%
s = "%sEditorPath%" %s%
;Shell it and get out
Run %t%
Run %s%
} Else ShowLine(sFuncs%i%_Line)
} Else {
;Check if it's external
If sLabels%i%_File {
;It's external. Check if it's Notepad++ or SciTE
If RegExMatch(sEditorPath, "i)scite\.exe$")
s := """-open:%f"" -goto:%l"
Else If RegExMatch(sEditorPath, "i)notepad\+\+\.exe$")
s := "-n%l ""%f"""
;Prep the parameters
SendMessage, 2370, 0, 0,, ahk_id %hSci%
StringReplace t, s, @%l, % sLabels%i%_Line + ErrorLevel + 1
StringReplace s, s, @%l, % sLabels%i%_Line
u := GetFile(sLabels%i%_File, True)
;Check if backslash is necessary (for SciTE)
If RegExMatch(sEditorPath, "i)scite\.exe$")
StringReplace u, u, \, \\, All
StringReplace t, t, @%f, %u%
StringReplace s, s, @%f, %u%
t = "%sEditorPath%" %t%
s = "%sEditorPath%" %s%
;Shell it and get out
Run %t%
Run %s%
} Else ShowLine(sLabels%i%_Line)
}
Goto GuiEscape ;Done
Return
AnimateWindow(hwnd,time,options){
local H:=0x10000, A:=0x20000,C:=0x10, B:= 0x80000,S:=0x40000,R:= 0x1, L:=0x2, D:=0x4, U:=0x8,O:="HACBSLURD",opt:="",Format:=""
opt := 0x0 + 0
Loop,parse,Options
If InStr(O,A_LoopField)
opt |= %A_LoopField%
If opt
DllCall("AnimateWindow", "UInt", hwnd, "Int", time, "UInt", opt)
WinSet, Redraw,,ahk_id %hwnd%
}
GetIndex(sSearch, bIsFunc, bAcceptAppend = False) {
Global
If bIsFunc {
If bAcceptAppend {
Loop %sFuncs0%
If InStr(sFuncs%A_Index%, sSearch " -") = 1
Return A_Index
} Else {
Loop %sFuncs0%
If (sFuncs%A_Index% = sSearch)
Return A_Index
}
} Else {
If bAcceptAppend {
Loop %sLabels0%
If InStr(sLabels%A_Index%, sSearch " -") = 1
Return A_Index
} Else {
Loop %sLabels0%
If (sLabels%A_Index% = sSearch)
Return A_Index
}
}
Return 0
}
GUIInteract(wParam, lParam, msg, hwnd) {
Local iCount, flags, bMDown, bMUp, sControl, cX, cY
Static bLButtonDown := False, bIgnoreMUp := False
Critical
;Check which message it is
If (msg = 256) { ;WM_KEYDOWN
IfEqual wParam, 13, Gosub SelectItem ;Enter
;Check if it's the textbox
If (hwnd = htxtSearch) {
If (wParam = 38) {
ControlFocus,, ahk_id %hlblList%
If Not WrapSel(True)
SendInput {Up}
} Else If (wParam = 40) {
ControlFocus,, ahk_id %hlblList%
If Not WrapSel(False)
SendInput {Down}
}
} Else If (hwnd = hlblList) { ;Make up/down wrap around
If (wParam = 38) Or (wParam = 40)
Return WrapSel(wParam = 38) ? True : ""
}
} Else If (msg = 522) And (hwnd = htxtSearch) { ;WM_MOUSEWHEEL
bWheeled := True
;Sign it if needed
wParam := wParam > 0x7FFFFFFF ? -(~wParam) - 1 : wParam
;Get notches turned
wParam := (wParam >> 16) / 120
If (wParam > 0) {
Loop % Abs(wParam) ;%
If Not WrapSel(True)
ControlSend,, {Up}, ahk_id %hlblList%
} Else {
Loop % Abs(wParam) ;%
If Not WrapSel(False)
ControlSend,, {Down}, ahk_id %hlblList%
}
} Else If (msg = 255) { ;WM_INPUT
;Get flags
flags := HID_GetInputInfo(lParam, 20 | 0x0100)
If (flags = -1) ;Check if we got an error
Return
;Check if middle mouse button is down/up
bMDown := flags & 0x0010
bMUp := flags & 0x0020
;Check for line history
If (flags & 0x0400) And GetKeyState("Shift", "P") {
iWheelTurns := Round(HID_GetInputInfo(lParam, 22 | 0x1100) / 120)
If (iWheelTurns > 0)
Loop %iWheelTurns%
LineHistory(True)
Else If (iWheelTurns < 0)
Loop % Abs(iWheelTurns) ;%
LineHistory(False)
}
;To save time for most cases this branch will be executed
If Not (bMDown Or bMUp)
Return
If bMDown And bShowing {
bIgnoreMUp := True
SetTimer, GuiEscape, -%iCancelWait%
} Else If bMUp And bShowing
Gosub SelectItem
Else If bMUp And bUseMButton And Not bIgnoreMUp And WinActive(sActiveWindow) {
;Get mouse data
MouseGetPos, clickX, clickY,, sControl
If InStr(sControl, sScintillaClass) {
ControlGet, hSci, Hwnd,, %sControl%, ahk_id %hNPP%
ControlGetPos, cX, cY,,, %sControl%, ahk_id %hNPP%
clickX -= cX, clickY -= cY
bCheckClick := True
}
Critical Off
Gosub SummonGUI
}
If bMUp And bIgnoreMUp
bIgnoreMUp := False
}
}
WrapSel(bUp) {
Local iCount, iSel
;Get selected item index and count. LB_GETCOUNT. LB_GETCURSEL.
SendMessage, 395, 0, 0,, ahk_id %hlblList%
iCount := ErrorLevel
SendMessage, 392, 0, 0,, ahk_id %hlblList%
iSel := ErrorLevel
;Select the first/last item. LB_SETCURSEL
If bUp And (iSel = 0) {
SendMessage 390, iCount - 1, 0,, ahk_id %hlblList%
Return 1
} Else If Not bUp And (iSel = iCount - 1) {
SendMessage 390, 0, 0,, ahk_id %hlblList%
Return 1
}
Return 0
}
;-------------------\
;Scanning Functions |
;-------------------/
;Retrieves labels and functions of the script
AnalyseScript:
;Reset counters
sLabels0 := 0
sFuncs0 := 0
sPaths0 := 0
;Get full text
sScript := Sci_GetText(hSci)
If bDirectives {
Gosub, Configuration ;Restore default values
GetScriptDirectives(sScript)
}
;Ban comments if necessary
If bFilterComments
FilterComments(sScript)
;Get labels and functions
GetScriptLabels(sScript)
GetScriptHotkeys(sScript)
GetScriptFunctions(sScript)
;Check if we're doing #Include files
If (iIncludeMode & 0x00000001) {
;Get the script's dir and set the default include path to it
StringLeft, sScriptDir, sScriptPath, InStr(sScriptPath, "", False, 0) - 1
SetWorkingDir, %sScriptDir%
;Loop through each #Include file
i := 1
Loop {
;Get the next include directive
i := RegExMatch(sScript, "im)^[[:blank:]]*#Include([[:blank:]]+i\*)?[[:blank:]]+\K.*?(?=\r)", t, i)
;Make sure we've got something
If Not i
Break
;Replace path variables
StringReplace t, t, % "%A_ScriptDir%", %sScriptDir%
StringReplace t, t, % "%A_AppData%", %A_AppData%
StringReplace t, t, % "%A_AppDataCommon%", %A_AppDataCommon%
;Check if it's a directory or file
s := FileExist(t)
If InStr(s, "D") ;It's a folder. Change working directory
SetWorkingDir, %t%
Else If s { ;It's a file
ScanScriptFile(t, iIncludeMode & 0x01000000)
SetWorkingDir, %sScriptDir% ;Put the working dir's path back to here
}
;Start at next line
i := InStr(sScript, "@n", False, i) + 1
}
}
;Check if we're also scanning library functions
If (iIncludeMode & 0x00000010) {
Loop, %A_MyDocuments%\AutoHotkey\Lib\*.ahk, 1, 1
ScanScriptFile(A_LoopFileLongPath, iIncludeMode & 0x01000000, True)
;Get path of running AutoHotkey
StringLeft sLibPattern, A_AhkPath, InStr(A_AhkPath, "", False, 0)
sLibPattern .= "Lib\*.ahk"
Loop, %sLibPattern%, 1, 1
ScanScriptFile(A_LoopFileLongPath, iIncludeMode & 0x01000000, True)
}
Return
ScanScriptFile(sPath, bRecurse = False, bFuncsOnly = False) {
Local sFile, s, i, sInclude, sScriptDir
sPath := AbsolutePath(sPath)
;Make sure it's not the same as the script
If (sPath = sScriptPath)
Return
;Make sure it hasn't already been done
Loop %sPaths0%
If (sPaths%A_Index% = sPath)
Return
;Load file
FileRead, sFile, %sPath%
;Remove comments if necessary
If (iIncludeMode & 0x00100000)
FilterComments(sFile, True)
;Get the script's dir and set the default include path to it
StringLeft sScriptDir, sPath, InStr(sPath, "", False, 0) - 1
SetWorkingDir, %sScriptDir%
sPaths0 += 1
sPaths%sPaths0% := sPath
;Scan for stuff
If (iIncludeMode & 0x00000100)
GetScriptFunctions(sFile, True)
If Not bFuncsOnly {
If (iIncludeMode & 0x00001000)
GetScriptLabels(sFile, True)
If (iIncludeMode & 0x00010000)
GetScriptHotkeys(sFile, True)
}
;Check if we're recursing
If bRecurse {
;Loop through each #Include file
i := 1
Loop {
;Get the next include directive
i := RegExMatch(sFile, "im)^[[:blank:]]*#Include([[:blank:]]+i\*)?[[:blank:]]+\K.*?(?=\r)"
, sInclude, i)
;Make sure we've got something
If Not i
Break
;Replace path variables
StringReplace sInclude, sInclude, % "%A_ScriptDir%", %sScriptDir%
StringReplace sInclude, sInclude, % "%A_AppData%", %A_AppData%
StringReplace sInclude, sInclude, % "%A_AppDataCommon%", %A_AppDataCommon%
;Check if it's a directory or file
s := FileExist(sInclude)
If InStr(s, "D") ;It's a folder. Change working directory
SetWorkingDir, %sInclude%
Else If s { ;It's a file
ScanScriptFile(sInclude, bRecurse)
SetWorkingDir, %sScriptDir% ;Put the working dir's path back to here
}
;Start at next line
i := InStr(sFile, "@n", False, i) + 1
}
}
}
AbsolutePath(sPath) {
If DllCall("shlwapi\PathIsRelativeA", "int", &sPath) {
VarSetCapacity(sAbs, 260)
DllCall("GetFullPathName", "str", sPath, "uint", 260, "str", sAbs, "int", 0)
Return sAbs
} Else Return sPath
}
;This sub analyses the script and add the labels in it to the array
GetScriptLabels(ByRef s, bMarkInclude = False) {
Local i, t, u
;Get the current file we're working on
If bMarkInclude
u := " -" GetFile(sPaths0)
;Reset counter
i := 1
Loop {
;Get next label
i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+:[[:blank:]]*$", t, i)
;Make sure we found something
If Not i
Break
;We found a label. Trim everything after the last colon
StringLeft t, t, InStr(t, ":", False, 0)
sLabels0 += 1 ;Increase counter
If bMarkInclude {
sLabels%sLabels0%_File := sPaths0
sLabels%sLabels0%_Line := LineFromPosEx(s, i)
If (iIncludeMode & 0x10000000)
t .= u
} Else {
sLabels%sLabels0%_File := 0
sLabels%sLabels0%_Line := LineFromPos(i)
}
sLabels%sLabels0% := t ;Add to array
;Set i to the beginning of the next line
i := InStr(s, "@n", False, i) + 1
If (i = 1)
Break
}
}
;This sub analyses the script and add the hotkeys in it to the array (uses the same array as labels)
GetScriptHotkeys(ByRef s, bMarkInclude = False) {
Local i, t, u
;Get the current file we're working on
If bMarkInclude
u := " -" GetFile(sPaths0)
i := 1
Loop {
;Get next hotkey
i := RegExMatch(s, "m)^[[:blank:]]*\K[[:blank:]a-zA-Z0-9\Q#!^+&<>*~$`-=\[]';/\.,\E]+::", t, i)
;Make sure we found something
If Not i
Break
;We found a hotkey.
sLabels0 += 1 ;Increase counter
If bMarkInclude {
sLabels%sLabels0%_File := sPaths0
sLabels%sLabels0%_Line := LineFromPosEx(s, i)
If (iIncludeMode & 0x10000000)
t .= u
} Else {
sLabels%sLabels0%_File := 0
sLabels%sLabels0%_Line := LineFromPos(i)
}
sLabels%sLabels0% := t ;Add to array
;Set i to the beginning of the next line
i := InStr(s, "@n", False, i) + 1
If (i = 1)
Break
}
}
;This sub analyses the script and add the functions in it to the array
GetScriptFunctions(ByRef s, bMarkInclude = False) {
Local i, t, u
;Get the current file we're working on
If bMarkInclude
u := " -" GetFile(sPaths0)
;Loop through the functions
i := 1
Loop {
;Get the next function
i := RegExMatch(s, "m)^[[:blank:]]*\K[a-zA-Z0-9#_@\$\?\[\]]+(?=\(.*?\)\s*?\{)", t, i)
;Check if we found something
If Not i
Break
;Make sure it's a valid function
If t Not In If,While
{ ;Increment counter
sFuncs0 += 1
t .= "()"
If bMarkInclude {
sFuncs%sFuncs0%_File := sPaths0
sFuncs%sFuncs0%_Line := LineFromPosEx(s, i)
If (iIncludeMode & 0x10000000)
t .= u
} Else {
sFuncs%sFuncs0%_File := 0
sFuncs%sFuncs0%_Line := LineFromPos(i)
}
sFuncs%sFuncs0% := t
}
;Get the next function
i := InStr(s, "@n", False, i) + 1
If (i = 1)
Break
}
}
GetScriptDirectives(ByRef s) {
Local i, sCommentFlag, val
;Get the comment flag used
i := RegExMatch(s, "im)^[[:blank:]]*#CommentFlag\K.*?(?=\r)", sCommentFlag)
If Not i
sCommentFlag := ";"
Else sCommentFlag = %sCommentFlag% ;Trim
;Check for TillaGoto.bFilterComments
bFilterComments := RegExMatch(s, "im)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.bFilterComments"
. "[[:blank:]]*=\K.*?$", val) ? val : bFilterComments
;Check for TillaGoto.iIncludeMode
iIncludeMode := RegExMatch(s, "im)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.iIncludeMode"
. "[[:blank:]]*=\K.*?$", val) ? val : iIncludeMode
}
FilterComments(ByRef s, bRespectLines = False) {
Local i, j, len, sCommentFlag, blank
i := 1
Loop {
;Get next block start
i := RegExMatch(s, "m)^[[:blank:]]*/\*", "", i)
If Not i
Break
;Get end of block, starting search at next line
j := InStr(s, "@n", False, i) + 1
j := RegExMatch(s, "Pm)^[[:blank:]]*\*/", len, j)
;Make sure there's an end of block
If Not j
len := StrLen(s) - i
Else len += (j - i)
VarSetCapacity(blank, len, 32)
;Check if we need to respect line numbers
If bRespectLines {
;Get number of lines that would be erased
l1 := LineFromPosEx(s, i)
l2 := LineFromPosEx(s, j)
Loop % (l2 - l1) ;%
NumPut(0x0D0A, blank, (A_Index - 1) * 2, "UShort")
}
;Blank out
DllCall("RtlMoveMemory", UInt, &s + i - 1, UInt, &blank, UInt, len)
If Not j
Break
Else i += len
}
;Get the comment flag used
i := RegExMatch(s, "im)^[[:blank:]]*#CommentFlag\K.*?(?=\r)", sCommentFlag)
If Not i
sCommentFlag := ";"
Else sCommentFlag = %sCommentFlag% ;Trim
;Check if the very first line is a comment
If (SubStr(s, 1, StrLen(sCommentFlag)) = sCommentFlag) {
len := InStr(s, "@r") - 1
VarSetCapacity(blank, len, 32)
DllCall("RtlMoveMemory", UInt, &s, UInt, &blank, UInt, len)
}
i := 1
Loop {
;Get next comment
i := RegExMatch(s, "iP)[[:blank:]\n]\K\Q" sCommentFlag "\E.*?(?=\r)", len, i)
;Check if we found anything
If Not i
Break
;Blank out comment
VarSetCapacity(blank, len, 32)
DllCall("RtlMoveMemory", UInt, &s + (i - 1), UInt, &blank, UInt, len)
i += len + 1
}
}
;------------------\
;ListBox Functions |
;------------------/
CreateList(filter = "") {
Global sLabels0, sFuncs0, bMatchEverywhere, hlblList
;Clear
GuiControl,, lblList,|
;Disable redraw
GuiControl, -Redraw, lblList
;Autotrim
filter = %filter%
If (filter = "") { ;Split cases for speed
Loop %sLabels0%
GuiControl,, lblList, % sLabels%A_Index%
Loop %sFuncs0%
GuiControl,, lblList, % sFuncs%A_Index%
} Else {
;Split cases for speed
If bMatchEverywhere {
;Parse words
StringSplit, words, filter, %A_Space%
;Split cases for speed
If (words0 > 1) {
Loop %sLabels0% {
bMatch := True
i := A_Index
Loop %words0% {
bMatch := bMatch And InStr(sLabels%i%, words%A_Index%)
If Not bMatch
Break
}
If bMatch
GuiControl,, lblList, % sLabels%A_Index%
}
Loop %sFuncs0% {
bMatch := True
i := A_Index
Loop %words0% {
bMatch := bMatch And InStr(sFuncs%i%, words%A_Index%)
If Not bMatch
Break
}
If bMatch
GuiControl,, lblList, % sFuncs%A_Index%
}
;It's one word
} Else {
Loop %sLabels0%
If InStr(sLabels%A_Index%, filter)
GuiControl,, lblList, % sLabels%A_Index%
Loop %sFuncs0%
If InStr(sFuncs%A_Index%, filter)
GuiControl,, lblList, % sFuncs%A_Index%
}
} Else {
Loop %sLabels0%
If (InStr(sLabels%A_Index%, filter) = 1)
GuiControl,, lblList, % sLabels%A_Index%
Loop %sFuncs0%
If (InStr(sFuncs%A_Index%, filter) = 1)
GuiControl,, lblList, % sFuncs%A_Index%
}
}
;Add hscrollbar if necessary
ListBoxAdjustHSB(hlblList)
;Select the first item. LB_SETCURSEL
SendMessage 390, 0, 0,, ahk_id %hlblList%
;Redraw
GuiControl, +Redraw, lblList
}
GetFile(i, bWholePath = False) {
Local s
If bWholePath
Return sPaths%i%
Else {
s := SubStr(sPaths%i%, InStr(sPaths%i%, "", False, 0) + 1)
s := (SubStr(s, -3) = ".ahk" ? SubStr(s, 1, -4) : s) ;Trim ".ahk"
Return s
}
}
ListBoxAdjustHSB(hLB) {
;Declare variables (for clarity's sake)
dwExtent := 0
dwMaxExtent := 0
hDCListBox := 0
hFontOld := 0
hFontNew := 0
VarSetCapacity(lptm, 53)
;Use GetDC to retrieve handle to the display context for the list box and store it in hDCListBox
hDCListBox := DllCall("GetDC", "Uint", hLB)
;Send the list box a WM_GETFONT message to retrieve the handle to the
;font that the list box is using, and store this handle in hFontNew
SendMessage 49, 0, 0,, ahk_id %hLB%
hFontNew := ErrorLevel
;Use SelectObject to select the font into the display context.
;Retain the return value from the SelectObject call in hFontOld
hFontOld := DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontNew)
;Call GetTextMetrics to get additional information about the font being used
;(eg. to get tmAveCharWidth's value)
DllCall("GetTextMetrics", "Uint", hDCListBox, "Uint", &lptm)
tmAveCharWidth := NumGet(lptm, 20)
;Get item count using LB_GETCOUNT
SendMessage 395, 0, 0,, ahk_id %hLB%
;Loop through the items
Loop %ErrorLevel% {
;Get list box item text
s := GetListBoxItem(hLB, A_Index - 1)
;For each string, the value of the extent to be used is calculated as follows:
DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", s, "int", StrLen(s), "int64P", nSize)
dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth
;Keep if it's the highest to date
If (dwExtent > dwMaxExtent)
dwMaxExtent := dwExtent
}
;After all the extents have been calculated, select the old font back into hDCListBox and then release it:
DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontOld)
DllCall("ReleaseDC", "Uint", hLB, "Uint", hDCListBox)
;Adjust the horizontal bar using LB_SETHORIZONTALEXTENT
SendMessage 404, dwMaxExtent, 0,, ahk_id %hLB%
}
GetListBoxItem(hLB, i) {
;Get length of item. 394 = LB_GETTEXTLEN
SendMessage 394, %i%, 0,, ahk_id %hLB%
;Check for error
If (ErrorLevel = 0xFFFFFFFF)
Return ""
;Prepare variable
VarSetCapacity(sText, ErrorLevel, 0)
;Retrieve item. 393 = LB_GETTEXT
SendMessage 393, %i%, &sText,, ahk_id %hLB%
;Check for error
If (ErrorLevel = 0xFFFFFFFF)
Return ""
;Done
Return sText
}
;--------------------\
;Scintilla Functions |
;--------------------/
Sci_GetText(hSci) {
;Used constants
SCI_GETLENGTH := 2006
SCI_GETTEXT := 2182
;Retrieve text length
SendMessage SCI_GETLENGTH, 0, 0,, ahk_id %hSci%
iLength := ErrorLevel
;Open remote buffer (add 1 for 0 at the end of the string)
RemoteBuf_Open(hBuf, hSci, iLength + 1)
;Fill buffer with text
SendMessage SCI_GETTEXT, iLength + 1, RemoteBuf_Get(hBuf),, ahk_id %hSci%
;Read buffer
VarSetCapacity(sText, iLength)
RemoteBuf_Read(hBuf, sText, iLength + 1)
;We're done with the remote buffer
RemoteBuf_Close(hBuf)
Return sText
}
Sci_GetSelText(hSci) {
;Used constants
SCI_GETSELTEXT := 2161
;Get length
SendMessage SCI_GETSELTEXT, 0, 0,, ahk_id %hSci%
iLength := ErrorLevel
;Check if it's none
If (iLength = 1)
Return ""
;Open remote buffer
RemoteBuf_Open(hBuf, hSci, iLength)
;Fill buffer
SendMessage, SCI_GETSELTEXT, 0, RemoteBuf_Get(hBuf),, ahk_id %hSci%
;Prep var
VarSetCapacity(sText, iLength)
RemoteBuf_Read(hBuf, sText, iLength)
;Done
RemoteBuf_Close(hBuf)
Return sText
}
Sci_GetLineText(hSci, iLine) {
;Used constants
SCI_GETLINE := 2153
SCI_LINELENGTH := 2350
;Retrieve line length
SendMessage SCI_LINELENGTH, iLine, 0,, ahk_id %hSci%
iLength := ErrorLevel
;Open remote buffer (add 1 for 0 at the end of the string)
RemoteBuf_Open(hBuf, hSci, iLength + 1)
;Fill buffer with text
SendMessage SCI_GETLINE, iLine, RemoteBuf_Get(hBuf),, ahk_id %hSci%
;Read buffer
VarSetCapacity(sText, iLength)
RemoteBuf_Read(hBuf, sText, iLength + 1)
;We're done with the remote buffer
RemoteBuf_Close(hBuf)
;Trim off ending characters
sText := RegExReplace(sText, "\R")
Return sText
}
LineFromPos(pos) {
Global
;SCI_LINEFROMPOSITION
SendMessage, 2166, pos - 1, 0,, ahk_id %hSci%
Return ErrorLevel + 1
}
LineFromPosEx(ByRef s, pos) {
i := 0, j := 1
Loop {
i := InStr(s, "@n", False, i + 1)
If (Not i) Or (i >= pos)
Break
j += 1
}
Return j
}
CheckTextClick(x, y) {
Local pos, line, linetext, i
;SCI_POSITIONFROMPOINTCLOSE
SendMessage, 2023, x, y,, ahk_id %hSci%
pos := ErrorLevel
;Check for error
If (pos = 0xFFFFFFFF)
Return False
Else {
;SCI_LINEFROMPOSITION
SendMessage, 2166, pos, 0,, ahk_id %hSci%
line := ErrorLevel
;SCI_POSITIONFROMLINE
SendMessage, 2167, line, 0,, ahk_id %hSci%
pos -= ErrorLevel
;Get line text
linetext := Sci_GetLineText(hSci, line)
;Trim after the first illegal character
i := RegExMatch(linetext, "[^a-zA-Z0-9#_@\$\?\[\]]", "", pos + 1)
If i
StringLeft, linetext, linetext, i - 1
;Trim before the first illegal character
i := RegExMatch(StringReverse(linetext), "[^a-zA-Z0-9#_@\$\?\[\]]", "", StrLen(linetext) - pos + 1)
If i
StringTrimLeft, linetext, linetext, StrLen(linetext) - i + 1
;Return found text
Return linetext
}
Return False
}
StringReverse(s) {
Loop, Parse, s
ret := A_LoopField ret
Return ret
}
ShowLine(line) {
Global hSci
;Record current line before moving
LineHistory(0, 1)
;Get the first visible line. SCI_GETFIRSTVISIBLELINE
SendMessage, 2152, 0, 0,, ahk_id %hSci%
If (ErrorLevel < line - 1) {
;Get the number of lines on screen. SCI_LINESONSCREEN
SendMessage, 2370, 0, 0,, ahk_id %hSci%
;Go to the line wanted + lines on screen. SCI_GOTOLINE
SendMessage, 2024, line - 1 + ErrorLevel, 0,, ahk_id %hSci%
}
;Go to the actual line we want. SCI_GOTOLINE
SendMessage, 2024, line - 1, 0,, ahk_id %hSci%
;Record new line
LineHistory(0, 2)
}
LineHistory(bForward, iRecordMode = 0) {
Static
Static iCurLine := 1, iLines0 := 0
Global hSci
;Check if we just need to record
If (iRecordMode = 1) ;Record current line
LH_GetCurLine(iLines%iCurLine%)
Else If (iRecordMode = 2) { ;Record to the next line
iCurLine += 1
LH_GetCurLine(iLines%iCurLine%)
;Set as the new limit
iLines0 := iCurLine
} Else If bForward { ;Forward
;Check if it is possible
If (iCurLine = iLines0)
Return
;Record the line we're on now
LH_GetCurLine(iLines%iCurLine%)
;Show the next line
iCurLine += 1
LH_SetCurLine(iLines%iCurLine%)
} Else { ;Backward
;Check if it is possible
If (iCurLine = 1)
Return
;Record the line we're on now
LH_GetCurLine(iLines%iCurLine%)
;Show the previous line
iCurLine -=1
LH_SetCurLine(iLines%iCurLine%)
}
}
LH_GetCurLine(ByRef uLine) {
Global hSci
SendMessage, 2152, 0, 0,, ahk_id %hSci% ;SCI_GETFIRSTVISIBLELINE
uLine := ErrorLevel
SendMessage 2008, 0, 0,, ahk_id %hSci% ;SCI_GETCURRENTPOS
uLine += ErrorLevel << 16
}
LH_SetCurLine(ByRef uLine) {
Global hSci
SendMessage, 2025, uLine >> 16, 0,, ahk_id %hSci% ;SCI_GOTOPOS
SendMessage, 2152, 0, 0,, ahk_id %hSci% ;SCI_GETFIRSTVISIBLELINE
SendMessage, 2168, 0, (uLine & 0xFFFF) - ErrorLevel,, ahk_id %hSci% ;SCI_LINESCROLL
}
;--------------\
;HID Functions |
;--------------/
HID_Register(UsagePage = False, Usage = False, Handle = False, Flags = 0) {
;Prep var
VarSetCapacity(uDev, 12)
;Check if hwnd needs to be null (RIDEV_REMOVE or RIDEV_EXCLUDE)
bNull := (Flags & 0x00000001) Or (Flags & 0x00000010)
NumPut(UsagePage, uDev, 0, "UShort")
NumPut(Usage, uDev, 2, "UShort")
NumPut(Flags, uDev, 4)
NumPut(bNull ? 0 : Handle, uDev, 8)
;Call
r := DllCall("RegisterRawInputDevices", "UInt", &uDev, "UInt", 1, "UInt", 12)
;Check for error
If Not r {
MsgBox % "RegisterRawInputDevices call failed.@nReturn value: " r "@nErrorLevel: " ErrorLevel
. "@nLine: " A_LineNumber "@nLast Error: " A_LastError ;%
Return -1
}
Return 0
}
HID_GetInputInfo(InputHandle, Flag) {
Static uRawInput, iLastHandle := 0
;Check if it's the same handle
If (InputHandle = iLastHandle) ;We can retrieve the data without having to call again
Return NumGet(uRawInput, Flag, HID_NumIsShort(Flag)
? (HID_NumIsSigned(Flag) ? "Short" : "UShort")
: (HID_NumIsSigned(Flag) ? "Int" : "UInt"))
Else { ;We need to get a fresh copy
;Get raw data size. RID_INPUT
r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt"
, 0x10000003, "UInt", 0, "UInt*", iSize, "UInt", 16)
If (r = -1) Or ErrorLevel {
;MsgBox % "GetRawInputData call failed.@nReturn value: " r "@nErrorLevel: " ErrorLevel
;. "@nLine: " A_LineNumber "@nLast Error: " A_LastError
Return -1
}
;Prep var
VarSetCapacity(uRawInput, iSize)
;Get raw data. RID_INPUT
r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt"
, 0x10000003, "UInt", &uRawInput, "UInt*", iSize, "UInt", 16)
If (r = -1) Or ErrorLevel {
;MsgBox % "GetRawInputData call failed.@nReturn value: " r "@nErrorLevel: " ErrorLevel
;. "@nLine: " A_LineNumber "@nLast Error: " A_LastError
ErrorLevel := -1 ;Set errorlevel to fail
Return -1
} Else If (r <> iSize) {
;MsgBox GetRawInputData did not return the correct size.@nSize returned: %r%@nSize allocated: %iSize%
ErrorLevel := -1 ;Set errorlevel to fail
Return -1
}
;Keep handle reference of current uRawInput
iLastHandle := InputHandle
;Retrieve data
Return NumGet(uRawInput, Flag, HID_NumIsShort(Flag)
? (HID_NumIsSigned(Flag) ? "Short" : "UShort")
: (HID_NumIsSigned(Flag) ? "Int" : "UInt"))
}
Return 0
}
;Internal use only
HID_NumIsShort(ByRef Flag) {
If (Flag & 0x0100) {
Flag ^= 0x0100 ;Remove it
Return True
} Return False
}
;Internal use only
HID_NumIsSigned(ByRef Flag) {
If (Flag & 0x1000) {
Flag ^= 0x1000 ;Remove it
Return True
} Return False
}
;----------------\
;Other Functions |
;--------------/
;Based on Laszlo's code (which in turn is based on Shimanov's code)
;http://www.autohotkey.com/forum/viewtopic.php?p=70690#70690
GetProcessPath(hwnd) {
WinGet, PID, PID, ahk_id %hwnd%
hProc := DllCall("OpenProcess", UInt, 0x418, Int, 0, UInt, PID)
VarSetCapacity(sPath, 255)
DllCall("psapi.dll\GetModuleFileNameExA", UInt, hProc, UInt, 0, Str, sPath, UInt, 255)
DllCall("CloseHandle", hProc)
Return sPath
}
;EmptyMem() by heresy
;http://www.autohotkey.com/forum/viewtopic.php?t=32876
EmptyMem(PID="AHK Rocks"){
pid:=(pid="AHK Rocks") ? DllCall("GetCurrentProcessId") : pid
h:=DllCall("OpenProcess", "UInt", 0x001F0FFF, "Int", 0, "Int", pid)
DllCall("SetProcessWorkingSetSize", "UInt", h, "Int", -1, "Int", -1)
DllCall("CloseHandle", "Int", h)
}