AHK Code Explorer
PSPad (my favourite editor) has a feature called code explorer, supporting several programming languages.
With it you can see and navigate to functions, defines, labels etc.
Due to the lack of a code explorer for AutoHotkey I coded something similar to bridgeover the time till PSPad supports it.
(
Jan 29, 2007: Meanwhile AutoHotkey is supported, but still very rudimentarily.)
It is easy to use:
You start it and switch to the PSPad window.
With ctrl-shift-e you display the code explorer window with a treeview.
If you select an element (hotkey, hotstring, label or function) with a double-click, the code explorer window will disappear and you will be navigated to the corresponding line in the source code.
Or you hide the window with a second ctrl-shift-e.
The AHK code explorer considers both types of comments. It is surely adaptable to other editors; I pointed out the PSPad specific actions.
Changes
Jan 31, 2007: added a user definable label type.
Feb 12, 2007: advanced the above to show comments in a hierarchical contents list. The identification pattern has changed.
Mar 31, 2007: Fixed: Hotstrings with options will be identified now correctly. Fixed: Assignments like var:=value starting at the beginning of a line won't be identified as labels any more.
Comment format
A hierarchical contents list is fed with comment lines with certain rules.
Due to affect the readability of existing comments as little as possible, the precursory pattern for hierarchical comment lines is "
;.", with eventually directly following points.
Because most coders start their comments in the same indentation as the following code, the HCL doesn't have to start necessarily in the first column. But the ";." sequence must be the first printable characters in the line.
The number of points in the precursory pattern represents the corresponding level inside the TreeView. ";." is the highest level, ";.." the second level etc. The level is not limited.
Code:
;. Headline 1
;.. Headline 1.1
;.. Headline 1.2
;... Headline 1.2.1
;. Headline 2
To avoid the necessity of creating additional lines with hierarchical comments, it is possible to show only a part of a comment line. AHK code explorer uses a separator consisting of a multiple-space/tab sequence, a succession of two or more spaces/tabs. That still allows largely individual formatting. Which text will be displayed obeys the following rules:
a) No separator: the complete line
b) One separator: the text followed by the separator.
c) Two ore more separators: the first text enclosed by two separators
A multiple-space/tab sequence following the precursory pattern or standing at the end of the line will not be identified as a separator.
Code:
;. The complete line will be shown in the TreeView.
;. as well as this line, without leading spaces within the comment
;. as well as this line
;. visible hidden
;. hidden visible hidden hidden hidden
;. visible hidden
Hierarchical comment lines are also recognized inside /* ... */-type comments.
IndentationIf the indentation level raises by more than 1, additional empty labels will be inserted in the TreeView.
This script itself is commented in the described style.
Code:
; A simplified surrogate for the missing PSPad code explorer for AutoHotkey
; by Rabiator
; -------------------------------------------------------------------------
;. --- Groups ---
; Control existance, order and behavior of the groups. The following table
; contains for each group its name, starting state of "Expand" and an icon
; number. If you dont't want to have it displayed in the TreeView, remove it
; from the table.
;.. The Group names are: "Functions", "Hotkeys", "Hotstrings", "Labels",
; "DllCalls", "MyLabels". These names are fix.
;.. Expanded : 0 = collapsed, 1 = expanded
;.. Icon number : The 3rd element is the number of the icon in the ImageList
; (see there).
Groups =
(
Functions, 0, 2
Hotkeys, 0, 3
Hotstrings, 0, 4
Labels, 0, 5
DllCalls, 0, 6
MyLabels, 1, 7
)
Loop, Parse, Groups, `n
{
Sep := (A_Index = 1) ? "" : "|"
StringSplit, Arr, A_LoopField, `,
GroupNames = %GroupNames%%Sep%%Arr1%
%Arr1%__Expanded = %Arr2%
%Arr1%__IconNo = %Arr3%
}
New := 1
; Start and Stop
^+e::
If (New = 0)
{
Gui, Destroy
New := 1
Return
}
; Only run if the editor window is active.
IfWinNotActive, PSPad -
Return
New := 0
;. Get the actual line number
; (PSPad: from the status bar. I hope this won't change in later versions.)
My_TitleMatchMode := A_TitleMatchMode
My_TitleMatchModeSpeed := A_TitleMatchModeSpeed
SetTitleMatchMode, 2
SetTitleMatchMode, Slow
WinGetText, Var
RegExMatch(Var, "s)\s+?(?'LineNo'[\d]+) :\s*\d+\s*\((?'Lines'\d+)", Act)
SetTitleMatchMode, %My_TitleMatchMode%
SetTitleMatchMode, %My_TitleMatchModeSpeed%
;. Save the contents
MyClipboard := ClipBoard
Send, ^a^c
Sleep, 100
Contents := Clipboard
Clipboard := MyClipboard
; Unselect the text by jumping to the actual line number (PSPad).
Send, ^g%ActLineNo%{ENTER}
; ------------------------------------------------------------------------
;. Parse for hotstrings, hotkeys, labels, functions and DllCalls
; Clear all lists
HotKeys =
Hotstrings =
Labels =
Functions =
DllCalls =
MyPatterns =
MyLabels =
bComment := 0
bFunction := 0
;. Parse each line
Loop, Parse, Contents, `n, `r
{
LineNo := A_Index
Line := A_LoopField
;
If (Mod(LineNo, 1000) = 0)
ToolTip Parsing line %LineNo% of %ActLines% , A_ScreenWidth/2, A_ScreenHeight/2
;.. Exclude /* .. */ comments
FoundPos := RegExMatch(Line, "^\s\/\*")
If (FoundPos)
{
bComment := 1 ; a comment section begins
Continue
}
If (bComment = 1)
{
FoundPos := RegExMatch(Line, "^\s\*\/")
If (FoundPos)
bComment := 0 ; a comment section ends
Continue
}
;.. Find Hotkeys
FoundPos := RegExMatch(Line, "([^\s:]+?)::", HotkeyPat)
If (FoundPos = 1)
Hotkeys = %Hotkeys%%HotkeyPat1%,%LineNo%`n
;.. Find Hotstrings
; improved by bold
FoundPos := RegExMatch(Line, ":[*?a-zA-Z0-9]*:([^\s:]+?)::", HotstringPat)
If (FoundPos)
Hotstrings = %Hotstrings%%HotstringPat1%,%LineNo%`n
;.. Find Labels
FoundPos := RegExMatch(Line, "([^\s;:]+):(\s|$)", LabelPat)
If (FoundPos = 1)
Labels = %Labels%%LabelPat1%,%A_Index%`n
;.. Find Functions
If (bFunction = 0)
{
; alphanumeric letters (international) and # _ @ $ ? [ ]
FoundPos := RegExMatch(Line, "^\s*(?'Name'[\w€-ÿ#_@$\?\[\]]+)(?'Pars'\(.*\))", Function)
If (FoundPos)
{
; prevent from taking an expression-if with directly following ( as a function.
If (FunctionName = "if")
Continue
FuncDeclLine := LineNo
; Find the {
; OTB?
FoundPos := RegExMatch(Line, ")\s*\{")
If (FoundPos)
; { found in the 1st line
Functions = %Functions%%FunctionName%%FunctionPars%,%FuncDeclLine%`n
Else
bFunction := 1
}
}
Else
{
; no OTB
FoundPos := RegExMatch(Line, "^\s*{")
If (FoundPos)
{
; { found in one of the following lines
Functions = %Functions%%FunctionName%%FunctionPars%,%FuncDeclLine%`n
bFunction := 0
}
FoundPos := RegExMatch(Line, "^\s*?[^;\s]")
If (FoundPos)
; found something that doesn't belong to a function
bFunction := 0
}
;.. Find DllCalls
FoundPos := RegExMatch(Line, "^\s*(DllCall\(.*?\))", DllCallPat)
If (FoundPos)
DllCalls = %DllCalls%%DllCallPat1%,%LineNo%`n
;.. Find user-defined labels
If GroupNames contains MyLabels
{
; Search for the pattern ";."
FoundPos := RegExMatch(Line, "^\s*;(\.+)\s(.*)", MyPat)
If (FoundPos)
{
Line = %Line% ; Trim spaces and tabs
; Evaluate the indentation level
StringLen Level, MyPat1
MyPat2 = %MyPat2% ; Trim spaces and tabs
Var := RegExReplace(MyPat2, "\s{2,}", "¢")
StringSplit, Arr, Var, ¢
If (Arr0 > 2)
MyLabels = %MyLabels%%Level%|%Arr2%,%LineNo%`n
Else
MyLabels = %MyLabels%%Level%|%Arr1%,%LineNo%`n
}
}
}
; ------------------------------------------------------------------------
;. Prepare and create the GUI
;.. Create an ImageList
ImageListID := IL_Create(7)
IL_Add(ImageListID, "shell32.dll", 4)
IL_Add(ImageListID, "shell32.dll", 42)
IL_Add(ImageListID, "shell32.dll", 13)
IL_Add(ImageListID, "shell32.dll", 29)
IL_Add(ImageListID, "shell32.dll", 44)
IL_Add(ImageListID, "shell32.dll", 45)
IL_Add(ImageListID, "shell32.dll", 46)
;.. Create the GUI
Gui, Add, TreeView, ImageList%ImageListID% R20 gJump w400 h500 vTV
;.. Delete pending `n, sort objects and create the treeview
Level := -1
Loop, Parse, GroupNames, |
{
Group := A_LoopField
ObjIndex := A_Index
IconNo := A_Index + 1
StringTrimRight, %Group%, %Group%, 1
; Treat MyLabels differently
If (Group <> "MyLabels")
{
Sort, %Group%
ID_%Group% := TV_Add(Group, 0, "Icon1 Bold Expand" . %Group%__Expanded)
StringSplit, Arr%Group%, %Group%, `n
Loop, % Arr%Group%0
{
RegExMatch(Arr%Group%%A_Index%, "(.*),(\d*)", Name)
ID_%Group%C%A_Index% := TV_Add(Name1, ID_%Group%, "Icon" . %Group%__IconNo gJump)
a := ID_%Group%C%A_Index%
Line_%a% := Name2
}
}
Else ; Group is "MyLabels"
{
If (Level = -1) {
Level := 0
; Create the item and save the last ItemID of this level.
LevelID%Level% := TV_Add(Group, 0, "Icon1 Bold Expand" . %Group%__Expanded)
LastItem := LevelID%Level%
}
; Add all items regarding their level.
StringSplit, Arr%Group%, %Group%, `n
Loop, % Arr%Group%0
{
RegExMatch(Arr%Group%%A_Index%, "(.*),(\d*)", Name)
RegExMatch(Name1, "(\d+)\|(.*)", SubName)
NewLevel := SubName1
Name := SubName2
LineNo := Name2
Loop
{
If (NewLevel <= Level + 1)
; max. 1 indent
{
ParentLevel := NewLevel - 1
LevelID%NewLevel% := TV_Add(Name, LevelID%ParentLevel%, "Expand Icon7" . gJump)
If (NewLevel > Level)
; Mark parent item with bold and folder icon
TV_Modify(LevelID%ParentLevel%, "Bold Icon1")
Level := NewLevel
a := LevelID%NewLevel%
Line_%a% := LineNo
Break
}
Else
; Overall more than 1 indent
; Step-by-step we create and add an indented empty item.
{
Level++
ParentLevel := Level - 1
LevelID%Level% := a := TV_Add("<empty>", LevelID%ParentLevel%, "Expand Icon7" . gJump)
Line_%a% := -1 ; Empty items don't have a valid line number.
}
}
}
}
}
ToolTip
Gui, +Resize
Gui, Show,
Return
GuiSize:
; The treeview gets automatically resized with the window.
GuiControl, Move, TV, % "h" A_GuiHeight-20 " w" A_GuiWidth-20
; Minimizing the window will destroy it.
If (A_EventInfo = 1)
{
Gui, Destroy
New := 1
}
Return
GuiClose:
ExitApp
; Jump to the line with the selected item.
Jump:
; It's impossible to jump to the category
ItemID := TV_GetSelection()
ParentID := TV_GetParent(ItemID)
If Not ParentID
Return
; Fetch the line number
LineNo := Line_%ItemID%
; Don't jump to an empty item
If (LineNo = -1)
Return
Gui, Submit
; A command that selects the found line (PSPad).
Send, ^g%LineNo%{ENTER}
; Remove the GUI
Gui, Destroy
New := 1
Return
__________________________________________
Created with BBCodeWriter 6.6 - the one and only
