Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

TillaGoto - Go to functions, labels & hks in your script


  • Please log in to reply
135 replies to this topic
philou
  • Members
  • 68 posts
  • Last active: Jan 04 2011 01:12 PM
  • Joined: 26 Jul 2006
TheGood:

IntelliSense never worked right for me, so I'd like to suggest a means to get the command syntax for a selected or clicked-on command.
I could provide you with a sexy GDI+ way to display the syntax and/or info,
as well as means to get the info (maybe querying <!-- m -->http://www.autohotke.../docs/commands/<!-- m -->%Command%.htm, and RegEx'ing the CommandySyntax part.)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

TheGood:

IntelliSense never worked right for me, so I'd like to suggest a means to get the command syntax for a selected or clicked-on command.
I could provide you with a sexy GDI+ way to display the syntax and/or info,
as well as means to get the info (maybe querying <!-- m -->http://www.autohotke.../docs/commands/<!-- m -->%Command%.htm, and RegEx'ing the CommandySyntax part.)

I've actually been working on a very complete IntelliSense system based on the Scintilla APIs. I got very far into it (+1800 lines), but now feel like I bit off more than I can chew. That is partly why I started TillaGoto, in order to get me into the feel of working with the Scintilla control and parsing scripts.

What you are proposing seems like a simple project. Therefore, I might be able to do it properly. Tell me if this is what you mean:
- There is no automatic IntelliSense (that is, as the user types)
- Instead, the user must request support by clicking on a command/function
- If the command/function clicked on is recognized, a small little GUI is shown with all the parameters and possibly info.

Is that what you mean? If so, then it is definitely (theoretically very easily even) doable. And yes, I would accept your GDI+ way, lol. :D

Edit: Just to clarify, this would a project seperate from TillaGoto. Is that what you had in mind?

philou
  • Members
  • 68 posts
  • Last active: Jan 04 2011 01:12 PM
  • Joined: 26 Jul 2006
Well, seeing as a part of the needed functionality (getting the text at clicked position) is already there, I'd put it into TillaGoto, but that's just being lazy. It's your app, you get to decide what to do ;)

What you are proposing seems like a simple project. Therefore, I might be able to do it properly. Tell me if this is what you mean:
- There is no automatic IntelliSense (that is, as the user types)
- Instead, the user must request support by clicking on a command/function
- If the command/function clicked on is recognized, a small little GUI is shown with all the parameters and possibly info.

Yes.
It's a bit embarrassing, but I keep forgetting certain parameters or the correct order of them.

A fully-fledged IntelliSense based on the Scintilla APIs sounds mighty good, tho.

GDI+:
I've been planning a skinnable GDI+ notification app, which would come in handy in displaying the info. Alas, GDI+ is crap when it comes to drawing a string with multiple different font styles, so that would be a limit.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
This works:
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

}


TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
Small update:

- Changed AbsolutePath() to use APIs (much more reliable)

Yes.
It's a bit embarrassing, but I keep forgetting certain parameters or the correct order of them.

A fully-fledged IntelliSense based on the Scintilla APIs sounds mighty good, tho.

Don't worry, we all forget parameters sometimes!
I do intend to work on my IntelliSense project as soon as I muster up the courage to attack it!
As for the command syntax program we discussed, I'll play around during my free time this week to see what it gives :)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release:

- Largely improved the Line History feature. It now behaves much more intuitively.



TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release:

- Added bDirectives for TillaGoto directives
- Fixed sPathMatching failing to match for modified Notepad++ documents
- Changed LShift to Shift in general for line history feature
- Fixed bug where the next line of comments wouldn't get commented out

About the directives feature:
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".


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
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

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

/* 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#[email protected]\$\?\[\]]+:[[: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#[email protected]\$\?\[\]]+(?=\(.*?\)\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#[email protected]\$\?\[\]]", "", pos + 1)
        If i
            StringLeft, linetext, linetext, i - 1
        
        ;Trim before the first illegal character
        i := RegExMatch(StringReverse(linetext), "[^a-zA-Z0-9#[email protected]\$\?\[\]]", "", 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 [email protected] 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 [email protected] 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 [email protected] 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 [email protected] 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)
}


TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

Thank you so much for this program, programming ahk is much more professional and practical now.

Thank you! :D I can't live without it anymore lol. I use it all the time now!

I think it would be great to have a hotkey that can toggle iIncludeMode, what do you think?

yes, I had the same idea! I have another idea also... You'll see in the next release :)

Alternatively, possibly even better, there could be a RegExMatch option.

Can you expand on that?

Edit: Ah OK, I see what you mean. Do you mean to transform the search textbox into a regex field? I don't know if it's the right approach, but I understand your idea. Do you actually need such a powerful tool though? Wouldn't it be a little overkill?

Also, you can already single out labels/hotkeys from functions (or vice versa). Do you need even more flexibility than that? As it is right now, knowing any part of what you want is enough to narrow down the search (ie. "()" for functions and ":" for labels/hotkeys or "::" for hotkeys alone), and that's pretty good already.

Also, for the hotkey idea, I've been thinking about it and I don't know if it's worth it. I'm trying to think if the option would actually be used often enough to benefit from not having to go through the hassle of the script file to change one line (or simply just type it out in the script file you're working on if bDirectives is True). What do you think?

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Sometimes you do not know the name and this is where it is needed.

This is possible:
Show hotkeys only "::"
Show included hotkeys only ":: -"
Show Functions only "()"
Show included functions only "() -"

This is not possible:
Show only internal hotkeys/labels/functions
Show only external labels


I think one or the other would profit from this options quite a lot.

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007

Sometimes you do not know the name and this is where it is needed.
I think one or the other would profit from this options quite a lot.

OK, I think I have the perfect solution. :wink:

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
I am watching for it :)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release with exciting new features! :D

- Added the ability to exclude words (see in keyboard usage)
- The directive TillaGoto.bFilterComments is now also valid for #Include files (it will override the default setting)
- Added the ability to exclude comment blocks from scanning (see in general usage)
- The line history feature now remembers the line history across different opened script (by comparing the script path). As a side-effect, resaving (ie. "Save As...") the same script as another file will erase the line history.
- sPathMatching is now mandatory (line history relies on it)


About the word exclusion feature

You can also exclude items containing a certain string by preceding the string with an exclamation mark (ie. "!"). For example, typing "!() MyLabel" will filter out all the functions/labels/hotkeys that do not contain "MyLabel" as well as those that do contain "()".


About comment block exclusion

Because large blocks of comments are the most intensive to scan, if you know a comment block does not contain labels/hotkeys/functions look-alikes, you can exclude it from comment filtering by adding an exclamation mark (ie. "!") in the starting line of the block (ie. "/*!")



HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thank you :wink:

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
New release:

- Added uGotoDef (see keyboard usage)
- Added a few tweaks to reduce listbox flickering during typing
- Changing selection now keeps focus on the textbox
- Simplified the directives system
- Fixed small bug in line history feature


About uGotoDef

You can go to the definition line of the function/label on which the caret is located by pressing the hotkeys specified in uGotoDef (default is Shift+Enter). It is the keyboard equivalent of middleclicking on a function/label name.


Thank you :wink:

I appreciate your inputs to make TillaGoto better :D