AutoHotkey Scripting Philosophy or Speeding Up the InstantHotstring.ahk script
“Before you can fix a problem you must understand it. Before you can understand a problem you must fix it.”
Sounds like a chicken/egg problem, but I assure you it isn’t. You only begin to understand a problem when you start working on a solution. As you grind through a script, you develop a deep awareness of its inner workings. I encourage any AutoHotkey novice to jump into a new script with both feet—even if you don’t think you know what you’re doing.
Sitting around and speculating about possible answers to a question often serves as mental thumb-twiddling. You must start somewhere—anywhere. Until you actually dig into it and undertake the work, you will never truly comprehend the obstacles and pitfalls associated with implementing a fix. You’ll find this singularly true when writing AutoHotkey scripts— especially as a beginner. The answer to the question “Where do I start?” is “Anywhere!”
When undertaking a new AutoHotkey script, few people (if anyone) know exactly how to get it done. With a multitude of tools, you’ll find numerous paths to an answer. The key to getting started lies in locking onto your goal (or where you want to end up). Then begin testing the various tools and methods available in AutoHotkey. Each test gives a little more insight into possible paths and the associated obstacles. Eventually, you’ll reach an understanding of the problem and the possible paths to a solution.
Trial and error is not a waste of time. It’s just as important to learn what not to do as it is to discover the things that work. Anyone involved in AutoHotkey script writing accepts the slings and arrows of authoring code. In fact, any misstep often points us in the right direction while adding to our toolbox for the future.
New AutoHotkey scriptwriters should maintain patience with themselves. While writing an AutoHotkey script serves as a method for adding more power to your Windows computer, you’ll find the journey of discovery as important as the result. Since the road curves and occasionally misdirects, you won’t travel in a straight line. However, each stumble teaches you important lessons about how to use AutoHotkey.
Ultimately, the experienced AutoHotkey practitioner might rewrite all (or a large section) of the code—tossing the original. This often occurs after early-on launching into one solution, then, after understanding the problem, regrouping and rewriting.
The InstantHotstring.ahk Script
I’ve been working on (and writing blogs about) the InstantHotstring.ahk script (also found at the ComputorEdge Software Showcase) for quite a while now. I’ve since dug into many diverse AutoHotkey issues unrelated to my original goal of exploring the capabilities of the new Hotstring() function. (Check out this Web page for a list of the InstantHotstring.ahk script blogs.) I can say that I now have a deep understanding of the InstantHotstring.ahk script.
I originally implemented what turned out to be a slow Hotstring file loading process by reusing the tools I implemented early-on in the script development. Now that I comprehend the slow-speed problem that cropped up when writing a working (yet clumsy) solution, I’m ready to rewrite it with a simpler solution.
When adding new Hotstrings via the GUI window, the InstantHotstring.ahk script takes input from a variety of GUI controls and writes the Hotstring code then activating it using the Hotstring() function. While this works great for setting up individual Hotstrings within the app, using the same code to add Hotstrings from a file produces a slow, cumbersome process. Even though the code works, the idea of tearing apart a previously written Hotstring to insert it into the GUI controls, then recombining it back into its original form, adds too much unnecessary overhead. Directly loading the Hotstrings without interaction with the GUI controls cuts out the middleman.
As I worked on the original implementation, I began seeing the symptoms of a script devolving into chaos. Here I offer these signs to beginners as indications of when the time to rethink and rewrite the code may have arrived.
Symptoms of AutoHotkey Getting Out of Control
Originally, I thought that using the routines which effectively set up single Hotstrings in the InstantHotstring.ahk script would provide suitable techniques for loading and activating Hotstrings from a file. While it worked, the script became more and more of a kludge in ways I hadn’t predicted. I arrived at a point where I knew that I needed to rewrite the Hotstring file load subroutine.
When the times comes to rethink the writing of a script, you’ll get hints similar to these:
1. Slow Scripts
If you find yourself doing a lot of waiting, it may be time to reexamine how you’re approaching the problem. Some commands and features take a little time to do their job. If you only run them once, then you may not notice the difference. But if you put them inside a loop, the time delay can drive you crazy.
For example, I use the ControlGet, OutputVar, List command to capture all of the Hotstrings currently active in the DropDownList GUI control in the InstantHotstring.ahk script. The script requires this list to check for duplicates and update current Hotstrings. While it works in the blink of an eye when activating or updating a single Hotstring, running it for every Hotstring the script loads from a file puts a tremendous drag on the routine. I needed to change to script to only extract the current DropDownList GUI control list once when loading Hotstrings from a file.
Running commands too many times (usually inside a loop):
While the ControlGet, OutputVar, List command:
ControlGet, Items, List,, ComboBox1, A
can cause a particular delay, other commands can add significant running time when placed inside a loop. The DropDownList GUI control requires the following commands to update a Hotstring found on the list:
Control, Delete, %A_Index% , ComboBox1
This command removes the matched items from the DropDownList GUI control.
GuiControl,, ComboBox1 , % ":" . Options . ":" . NewString . "::" . TextInsert
This command inserts the updated Hotstring into the DropDownList GUI control.
Hotstring(":" . OldOptions . ":" . NewString , TextInsert , "Off")
For certain Hotstring options (case sensitive (C) and inside a word (?)), I needed to disable the active Hotstring before I could activate it with new options. Subsequently, the script activates (or reactivates) each Hotstring.
My solution only downloads the items in the DropDownList GUI control once into the variable DropDown:
ControlGet, DropDown, List, , ComboBox1, A
making any changes in the new variable rather than directly in the DropDownList GUI control. Then, after reading and processing the entire Hotstring file, the script replaces the items in the DropDownList GUI control with the updated variable (DropDown):
NewList := "|" . StrReplace(Dropdown,"`n","|") GuiControl,, ComboBox1 , %NewList%
The first line of code replaces newline characters (`n) with the delimiting character (|) for the DropDownList GUI control. Preceding the list with a single vertical line character forces replacement of the entire contents of the DropDownList GUI control with the variable contents rather than appending to the list.
While this approach did not eliminate all of the loops, it did eliminate the slow-processing DropDownList GUI control commands.
For an alternative approach, I could replace the limited DropDownList GUI control with the ListView GUI control which offers a number of superior features and functions giving enhanced control—plus, more hooks for adding capabilities. I used the DropDownList GUI control merely for exploration of the control’s capabilities. (For more information on how to use the ListView GUI control, see the scripts—AddressBook.ahk, AutoHotkeyControl, and ToDoList— in the e-book AutoHotkey Applications.)
2. Adding Traps and Exceptions to a Routine
Another sign of a script run amok crops up when fixing too many unique problems arising from reusing special purpose routines for multiple purposes with traps and exceptions.
When creating and resetting single Hotstrings via the app, the variations in how the options worked forced me to add unique code for dealing with those discrepancies. However, when loading completed Hotstrings from a file I did not need the code for those exceptions. I chose to deal with the differences by setting up a variable (LoadHotstring) which activated whenever I loaded Hotstrings from a file. I used that condition to skip unneeded pieces of the subroutine:
If (LoadHotstrings != 1)
I used a form of this conditional a number of times to prevent regular Hotstring update routines from running. By rewriting a simpler LoadHotstrings subroutine, I eliminated the need to trap unneeded code.
If you find yourself adding this type of modification to a script, then you may want to rethink your approach to the problem.
3. Adding Extra Subroutines for New Exceptions to the Old Routine
Inevitably, some Hotstrings loaded from any .ahk file may contain unwanted extras—in this case, comments on the same line preceded by a semicolon (;). This required special formatting code before loading a Hotstring from a file into the DropDownList GUI control:
GuiControl, , Edit1, % HotString ; Removes comments HotText := StrSplit(HotString, A_Space . ";") ; Trim rightside spaces GuiControl, , Edit2, % RTrim(HotText," `r")
While appropriate for the LoadHotstrings file reading subroutine, adding it to the original AddHotstring subroutine only complicates the entire script.
3. Calling Numerous GoSub Commands
Another hint that could force you to reassess your approach to an AutoHotkey script pops up when you see the overuse of the GoSub command or GoTo command:
GoSub SetOptions GoSub CapsCheck GoSub AddHotstring
While using the GoSub command may serve an important purpose in many scripts, it can also indicate that you have created a monster—as described above. Be sure the subroutine serves a clear purpose—not needing traps and exceptions. Often converting a subroutine into a function forces you to evaluate whether you have made the subroutine too complicated.
Tip: As a rule, you should avoid using the GoTo command which almost always makes scripts difficult to follow. However, you might find times when the GoTo command offers unique capabilities as demonstrated in the CheeseBurgerRecipe.ahk script (see “GoTo Command Versus GoSub Command“).
4. Code Logic Too Difficult to Follow
The more you add to a script, the more complicated it gets. By creating functions and subroutines for any repeated code you make it easier to keep the script free of redundancy. However, eventually, you’ll find it confusing when searching for the appropriate code in the script. Any documentation or comments you add will make it easier for you or anyone else using the script. Plus, although not required, maintaining the subroutines in the same order as the GUI controls appear in the main GUI window can help to understand the logic of the script.
Note: I’m terrible at documenting my scripts. Fortunately, many of the earlier scripts are so short they don’t require much. Plus, the discussions in my blogs and books go into much more detail than I could ever put into the script.
In the InstantHotstring.ahk script, I’ve taken a little more time and inserted one-line comments before key snippets, plus I’ve added visually distinguishable headings before major subroutines and functions:
/* ============================================= Subroutine LoadHotstrings: Loads Hotstrings from the Selected File ============================================= Loads Hotstrings from the selected (FileSelectFile) file replacing or updating duplicate Hotstrings from the file. */
While these documenting techniques help, sometimes you may want to take the time to consider alternative approaches to simplify the script. We tend to dogmatically stick with our original script because…well…it works. The thought of making major changes overwhelms us. Sometimes we may not find making changes practical. Other times we’re glad we invested time into improving the script.
Basic Hotstring Loading
My fix for speeding up the InstantHotstring.ahk script and removing complications from the code involved bypassing the original code in the AddHotstring subroutine and writing a new LoadHotstring subroutine which:
- Only downloads the DropDownList GUI control contents once into the variable DownLoad.
- If found in the variable, the script skips duplicate Hotstrings (Continue command).
- Updates any found Hotstrings with changed parameters directly in the variable.
- Adds each new file Hotstring directly to the DropDown list variable.
- Only updates DropDownList GUI control from the variable once at the completion of processing the file Hotstrings.
I don’t regret the time I took running down that first path. I tested and implemented many tricks resolving the little problems which cropped up. Plus, I developed a deep understanding of the slow-script problem which made coming up with a simpler solution much easier. Sometimes pursuing an errant path provides the knowledge you need to get onto a better road.
Click the Follow button at the top of the sidebar on the right of this page for e-mail notification of new blogs. (If you’re reading this on a tablet or your phone, then you must scroll all the way to the end of the blog—pass any comments—to find the Follow button.)
This post was proofread by Grammarly
(Any other mistakes are all mine.)
(Full disclosure: If you sign up for a free Grammarly account, I get 20¢. I use the spelling/grammar checking service all the time, but, then again, I write a lot more than most people. I recommend Grammarly because it works and it’s free.)