/*! TheGood TillaGoto - Go to functions and labels in your script Usage, changelog and help can be found in the thread: http://www.autohotkey.com/forum/viewtopic.php?t=41575 */ /*! All of these settings are fine with their default values. But if you use an editor other than Notepad++, Notepad2, SciTE or SciTE4AutoHotkey, you will have to set the right values for sActiveWindow, sScintillaClass, sMustExist, and sPathMatching. You will also have to report to the TillaGoto forum thread so that I can add compatibility (if possible) for external file launching for your editor (which currently only supports the three mentioned) if you wish to use the #Include file scanning feature. You may also post in the thread for help with making good regexs for your specific editor. */ ;______________________________________ ;CONFIGURATION ;Window matching configuration (preconfigured for Notepad++, Notepad2, SciTE and SciTE4AutoHotkey) sActiveWindow := "\.ahk (-|\*) (Notepad(\+\+|2)|SciTE(4AutoHotkey( \[.*?\])?)?)$" ;Regular expression which ;should match the window of the editor containing the Scintilla control. It will ;determine when TillaGoto should be monitoring hotkeys. sMustExist := "ahk_class (Notepad(\+\+|2)|SciTEWindow)" ;Needed if bQuitWithEditor is True. This regex ;will make TillaGoto quit if a matching window does not exist. It is currently set ;to match the editors' classes, but it may be set to match anything else. sPathMatching := "(\*( )?)?\K.*(?= (-|\*) (Notepad(\+\+|2)|SciTE))" ;Regular expression which should match ;the path of the currently edited file from the window title. sScintillaClass := "Scintilla" ;Class name of the Scintilla control. Exclude instance number. ;Appearance bTrayIcon := True ;Set to True to show the tray icon, False to keep it hidden. iGUIWidth := 230 ;Specify the width of the GUI. If bWideView is True, iGUIWidth represents the ;minimum width of the GUI, no matter how short the items are. iGUIHeight := 12 ;Specify, in number of rows, the height of the listbox. iMargin := 2 ;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 := False ;Set to True to position the GUI on the left side instead of the right side. bWideView := True ;Set to True to auto-resize the width of the GUI so that all the items are visible ;without the horizontal scrollbar. bAlignFilenames := False ;Set to True to align the filenames appended to functions/labels/hotkeys. Set to ;False to align them to the right (minimal GUI width). This setting is used only ;if iIncludeMode has the option 0x10000000 and if bWideView is set to True. cGUIBG := "" ;Specify the GUI's background colour. Leave blank for default. cControlBG := "" ;Specify the controls' background colour. Leave blank for default. cControlFG := "" ;Specify the controls' foreground (text) colour. Leave blank for default. iControlFontSize := 8 ;Specify the size of the font for the textbox and listbox. fControlFont := "Courier New" ;Name of the font for the textbox and listbox. Must be monospace for proper ;alignment. A better font than Courier New would be Consolas. ;Hotkeys uSummonGUI := "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 uGotoDef := "+Enter" ;Specify the hotkey you want to use to go to a function/label on whose name the ;caret is located. Similar to the middleclicking feature. ;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 := False ;Set to True to make TillaGoto go straight to showing the GUI and exit on close. ;In this mode, TillaGoto cannot keep track of line history or use hotkeys. bQuitWithEditor := False ;Set to True to make TillaGoto terminate when the editor is also terminated. If ;set to True, please also see sMustExist. 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". ;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 := 0x10100101 ;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 bCacheFiles := True ;Set to True to enable file caching so that scanning is only done once on ;files that have already been scanned in the past. This does not affect the ;current script (which is always re-scanned). Caching is highly recommended as ;it greatly enhances performance of the include files scanning feature. ;TillaGoto directives bDirectives := True /*! Set to True to scan the script for TillaGoto directives. Directives allow you to change TillaGoto's behaviour for the script you are working on. You put them in your script (anywhere) as a line comment. The syntax is (without the quotes): ";TillaGoto. = ". These settings override the default settings you put here. Supported settings are bFilterComments, iIncludeMode, and ScanFile. - bFilterComments allows you to force comment filtering on/off for the script you put it in. For example, to turn off comment filtering, add the line (without the quotes): ";TillaGoto.bFilterComments = 0" to your script. This is the only directive that also works when detected inside #Include files and library files that are scanned. - iIncludeMode allows you to change TillaGoto's default iIncludeMode settings for the script you put it in. For example, to force TillaGoto to only scan for functions in #Include files (with no other options), use: ";TillaGoto.iIncludeMode = 0x00000100". Or to simply turn it off, use: ";TillaGoto.iIncludeMode = 0" - ScanFile allows you to force TillaGoto to always scan a file, as if it was an Include file, even though it isn't used with the #Include directive. The directive is processed in exactly the same way as an #Include file would be. For example, the directive ";TillaGoto.ScanFile = %A_ScriptDir%\script.ahk" will have the same effect as if TillaGoto saw the directive "#Include %A_ScriptDir%\script.ahk". Directories to change the working directory are also supported. The variable iIncludeMode must contain the 0x00000001 option in order to work. Note that the ScanFile directives are queued up and processed right after the script's actual #Include files are scanned. Therefore, it doesn't matter where they are placed relative to the actual #Include directives, but the order in which they are written does (in order to be able to change the working directory of subsequent ones). The working directory is set back to %A_ScriptDir% before processing them. The ScanFile feature is useful when working inside a project with many satellite files. A simple ScanFile directive to the main script in each satellite file will allow access to all the functions no matter on which file you're working on. */ ;______________________________________ ;DO NOT CHANGE ANYTHING BELOW THIS LINE ;Keep backup values bFilterCommentsOrig := bFilterComments iIncludeModeOrig := iIncludeMode #Include %A_ScriptDir%\RemoteBuf.ahk #EscapeChar @ #NoTrayIcon #SingleInstance Force SetTitleMatchMode, RegEx ;Show tray icon if necessary If bTrayIcon Menu, Tray, Icon ;Get scrollbar width and height SysGet, SM_CXVSCROLL, 2 SysGet, SM_CYHSCROLL, 3 ;Check if we'll be using the caching feature If bCacheFiles { GetFileCRC32() ;Initialize GetFileCRC32 OnExit, DeleteCache ;Register sub to delete cache upon exiting the script } ;Create GUI Gui, +AlwaysOnTop +Border +ToolWindow +LastFound -Caption Gui, Font, s%iControlFontSize% c%cControlFG%, %fControlFont% Gui, Color, %cGUIBG%, %cControlBG% Gui, Margin, %iMargin%, %iMargin% Gui, Add, Edit, h20 w%iGUIWidth% vtxtSearch gtxtSearch_Event hwndhtxtsearch Gui, Add, ListBox, Sort wp vlblList glblList_Event hwndhlblList +HScroll +256 ;LBS_NOINTEGRALHEIGHT hGui := WinExist() ;Get the height of a listbox item SendMessage, 417,,,, ahk_id %hlblList% ;LB_GETITEMHEIGHT iGUIItemHeight := ErrorLevel ;Catch WM_INPUT, WM_KEYDOWN and WM_MOUSEWHEEL OnMessage(255, "GUIInteract") OnMessage(256, "GUIInteract") OnMessage(522, "GUIInteract") ;Register the mouse with RIDEV_INPUTSINK If HID_Register(1, 2, hGui, 0x00000100) MsgBox, 0x1010, HID_Register failed!, %ErrorLevel% ;Register summon hotkey before Quick Mode Hotkey, IfWinActive, ahk_id %hGui% Hotkey, %uSummonGUI%, SummonGUI ;Check if we're in quick mode If bQuickMode { ;Check for SciTE4AutoHotkey's toolbar before summoning If WinActive("AHKToolbar4SciTE") WinActivate, %sActiveWindow% ;Activate actual editor ;Go straight to summoning the GUI Goto SummonGUI } ;Register main hotkeys Hotkey, IfWinActive, %sActiveWindow% Hotkey, %uSummonGUI%, SummonGUI Hotkey, %uGotoDef%, GotoDefinition Hotkey, %uGoBack%, PreviousView Hotkey, %uGoForward%, NextView ;Optimize before starting loop or ending autoexecute section EmptyMem() If bQuitWithEditor { Loop { Sleep, 1000 ;Check if we need to quit If Not WinExist(sMustExist) ExitApp } } Return ;------------\ ;GUI related | ;------------/ ;User summoned the GUI SummonGUI: ;Switch thread to full speed SetBatchLines, -1 ;Check if focus is not on us If Not WinActive("ahk_id " hGui) { ;Check if editor is valid hEditor := WinActive(sActiveWindow) If hEditor sEditorPath := GetProcessPath(hEditor) Else Return ;We were summoned from a foreign active window } ;Check if we're already showing If bShowing { ;Check if we're currently active If WinActive("ahk_id " hGui) { ;Check if there's an item currently selected. LB_GETCURSEL SendMessage, 0x188, 0, 0,, ahk_id %hlblList% ;Check for LB_ERR If (ErrorLevel <> 0xFFFFFFFF) Goto SelectItem Else ControlFocus,, ahk_id %htxtSearch% ;Put focus back on the textbox } Else { ;We're not active. That means the editor is active ;Check if the mouse was used If bCheckClick { clickText := CheckTextClick(clickX, clickY, iPos) If clickText { Gosub, SelectItem If Not bCheckClick Return ;SelectItem found a match. We're done } bCheckClick := False ;clickText failed. Validate listbox selection if any. LB_GETCURSEL SendMessage, 0x188, 0, 0,, ahk_id %hlblList% ;Check for error. LB_ERR If (ErrorLevel <> 0xFFFFFFFF) Goto, SelectItem } ;Check if new text has been selected s := Sci_GetSelText(hSci) If (s <> sSelText) And Not InStr(s, "@n") { ;Keep the old sSelText := s ;Copy the selected text in the textbox GuiControl,, txtSearch, %sSelText% ;Create a list based on sel CreateList(sSelText) ;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% } ;Focus on textbox WinActivate, ahk_id %hGui% ControlFocus,, ahk_id %htxtSearch% } } ;Get handle to focused control ControlGetFocus, cSci, ahk_id %hEditor% ;Check if it fits the class name If InStr(cSci, sScintillaClass) ControlGet, hSci, Hwnd,, %cSci%, ahk_id %hEditor% Else Return ;Get the filename WinGetTitle, t, ahk_id %hEditor% RegExMatch(t, sPathMatching, sScriptPath) Gosub, AnalyseScript ;Check if we're doing CheckOnClick If bCheckClick { clickText := CheckTextClick(clickX, clickY, iPos) If clickText { Gosub, SelectItem If Not bCheckClick Return } bCheckClick := False } ;Check if we have to append filename If (iIncludeMode & 0x10000000) AppendFilename() ;Check if text is selected sSelText := Sci_GetSelText(hSci) If (sSelText <> "") And Not InStr(sSelText, "@n") { ;Copy the selected text in the textbox GuiControl,, txtSearch, %sSelText% ;Create a list based on sel CreateList(sSelText) ;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() } ;Set up textbox and listbox width If bWideView { ;We need to calculate the dimensions of the GUI to accomodate all items ;Check if there will be a vscroll if filter is empty bVScroll := (sLabels0 + sFuncs0 > iGUIHeight) ;Get the longest item if filter is empty iW := GetLongestItem(hlblList) + (bVScroll ? SM_CXVSCROLL : 0) + 4 iW := iW < iGUIWidth ? iGUIWidth : iW ;Make sure there's no hscroll PostMessage, 404,,,, ahk_id %hlblList% ;LB_SETHORIZONTALEXTENT ;Update the size of the controls ControlMove,,,, iW,, ahk_id %htxtSearch% ControlMove,,,, iW,, ahk_id %hlblList% } Else iW := iGUIWidth ;Check if hscroll will appear and adjust height to make sure %iGUIHeight% items are visible SendMessage, 403, 0, 0,, ahk_id %hlblList% ;LB_GETHORIZONTALEXTENT ControlMove,,,,, iGUIHeight * iGUIItemHeight + (ErrorLevel > iW ? SM_CYHSCROLL : 0) + 4, ahk_id %hlblList% ;Get window info WinGetPos, iX, iY,,, ahk_id %hEditor% ControlGetPos, sX, sY, sW, sH, %cSci%, ahk_id %hEditor% iX += sX + (Not bPosLeft ? sW - (iW + (iMargin * 2)) - (Sci_VScrollVisible(hSci) ? SM_CXVSCROLL : 0) - 2 : 2) iY += sY + 2 ;Make sure we should still show the GUI If (hEditor <> WinActive(sActiveWindow)) Return Gui, Show, w0 h0 WinSet, Transparent, 0, ahk_id %hGui% Gui, Show, AutoSize x%iX% y%iY% bShowing := True SetTimer, CheckFocus, 50 ;Put the focus on the textbox ControlFocus,, ahk_id %htxtSearch% ;Do the fade-in effect i := 0 While (i <= iTransparency) { WinSet, Transparent, %i%, ahk_id %hGui% i += 15 Sleep, 10 } If Not iTransparency Or (iTransparency = 255) ;Turn off if opaque WinSet, Transparent, OFF, ahk_id %hGui% Return GuiEscape: bShowing := False Gui, Cancel Gui, Show, Hide w0 h0 If bQuickMode ExitApp EmptyMem() Return CheckFocus: If Not WinActive("ahk_id " hGui) And Not WinActive("ahk_id " hEditor) { SetTimer, CheckFocus, Off Gosub, GuiEscape } Return ;Incremental searching txtSearch_Event: If bShowing { GuiControlGet, s,, txtSearch CreateList(s) } Return PreviousView: LineHistory(False) Return NextView: LineHistory(True) Return GotoDefinition: clickX := -1, clickY := -1, bCheckClick := True Goto, SummonGUI 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) ;Check if we found something If Not i { ;Try with labels StringTrimRight, clickText, clickText, 2 clickText .= ":" i := GetIndex(clickText, False) If Not i Return bIsFunc := False } Else bIsFunc := True ;Move the caret to the position before going to item. SendMessage, 2025, iPos, 0,, ahk_id %hSci% ;SCI_GOTOPOS bCheckClick := False } Else { ;Get selected item index. LB_GETCURSEL SendMessage, 0x188, 0, 0,, ahk_id %hlblList% ;Check for error. LB_ERR If (ErrorLevel = 0xFFFFFFFF) Return ;Get the associated item data i := GetListBoxItemData(hlblList, ErrorLevel) ;Retrieve function flag in highword and set i to the index in lowword bIsFunc := (i >> 16) i &= 0xFFFF } If bIsFunc { ;Check if it's external If sFuncs%i%_File LaunchFile(GetFile(sFuncs%i%_File, True), sFuncs%i%_Line) Else ShowLine(sFuncs%i%_Line) } Else { ;Check if it's external If sLabels%i%_File LaunchFile(GetFile(sLabels%i%_File, True), sLabels%i%_Line) Else ShowLine(sLabels%i%_Line) } Goto GuiEscape ;Done Return LaunchFile(sFilePath, iLine) { Global sEditorPath, sPathMatching, cSci ;It's external. Check if it's Notepad++, Notepad2, or SciTE If RegExMatch(sEditorPath, "i)scite\.exe$") t := """-open:%f""" Else If RegExMatch(sEditorPath, "i)notepad(\+\+|2)\.exe$") t := """%f""" ;Check if backslash is necessary (for SciTE) If RegExMatch(sEditorPath, "i)scite\.exe$") StringReplace u, sFilePath, \, \\, All Else u := sFilePath ;Set the path into the StringReplace t, t, @%f, %u% ;Shell it and get out Run "%sEditorPath%" %t% ;Wait for the new path (and make sure we're not somehow caught in the loop) While (t <> sFilePath) And (A_Index < 50) { WinGetActiveTitle, t RegExMatch(t, sPathMatching, t) Sleep, 50 } ;Check if detected the newly opened script If (t = sFilePath) { ;Get handle to Scintilla control ControlGet, hSci, Hwnd,, %cSci%, A ;Make the target line appear on top SendMessage, 2370, 0, 0,, ahk_id %hSci% SendMessage, 2024, iLine + ErrorLevel - 1, 0,, ahk_id %hSci% SendMessage, 2024, iLine - 1, 0,, ahk_id %hSci% } } GetIndex(sSearch, bIsFunc) { Global If bIsFunc { Loop %sFuncs0% If (sFuncs%A_Index% = sSearch) 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, bForward 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) { ;Up If Not WrapSel(True) { ControlSend,, {Up}, ahk_id %hlblList% Return True } } Else If (wParam = 40) { ;Down If Not WrapSel(False) { ControlSend,, {Down}, ahk_id %hlblList% Return True } } Else If (wParam = 33) { ;Page Up ControlSend,, {PgUp}, ahk_id %hlblList% Return True } Else If (wParam = 34) { ;Page Down ControlSend,, {PgDn}, ahk_id %hlblList% Return True } Else If (wParam = 35) And GetKeyState("Ctrl", "P") { ;Ctrl+End ControlSend,, {End}, ahk_id %hlblList% Return True } Else If (wParam = 36) And GetKeyState("Ctrl", "P") { ;Ctrl+Home ControlSend,, {Home}, ahk_id %hlblList% Return True } } 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 ;Check if the listbox is even populated SendMessage, 395, 0, 0,, ahk_id %hlblList% If Not ErrorLevel Return ;Listbox is empty ;Sign it if needed wParam := wParam > 0x7FFFFFFF ? -(~wParam) - 1 : wParam ;Get notches turned wParam := Round((wParam >> 16) / 120) bForward := wParam > 0 Loop % Abs(wParam) If Not WrapSel(bForward) ControlSend,, % bForward ? "{Up}" : "{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 0 ;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") And WinActive(sActiveWindow) { iWheelTurns := HID_GetInputInfo(lParam, 22 | 0x1100) If (iWheelTurns <> -1) { ;Check for error iWheelTurns := Round(iWheelTurns / 120) bForward := iWheelTurns > 0 Loop % Abs(iWheelTurns) LineHistory(bForward) } ;Done here Return 0 } ;To save time for most cases this branch will be executed If Not (bMDown Or bMUp) Return 0 If bMDown And bShowing { SetTimer, GuiEscape, -%iCancelWait% bIgnoreMUp := True } Else If bMUp And bUseMButton And (Not bIgnoreMUp Or bShowing) And WinActive(sActiveWindow) { ;Cancel timer SetTimer, GuiEscape, Off ;Get mouse data MouseGetPos, clickX, clickY,, sControl If InStr(sControl, sScintillaClass) { ControlGet, hSci, Hwnd,, %sControl%, A ControlGetPos, cX, cY,,, %sControl%, A clickX -= cX, clickY -= cY bCheckClick := True } Else bCheckClick := False Critical, Off Gosub, SummonGUI } If bMUp And bIgnoreMUp bIgnoreMUp := False Return 0 } } 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 } ;This sub deletes all cache files on exit DeleteCache: FileDelete, %A_Temp%\*.TGcache ExitApp Return ;-------------------\ ;Scanning Functions | ;-------------------/ ;Retrieves labels and functions of the script AnalyseScript: ;Reset counters sLabels0 := 0 sFuncs0 := 0 sPaths0 := 0 sScanFile0 := 0 ;Get full text sScript := Sci_GetText(hSci) If bDirectives 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 StringLeft, sScriptDir, sScriptPath, InStr(sScriptPath, "\", False, 0) - 1 ;Set the default include path to the script directory sWorkDir := sScriptDir SetWorkingDir, %sWorkDir% ;Loop through each #Include file i := 1 Loop { ;Get the next include directive i := RegExMatch(sScript, "im)(*ANYCRLF)^[[:blank:]]*#Include(Again)?[[:blank:]]*(,|[[:blank:]]+)[[:blank:]]*(\*i[[:blank:]]+)?\K.*?$", 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 sWorkDir := t SetWorkingDir, %sWorkDir% } Else If s { ;It's a file ScanScriptFile(t, iIncludeMode & 0x01000000, False, True) SetWorkingDir, %sWorkDir% ;Put the working dir's path back to here } ;Start at next line i := InStr(sScript, "@n", False, i) + 1 If (i = 1) ;Check if that was the last line Break } ;Put working dir back to here sWorkDir := sScriptDir SetWorkingDir, %sWorkDir% ;Loop through Scan directives, if any Loop %sScanFile0% { t := sScanFile%A_Index% ;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 sWorkDir := t SetWorkingDir, %sWorkDir% } Else If s { ;It's a file ScanScriptFile(t, iIncludeMode & 0x01000000, False, True) SetWorkingDir, %sWorkDir% ;Put the working dir's path back to here } } } ;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, False) ;With bFuncsOnly flag ;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, False) ;With bFuncsOnly flag } Return ScanScriptFile(sPath, bRecurse = False, bFuncsOnly = False, bIsInclude = False) { Local sFile, s, i, sInclude, sScriptDir, iCacheIndex, iCacheType, bNewCache, sWorkDir 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 sPaths0 += 1 sPaths%sPaths0% := sPath sPaths%sPaths0%_Inc := bIsInclude If bWideView sPaths%sPaths0%_Len := StrLen(GetFile(sPaths0)) + 1 ;Needed for alignment ;Get the script's dir and set the default include path to it StringLeft sScriptDir, sPath, InStr(sPath, "\", False, 0) - 1 sWorkDir := sScriptDir SetWorkingDir, %sWorkDir% If bCacheFiles { ;iCacheType := 0x1111 - 0x1000 = file changed, 0x100 = hotkeys, 0x10 = labels, 0x1 = funcs iCacheIndex := IsCached(sPath, iCacheType) ;Check if the file has simply changed If iCacheIndex And (iCacheType & 0x1000) bNewCache := True ;To force a rescan Else bNewCache := Not iCacheIndex ;Expand cache array if it is not cached If Not iCacheIndex { sCache0 += 1 iCacheIndex := sCache0 sCache%iCacheIndex%_Path := sPath } If bNewCache { ;Check if we need to prep for include scanning ;Load file and filter comments if necessary FileRead, sFile, %sPath% ApplyCommentFilterSetting(sFile) } ;Scan for stuff If (iIncludeMode & 0x00000100) { ;Check if it was cached (and if it was retrieved successfully) If (iCacheType & 0x001) And GetCachedScriptFunctions(iCacheIndex) Or bNewCache { i := sFuncs0 GetScriptFunctions(sFile, True) If (i <> sFuncs0) CacheFile("Functions", i, sFuncs0, iCacheIndex) } } ;Check if we shold also scan for labels and hotkeys If Not bFuncsOnly { If (iIncludeMode & 0x00001000) { ;Check if it was cached If (iCacheType & 0x010) And GetCachedScriptLabels(iCacheIndex) Or bNewCache { i := sLabels0 GetScriptLabels(sFile, True) If (i <> sLabels0) CacheFile("Labels", i, sLabels0, iCacheIndex) } } If (iIncludeMode & 0x00010000) { ;Check if it was cached If (iCacheType & 0x100) And GetCachedScriptHotkeys(iCacheIndex) Or bNewCache { i := sLabels0 GetScriptHotkeys(sFile, True) If (i <> sLabels0) CacheFile("Hotkeys", i, sLabels0, iCacheIndex) } } } ;Calculate CRC and add to cache array sCache%iCacheIndex%_CRC := GetFileCRC32(sPath) } Else { ;We don't cache ;Load file and comment if requested FileRead, sFile, %sPath% ApplyCommentFilterSetting(sFile) 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 { ;Check if include files are cached If bCacheFiles And Not bNewCache { Loop, Parse, sCache%iCacheIndex%_IncFiles, @n ;Get the list of cached files If FileExist(A_LoopField) ScanScriptFile(A_LoopField, bRecurse, False, bIsInclude) } Else { ;We'll have to manually look for the include files ;Loop through each #Include file i := 1 Loop { ;Get the next include directive i := RegExMatch(sFile, "im)(*ANYCRLF)^[[:blank:]]*#Include(Again)?[[:blank:]]*(,|[[:blank:]]+)[[:blank:]]*(\*i[[:blank:]]+)?\K.*?$", 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 sWorkDir := sInclude SetWorkingDir, %sWorkDir% } Else If s { ;It's a file ;Add the file to the cache array If bCacheFiles sCache%iCacheIndex%_IncFiles .= AbsolutePath(sInclude) "@n" ScanScriptFile(sInclude, bRecurse, False, bIsInclude) SetWorkingDir, %sWorkDir% ;Put the working dir's path back to here } ;Start at next line i := InStr(sFile, "@n", False, i) + 1 If (i = 1) ;Check if that was the last line Break } } } } 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 } IsCached(sPath, ByRef iCacheType) { Local i ;Default value iCacheType := 0 ;Look for path in cache array Loop %sCache0% { If (sCache%A_Index%_Path = sPath) { i := A_Index Break } } ;Check if we found the path If Not i Return 0 Else { ;Check first if it's the same file If (GetFileCRC32(sPath) = sCache%i%_CRC) iCacheType := (sCache%i%_Functions ? 0x001 : 0) | (sCache%i%_Labels ? 0x010 : 0) | (sCache%i%_Hotkeys ? 0x100 : 0) Else iCacheType := 0x1000 Return i } } CacheFile(sType, iStart, iStop, iCacheIndex) { Local i, iRand, sAppend While Not iRand { ;Loop until we have a unique number Random, iRand, 1 ;Choose random number Loop %sCache0% ;Make sure it's not already taken If (sCache%A_Index%_Functions = iRand) Or (sCache%A_Index%_Labels = iRand) Or (sCache%A_Index%_Hotkeys = iRand) { iRand := 0 ;Already taken. Cancel it. Break } } ;Delete file in case it exists FileDelete, %A_Temp%\%iRand%.TGcache ;Prep var for file append If (sType = "Functions") { Loop % (iStop - iStart) { i := iStart + A_Index sAppend .= sFuncs%i% "@n" sFuncs%i%_Line "@n" } } Else { Loop % (iStop - iStart) { i := iStart + A_Index sAppend .= sLabels%i% "@n" sLabels%i%_Line "@n" } } ;Write var to file FileAppend, %sAppend%, %A_Temp%\%iRand%.TGcache ;Add cache reference to cache array sCache%iCacheIndex%_%sType% := iRand } ;This sub analyses the script and add the labels in it to the array GetScriptLabels(ByRef s, bExternal = False) { Local i, n, t, u := GetScriptEscapeChar(s) ;Reset counter i := 1 Loop { ;Get next label. All valid labels are detected (invalid characters are commas, spaces, and the escape char). Labels ending in two colons not supported i := RegExMatch(s, "m)(*ANYCRLF)^[[:blank:]]*\K[a-zA-Z0-9\Q@@#$_[]?~`!%^&*+-()={}|\:;""'<>./\E]*[a-zA-Z0-9\Q@@#$_[]?~`!%^&*+-()={}|\;""'<>./\E]:[[: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) ;Make sure it doesn't contain an escape character If Not InStr(t, u) { sLabels0 += 1 ;Increase counter If bExternal { sLabels%sLabels0%_File := sPaths0 sLabels%sLabels0%_Line := LineFromPosEx(s, i) If bWideView ;Needed to align filenames later on If bAlignFilenames sLabels%sLabels0%_Len := StrLen(t) Else sLabels%sLabels0%_Len := sPaths%sPaths0%_Len + StrLen(t) } Else { sLabels%sLabels0%_File := 0 sLabels%sLabels0%_Line := LineFromPos(i) sLabels%sLabels0%_Len := 0 } 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 } } GetCachedScriptLabels(iCacheIndex) { Local s, bLineType ;Formulate path s := A_Temp "\" sCache%iCacheIndex%_Labels ".TGcache" ;Check if the file exists If Not FileExist(s) Return True ;Error Else { Loop, Read, %s% { If bLineType sLabels%sLabels0%_Line := A_LoopReadLine Else { sLabels0 += 1 sLabels%sLabels0% := A_LoopReadLine sLabels%sLabels0%_File := sPaths0 If bWideView If bAlignFilenames sLabels%sLabels0%_Len := StrLen(sLabels%sLabels0%) Else sLabels%sLabels0%_Len := StrLen(sLabels%sLabels0%) + sPaths%sPaths0%_Len } bLineType := Not bLineType } } } ;This sub analyses the script and add the hotkeys in it to the array (uses the same array as labels) GetScriptHotkeys(ByRef s, bExternal = False) { Local i, n, t, u, v i := 1 Loop { ;Get next hotkey i := RegExMatch(s, "m)(*ANYCRLF)^[[:blank:]]*\K([a-zA-Z0-9\Q%(){}|:""?#_!@@^+&<>*~$`-=\[]';/\.,\E]|( Up)|( & ))+(?=::)", t, i) ;Make sure we found something If Not i Break ;Check if it's a valid hotkey If Not IsValidHotkey(t) { ;It failed validity test. Check if it's the exception [escapechar][commentflag] ;Get the script's escape character and append comment flag u := u ? u : GetScriptEscapeChar(s) v := v ? v : GetScriptCommentFlag(s) StringReplace, t, t, %u%%v%, %v%, UseErrorLevel ;Check if it's worth rechecking validity If (Not ErrorLevel) Or (ErrorLevel And Not IsValidHotkey(t)) Goto, NextIteration } ;Append the semi-colons t .= "::" ;Expand the array and fill in the elements sLabels0 += 1 ;Increase counter If bExternal { sLabels%sLabels0%_File := sPaths0 sLabels%sLabels0%_Line := LineFromPosEx(s, i) If bWideView ;Needed to align filenames later on If bAlignFilenames sLabels%sLabels0%_Len := StrLen(t) Else sLabels%sLabels0%_Len := sPaths%sPaths0%_Len + StrLen(t) } Else { sLabels%sLabels0%_File := 0 sLabels%sLabels0%_Line := LineFromPos(i) sLabels%sLabels0%_Len := 0 } sLabels%sLabels0% := t ;Add to array NextIteration: ;Set i to the beginning of the next line i := InStr(s, "@n", False, i) + 1 If (i = 1) Break } } GetCachedScriptHotkeys(iCacheIndex) { Local s, bLineType ;Formulate path s := A_Temp "\" sCache%iCacheIndex%_Hotkeys ".TGcache" ;Check if the file exists If Not FileExist(s) Return True ;Error Else { Loop, Read, %s% { If bLineType sLabels%sLabels0%_Line := A_LoopReadLine Else { sLabels0 += 1 sLabels%sLabels0% := A_LoopReadLine sLabels%sLabels0%_File := sPaths0 If bWideView If bAlignFilenames sLabels%sLabels0%_Len := StrLen(sLabels%sLabels0%) Else sLabels%sLabels0%_Len := StrLen(sLabels%sLabels0%) + sPaths%sPaths0%_Len } bLineType := Not bLineType } } } ;This sub checks the validity of a hotkey IsValidHotkey(s) { Critical Hotkey, IfWinActive, Title ;Make sure it'll be a variant and not override a current shortcut Hotkey, % s, SummonGUI, UseErrorLevel Off ;Using SummonGUI only to test i := ErrorLevel ;Keep ErrorLevel value (because the next command will change it) Hotkey, IfWinActive ;Turn off context sensitivity Return (i <> 2) } ;This sub analyses the script and add the functions in it to the array GetScriptFunctions(ByRef s, bExternal = False) { Local i, n, t ;Loop through the functions i := 1 Loop { ;Get the next function i := RegExMatch(s, "m)(*ANYCRLF)^[[: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 bExternal { sFuncs%sFuncs0%_File := sPaths0 sFuncs%sFuncs0%_Line := LineFromPosEx(s, i) If bWideView ;Needed to align filenames later on If bAlignFilenames sFuncs%sFuncs0%_Len := StrLen(t) Else sFuncs%sFuncs0%_Len := sPaths%sPaths0%_Len + StrLen(t) } Else { sFuncs%sFuncs0%_File := 0 sFuncs%sFuncs0%_Line := LineFromPos(i) sFuncs%sFuncs0%_Len := 0 } sFuncs%sFuncs0% := t } ;Get the next function i := InStr(s, "@n", False, i) + 1 If (i = 1) Break } } GetCachedScriptFunctions(iCacheIndex) { Local s, bLineType ;Formulate path s := A_Temp "\" sCache%iCacheIndex%_Functions ".TGcache" ;Check if the file exists If Not FileExist(s) Return True ;Error Else { Loop, Read, %s% { If bLineType sFuncs%sFuncs0%_Line := A_LoopReadLine Else { sFuncs0 += 1 sFuncs%sFuncs0% := A_LoopReadLine sFuncs%sFuncs0%_File := sPaths0 If bWideView If bAlignFilenames sFuncs%sFuncs0%_Len := StrLen(sFuncs%sFuncs0%) Else sFuncs%sFuncs0%_Len := StrLen(sFuncs%sFuncs0%) + sPaths%sPaths0%_Len } bLineType := Not bLineType } } } GetScriptDirectives(ByRef s) { Local i, sCommentFlag, val ;Get the comment flag used sCommentFlag := GetScriptCommentFlag(s) ;Check for TillaGoto.bFilterComments bFilterComments := RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.bFilterComments[[:blank:]]*=[[:blank:]]*\K.*?$", val) ? val : bFilterCommentsOrig ;Check for TillaGoto.iIncludeMode iIncludeMode := RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.iIncludeMode[[:blank:]]*=[[:blank:]]*\K.*?$", val) ? val : iIncludeModeOrig ;Check for Include directives i := 1 Loop { i := RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.ScanFile[[:blank:]]*=[[:blank:]]*\K.*?$", val, i) If i { ;Add to array sScanFile0 += 1 sScanFile%sScanFile0% := val ;Move to next line i += StrLen(val) + 1 } Else Break } } ApplyCommentFilterSetting(ByRef sFile) { Global iIncludeMode ;Remove comments if necessary bOverride := GetScriptCommentOverride(sFile) If (bOverride <> -1) bOverride ? FilterComments(sFile, True) Else If (iIncludeMode & 0x00100000) FilterComments(sFile, True) } GetScriptCommentOverride(ByRef s) { ;Get the comment flag used sCommentFlag := GetScriptCommentFlag(s) ;Check for TillaGoto.bFilterComments Return RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*\Q" sCommentFlag "\E[[:blank:]]*TillaGoto\.bFilterComments[[:blank:]]*=[[:blank:]]*\K.*?$", val) ? val : -1 } FilterComments(ByRef s, bRespectLines = False) { Local i, j, len, sCommentFlag, blank, l1, l2 i := 1 Loop { ;Get next block start i := RegExMatch(s, "m)(*ANYCRLF)^[[: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)(*ANYCRLF)^[[: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) ;Put in the same amount of line feed characters Loop % (l2 - l1) NumPut(10, blank, A_Index - 1, "UChar") } ;Blank out DllCall("RtlMoveMemory", UInt, &s + i - 1, UInt, &blank, UInt, len) If Not j Break Else i += len } ;Get the comment flag used sCommentFlag := GetScriptCommentFlag(s) ;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, "imP)[[:blank:]\n]\K\Q" sCommentFlag "\E.*?$", 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 } } GetScriptCommentFlag(ByRef s) { i := RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*#CommentFlag[[:blank:]]*(,|[[:blank:]]+)[[:blank:]]*\K.*?[[:blank:]]*$", sCommentFlag) Return i ? sCommentFlag : ";" } GetScriptEscapeChar(ByRef s) { i := RegExMatch(s, "im)(*ANYCRLF)^[[:blank:]]*#EscapeChar[[:blank:]]*(,|[[:blank:]]+)[[:blank:]]*\K.*?[[:blank:]]*$", sEscapeChar) Return i ? sEscapeChar : "`" } ;------------------\ ;ListBox Functions | ;------------------/ AppendFilename() { Local n ;Check if we need to do any kind of aligning If bWideView { ;Set the minimum length to be the number of characters the listbox could take without expanding If Not bAlignFilenames n := GetMaxCharacters(hlblList, iGUIWidth) - 2 Else { Loop %sPaths0% If (sPaths%A_Index%_Len > n) n := sPaths%A_Index%_Len n := GetMaxCharacters(hlblList, iGUIWidth) - 2 - n } ;We need to find the longest item Loop %sLabels0% If (sLabels%A_Index%_Len > n) n := sLabels%A_Index%_Len Loop %sFuncs0% If (sFuncs%A_Index%_Len > n) n := sFuncs%A_Index%_Len ;Now append so that they're all aligned Loop %sLabels0% sLabels%A_Index%_List := sLabels%A_Index% (sLabels%A_Index%_File ? (GenSpaces(n - sLabels%A_Index%_Len + 1) AppendSymbol(sLabels%A_Index%_File) GetFile(sLabels%A_Index%_File)) : "") Loop %sFuncs0% sFuncs%A_Index%_List := sFuncs%A_Index% (sFuncs%A_Index%_File ? (GenSpaces(n - sFuncs%A_Index%_Len + 1) AppendSymbol(sFuncs%A_Index%_File) GetFile(sFuncs%A_Index%_File)) : "") } Else { ;Simple append Loop %sLabels0% sLabels%A_Index%_List := sLabels%A_Index% (sLabels%A_Index%_File ? (" " AppendSymbol(sLabels%A_Index%_File) GetFile(sLabels%A_Index%_File)) : "") Loop %sFuncs0% sFuncs%A_Index%_List := sFuncs%A_Index% (sFuncs%A_Index%_File ? (" " AppendSymbol(sFuncs%A_Index%_File) GetFile(sFuncs%A_Index%_File)) : "") } } GenSpaces(n) { VarSetCapacity(s, n, 32) NumPut(0, s, n, "UChar") VarSetCapacity(s, -1) Return s } AppendSymbol(i) { ;Use \ for include files and | for library functions Return sPaths%i%_Inc ? "\" : "|" } CreateList(filter = "") { Global sLabels0, sFuncs0, bMatchEverywhere, hlblList, bShowing, iIncludeMode Static sLastfilter := "@n" ;Initialize on an impossible filter ;Autotrim filter = %filter% ;Check if the filter is different If (filter = sLastfilter) And bShowing Return sLastfilter := filter ;Disable redraw GuiControl, -Redraw, lblList ;Clear GuiControl,, lblList,| ;Check if we need to take from the _List elements ;Although it looks extremely redundant and inefficient, it is much faster than the alternative If (iIncludeMode & 0x10000000) { If (filter = "") { ;Split cases for speed Loop %sLabels0% AddListBoxItem(hlblList, sLabels%A_Index%_List, A_Index) Loop %sFuncs0% AddListBoxItem(hlblList, sFuncs%A_Index%_List, A_Index + (0xFFFF << 16)) ;0xFFFF in highword means function } Else { ;Split cases for speed If bMatchEverywhere { ;Parse words StringSplit, words, filter, %A_Space% ;Check for negative conditions (!) Loop %words0% words%A_Index%_Not := NotWord(words%A_Index%) ;Split cases for speed If (words0 > 1) { Loop %sLabels0% { bMatch := True i := A_Index Loop %words0% { bMatch := bMatch And ((words%A_Index%_Not And Not InStr(sLabels%i%_List, words%A_Index%)) Or (Not words%A_Index%_Not And InStr(sLabels%i%_List, words%A_Index%))) If Not bMatch Break } If bMatch AddListBoxItem(hlblList, sLabels%A_Index%_List, A_Index) } Loop %sFuncs0% { bMatch := True i := A_Index Loop %words0% { bMatch := bMatch And ((words%A_Index%_Not And Not InStr(sFuncs%i%_List, words%A_Index%)) Or (Not words%A_Index%_Not And InStr(sFuncs%i%_List, words%A_Index%))) If Not bMatch Break } If bMatch AddListBoxItem(hlblList, sFuncs%A_Index%_List, A_Index + (0xFFFF << 16)) } ;It's one word } Else { ;Check if it's a negative condition (!) bNotWord := NotWord(filter) Loop %sLabels0% If (bNotWord And Not InStr(sLabels%A_Index%_List, filter)) Or (Not bNotWord And InStr(sLabels%A_Index%_List, filter)) AddListBoxItem(hlblList, sLabels%A_Index%_List, A_Index) Loop %sFuncs0% If (bNotWord And Not InStr(sFuncs%A_Index%_List, filter)) Or (Not bNotWord And InStr(sFuncs%A_Index%_List, filter)) AddListBoxItem(hlblList, sFuncs%A_Index%_List, A_Index + (0xFFFF << 16)) } } Else { Loop %sLabels0% If (InStr(sLabels%A_Index%_List, filter) = 1) AddListBoxItem(hlblList, sLabels%A_Index%_List, A_Index) Loop %sFuncs0% If (InStr(sFuncs%A_Index%_List, filter) = 1) AddListBoxItem(hlblList, sFuncs%A_Index%_List, A_Index + (0xFFFF << 16)) } } } Else { If (filter = "") { ;Split cases for speed Loop %sLabels0% AddListBoxItem(hlblList, sLabels%A_Index%, A_Index) Loop %sFuncs0% AddListBoxItem(hlblList, sFuncs%A_Index%, A_Index + (0xFFFF << 16)) } Else { ;Split cases for speed If bMatchEverywhere { ;Parse words StringSplit, words, filter, %A_Space% ;Check for negative conditions (!) Loop %words0% words%A_Index%_Not := NotWord(words%A_Index%) ;Split cases for speed If (words0 > 1) { Loop %sLabels0% { bMatch := True i := A_Index Loop %words0% { bMatch := bMatch And ((words%A_Index%_Not And Not InStr(sLabels%i%, words%A_Index%)) Or (Not words%A_Index%_Not And InStr(sLabels%i%, words%A_Index%))) If Not bMatch Break } If bMatch AddListBoxItem(hlblList, sLabels%A_Index%, A_Index) } Loop %sFuncs0% { bMatch := True i := A_Index Loop %words0% { bMatch := bMatch And ((words%A_Index%_Not And Not InStr(sFuncs%i%, words%A_Index%)) Or (Not words%A_Index%_Not And InStr(sFuncs%i%, words%A_Index%))) If Not bMatch Break } If bMatch AddListBoxItem(hlblList, sFuncs%A_Index%, A_Index + (0xFFFF << 16)) } ;It's one word } Else { ;Check if it's a negative condition (!) bNotWord := NotWord(filter) Loop %sLabels0% If (bNotWord And Not InStr(sLabels%A_Index%, filter)) Or (Not bNotWord And InStr(sLabels%A_Index%, filter)) AddListBoxItem(hlblList, sLabels%A_Index%, A_Index) Loop %sFuncs0% If (bNotWord And Not InStr(sFuncs%A_Index%, filter)) Or (Not bNotWord And InStr(sFuncs%A_Index%, filter)) AddListBoxItem(hlblList, sFuncs%A_Index%, A_Index + (0xFFFF << 16)) } } Else { Loop %sLabels0% If (InStr(sLabels%A_Index%, filter) = 1) AddListBoxItem(hlblList, sLabels%A_Index%, A_Index) Loop %sFuncs0% If (InStr(sFuncs%A_Index%, filter) = 1) AddListBoxItem(hlblList, sFuncs%A_Index%, A_Index + (0xFFFF << 16)) } } } ;Add hscrollbar if necessary ListBoxAdjustHSB(hlblList) ;Select the first item. LB_SETCURSEL SendMessage 390, 0, 0,, ahk_id %hlblList% ;Redraw GuiControl, +Redraw, lblList } NotWord(ByRef word) { If (SubStr(word, 1, 1) = "!") { StringTrimLeft, word, word, 1 Return StrLen(word) ;Return False if it's nothing anymore } Else Return False } 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% } AddListBoxItem(hLB, sItem, iItemData = 0) { SendMessage, 0x0180,, &sItem,, ahk_id %hLB% ;LB_ADDSTRING If iItemData SendMessage, 0x019A, ErrorLevel, iItemData,, ahk_id %hLB% ;LB_SETITEMDATA } GetListBoxItem(hLB, i) { ;Get length of item. LB_GETTEXTLEN SendMessage 394, %i%, 0,, ahk_id %hLB% ;Check for error If (ErrorLevel = 0xFFFFFFFF) Return "" ;Prepare variable VarSetCapacity(sText, ErrorLevel, 0) ;Retrieve item. LB_GETTEXT SendMessage 393, %i%, &sText,, ahk_id %hLB% ;Check for error If (ErrorLevel = 0xFFFFFFFF) Return "" ;Done Return sText } GetListBoxItemData(hLB, i) { SendMessage, 0x0199, i,,, ahk_id %hLB% ;LB_GETITEMDATA Return ErrorLevel } GetLongestItem(hLB) { ;We need the listbox to get the font used Global sLabels0, sFuncs0, iIncludeMode ;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) ;Now, we need to loop through each label/hotkey/function If (iIncludeMode & 0x10000000) { ;Check if we're taking from the _List elements. Split for speed Loop %sLabels0% { ;For each string, the value of the extent to be used is calculated as follows: DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", sLabels%A_Index%_List, "int", StrLen(sLabels%A_Index%_List), "int64P", nSize) dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth ;Keep if it's the highest to date If (dwExtent > dwMaxExtent) dwMaxExtent := dwExtent } Loop %sFuncs0% { ;For each string, the value of the extent to be used is calculated as follows: DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", sFuncs%A_Index%_List, "int", StrLen(sFuncs%A_Index%_List), "int64P", nSize) dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth ;Keep if it's the highest to date If (dwExtent > dwMaxExtent) dwMaxExtent := dwExtent } } Else { Loop %sLabels0% { ;For each string, the value of the extent to be used is calculated as follows: DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", sLabels%A_Index%, "int", StrLen(sLabels%A_Index%), "int64P", nSize) dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth ;Keep if it's the highest to date If (dwExtent > dwMaxExtent) dwMaxExtent := dwExtent } Loop %sFuncs0% { ;For each string, the value of the extent to be used is calculated as follows: DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", sFuncs%A_Index%, "int", StrLen(sFuncs%A_Index%), "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) ;Return the longest one found Return dwMaxExtent } ;Used to retrieve the number of characters that can fit in a given width GetMaxCharacters(hLB, iWidth) { ;We need the listbox to get the font used ;Declare variables (for clarity's sake) 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 DllCall("GetTextMetrics", "Uint", hDCListBox, "Uint", &lptm) tmAveCharWidth := NumGet(lptm, 20) ;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) Return Floor(iWidth / tmAveCharWidth) } ;--------------------\ ;Scintilla Functions | ;--------------------/ Sci_GetText(hSci) { ;Retrieve text length. SCI_GETLENGTH SendMessage 2006, 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. SCI_GETTEXT SendMessage 2182, 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) { ;Get length. SCI_GETSELTEXT SendMessage 2161, 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. SCI_GETSELTEXT SendMessage, 2161, 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) { ;Retrieve line length. SCI_LINELENGTH SendMessage 2350, 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. SCI_GETLINE SendMessage 2153, 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 } Sci_VScrollVisible(hSci) { ;Get the number of lines visible. SCI_LINESONSCREEN SendMessage, 2370, 0, 0,, ahk_id %hSci% i := ErrorLevel ;Get the number of lines in the document. SCI_GETLINECOUNT SendMessage, 2154, 0, 0,, ahk_id %hSci% ;Check if there are more lines than what can be shown Return (ErrorLevel > i) } LineFromPos(pos) { Global SendMessage, 2166, pos - 1, 0,, ahk_id %hSci% ;SCI_LINEFROMPOSITION 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, ByRef iPos) { Local line, linetext, i, j ;Check if we need to look for position If (x = -1) And (y = -1) SendMessage 2008, 0, 0,, ahk_id %hSci% ;SCI_GETCURRENTPOS Else SendMessage, 2023, x, y,, ahk_id %hSci% ;SCI_POSITIONFROMPOINTCLOSE iPos := ErrorLevel, j := iPos ;Check for error If (iPos = 0xFFFFFFFF) Return False Else { ;SCI_LINEFROMPOSITION SendMessage, 2166, iPos, 0,, ahk_id %hSci% line := ErrorLevel ;SCI_POSITIONFROMLINE SendMessage, 2167, line, 0,, ahk_id %hSci% j -= ErrorLevel ;Get line text linetext := Sci_GetLineText(hSci, line) ;Trim after the first illegal character i := RegExMatch(linetext, "[^a-zA-Z0-9#_@\$\?\[\]]", "", j + 1) If i StringLeft, linetext, linetext, i - 1 ;Trim before the first illegal character i := RegExMatch(StringReverse(linetext), "[^a-zA-Z0-9#_@\$\?\[\]]", "", StrLen(linetext) - j + 1) If i StringTrimLeft, linetext, linetext, StrLen(linetext) - i + 1 ;Return found text Return linetext } Return False } StringReverse(s) { DllCall("msvcrt\_strrev", "UInt",&s, "CDecl") return s } 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 Local t, sScriptPath Global sPathMatching, hEditor ;Check if we even have an editor If Not hEditor { hEditor := WinActive(sActiveWindow) ;Try to get the handle If Not hEditor Return } ;Get the filename WinGetTitle, t, ahk_id %hEditor% RegExMatch(t, sPathMatching, sScriptPath) ;Match file iCurFile := 0 Loop %iFile0% { If (iFile%A_Index% = sScriptPath) { iCurFile := A_Index iCurLine := iFile%A_Index%_Cur Break } } ;If we're working on a new file, expand array If Not iCurFile { iFile0 += 1 iCurLine := 1 iCurFile := iFile0 iFile%iCurFile%_Count := 0 iFile%iCurFile% := sScriptPath } ;Check if we just need to record If (iRecordMode = 1) ;Record current line LH_GetCurLine(iLines%iCurLine%_%iCurFile%) Else If (iRecordMode = 2) { ;Record to the next line iCurLine += 1 LH_GetCurLine(iLines%iCurLine%_%iCurFile%) ;Set as the new limit iFile%iCurFile%_Count := iCurLine } Else If bForward { ;Forward ;Check if it is possible If (iCurLine < iFile%iCurFile%_Count) { ;Record the line we're on now LH_GetCurLine(iLines%iCurLine%_%iCurFile%) ;Show the next line iCurLine += 1 LH_SetCurLine(iLines%iCurLine%_%iCurFile%) } } Else { ;Backward ;Check if it is possible If (iCurLine > 1) { ;Record the line we're on now LH_GetCurLine(iLines%iCurLine%_%iCurFile%) ;Show the previous line iCurLine -= 1 LH_SetCurLine(iLines%iCurLine%_%iCurFile%) } } iFile%iCurFile%_Cur := 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) { RIDEV_REMOVE := 0x00000001, RIDEV_EXCLUDE := 0x00000010 ;Prep var VarSetCapacity(uDev, 12) ;Check if hwnd needs to be null bNull := (Flags & RIDEV_REMOVE) Or (Flags & RIDEV_EXCLUDE) 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 { ErrorLevel := "RegisterRawInputDevices call failed." . "@nReturn value: " r . "@nErrorLevel: " ErrorLevel . "@nLine: " A_LineNumber . "@nLast Error: " A_LastError Return True } Return False } HID_GetInputInfo(InputHandle, Flag) { Static uRawInput, iLastHandle := 0 RID_INPUT := 0x10000003 ;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 r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", RID_INPUT, "UInt", 0, "UInt*", iSize, "UInt", 16) If (r = -1) Or ErrorLevel { ErrorLevel := "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 r := DllCall("GetRawInputData", "UInt", InputHandle, "UInt", RID_INPUT, "UInt", &uRawInput, "UInt*", iSize, "UInt", 16) If (r = -1) Or ErrorLevel { ErrorLevel := "GetRawInputData call failed." . "@nReturn value: " r . "@nErrorLevel: " ErrorLevel . "@nLine: " A_LineNumber . "@nLast Error: " A_LastError Return -1 } Else If (r <> iSize) { ErrorLevel := "GetRawInputData call failed." . "@nReturn value: " r . "@nErrorLevel: " ErrorLevel . "@nLine: " A_LineNumber . "@nLast Error: " A_LastError 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")) } } ;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) } ;Based on Lazslo's CRC32() and MCode() functions ;http://www.autohotkey.com/forum/viewtopic.php?p=158999#158999 GetFileCRC32(path = False) { Static CRC32, CRC32_Init, CRC32LookupTable ;Check if we're initiating If Not path Or Not CRC32 { ;Prep MCode for CRC32_Init() hex := "33c06a088bc85af6c101740ad1e981f12083b8edeb02d1e94a75ec8b542404890c82403d0001000072d8c3" VarSetCapacity(CRC32_Init, StrLen(hex) // 2) Loop % StrLen(hex) // 2 NumPut("0x" . SubStr(hex, 2 * A_Index - 1, 2), CRC32_Init, A_Index - 1, "Char") ;Prep MCode for CRC32() hex := "558bec33c039450c7627568b4d080fb60c08334d108b55108b751481e1ff000000c1ea0833148e403b450c89551072db5e8b4510f7d05dc3" VarSetCapacity(CRC32, StrLen(hex) // 2) Loop % StrLen(hex) // 2 NumPut("0x" . SubStr(hex, 2 * A_Index - 1, 2), CRC32, A_Index - 1, "Char") VarSetCapacity(CRC32LookupTable, 256 * 4) DllCall(&CRC32_Init, "uint",&CRC32LookupTable, "cdecl") } If path { FileGetSize, Bytes, %path% FileRead, Buffer, %path% Return DllCall(&CRC32, "uint",&Buffer, "uint",Bytes, "int",-1, "uint",&CRC32LookupTable, "cdecl uint") } }