Note that this document is not complete.
There are a few pull requests to review and possibly merge. In particular, Helgef has designed a namespace/module system that would be very useful to some users, and may have some impact on other things (like default accessibility of rarely used built-in classes?).
Some changes are being considered for implicit global variables now that there's very little reason to use them in global subroutines (or use global subroutines at all) other than the auto-execute section; and because namespaces/modules (see Pull Requests) reduce the need to isolate globals from locals by default.
One possibility is to adopt Python's policy of allowing functions to access any global if the function does not contain an assignment to that variable.
Changes to how ByRef works are being considered in conjuction with the above, and to possibly make them work better with newer language features (like arrays and closures).
Some directives may be changed to use a subset of expression syntax, for consistency.
The error model should be revised to facilitate proper error handling. The error dialog can be improved to make debugging easier. An example implementation, details and ideas can be found at GitHub/Lexikos/Object.ahk.
A_LastError should probably not be set in cases where the function always throws an exception when there's an error. Some functions which throw currently do not set A_LastError.
Accessing an unset variable (except to set it, or with IsSet) should be treated as an error rather than an optional warning. Some implementation details need to be changed to permit this error to behave as an error should (exiting the thread, throwing an exception, or continuing if instructed by OnError/the user).
The following potential changes will most likely not be included in the initial v2.0 release. They will be considered for inclusion in a later release, such as v2.1, or v2.0.x if backward-compatible.
Gui.AddControl and GuiControl.Add currently require an array, but it would be trivial to support any enumerable object.
Maybe change WinGetID to WinGetHwnd for consistency with various other things. On the other hand, I don't want it to be inconsistent with
ahk_id, and don't want to change that to
ahk_hwnd. Reconsider this if the
ahk_ WinTitle scheme is ever updated or removed.
FileGetShortcut and FileCreateShortcut have too many parameters, but I haven't decided whether to change them or just replace both with a function to get a ShellLinkObject (through COM), either built-in or as a trivial script. ShellLinkObject has all the same capabilities plus a
Hotkey property and maybe others.
SplitPath could return an object when no output parameters are passed. Property names are probably easier to remember than parameter order, and easier to read and write than counting commas. This should have no negative impact on compatibility if added later, presuming scripts do not rely on SplitPath returning "".
Background info: Each window group consists of a list of window specs. A window spec in v1 consists of WinTitle, WinText, ExcludeTitle and ExcludeText. Window specs are evaluated in the order they were added to the group. For convenience, "rule" means "window spec" in the text below.
Problems in v1:
ExcludeTitle in v1 is limited to text matches; it does not support
ahk_class or similar for excluding windows by class, id, pid, etc. This is a source of confusion, as some users expect ExcludeTitle to be as flexible as WinTitle (despite the documentation being fairly explicit about what ExcludeTitle does).
A window matches the group if it matches any one of the rules added by GroupAdd, even if it matches the ExcludeTitle or ExcludeText of one of the other rules (regardless of the order of rules). This is sometimes counter-intuitive and makes ExcludeTitle/Text rarely useful with window groups.
The most likely solution is to introduce "negative" or "exclusive" rules. Rules are evaluated in the order they were added to the group, ending with the first rule (positive or negative) that matches the window. This provides the following benefits:
There are different ways this could be achieved:
Treat a rule as negative if it has ExcludeTitle/Text but not WinTitle/Text. Using the same combination directly (without a group) would produce different results, which may cause confusion.
Remove ExcludeTitle and ExcludeText from all commands, and add a new command or parameter for adding negative rules to a window group.
ExcludeTitle doesn't support ahk_pid and similar things. All it does is what the help file says: "Windows whose titles include this value will not be considered." The reason for this is that the added code size doesn't seem justified given how rarely ExcludeTitle is used.
In either case, a negative rule has no effect if there aren't any positive rules following it, because the absence of any subsequent positive rules means that the window won't be a match anyway. Therefore, for convenience, a group could match all windows by default if the last rule is negative. That way, if group N contains only negative rules,
ahk_group N matches all windows except those excluded by the group (instead of always matching nothing at all).
Some other ideas might supersede all considerations about ExcludeTitle/ExcludeText:
An oft-requested feature is to allow global settings such as SetTitleMatchMode, DetectHiddenWindows and DetectHiddenText to be overruled temporarily for a given WinTitle or command. At the moment, the most likely form it could take is
Title ahk_mode 3 ahk_dhw 1. Implementing this in a later release is unlikely to affect existing scripts; therefore this is not currently planned for v2.0.
Current thoughts as of 2020: This functionality is probably better off as methods of certain objects, such as
arr.IndexOf(value) instead of
value in arr.
contains are already reserved in v2-alpha (so adding them later won't break anything), v2 might seem incomplete without them, since users converting existing scripts from v1 (or just used to using v1) will need to find some alternative that works in v2. They should probably be implemented before the v2.0 release, and they should be updated to be more modern (allow arrays).
Current usage: if var [not] in/contains MatchList. MatchList is a comma-delimited list. Var contains a string.
It seems logical to allow an array as a direct substitute for MatchList. Someone suggested that the new operators should never accept a comma-delimited list, but in that case a string would be interpreted one of the following ways:
x in str=
x = str(not useful)
str1 contains str2=
InStr(str1, str2)(expressive and intuitive, but hardly useful)
x in/contains str=
x in/contains [str](saving only a couple of characters and the overhead of creating an array; but an optimisation could allow the list of values in  to be passed directly to the operator without creating an actual array)
x in/contains str=
x in/contains StrSplit(str)
x in str=
StrLen(x) = 1 && InStr(str, x)(but the author probably knows
StrLen(x) = 1, so could just write
InStr(str, x)in the first place)
x contains strmostly equivalent to
x ~= '[' str ']'(regex character class)
str1 in str2=
str2 contains [str1]or
InStr(str2, str1)(substring match)
str1 in [str2, str3]= str1 equals str2 or str3 (not a substring match)
If arrays are supported on the left side of
contains, how would that work? Traditionally the difference between
contains is that
in does a direct comparison of the value while
contains does a substring match. Would
arr1 contains arr2 search for each element of arr2 as a substring of each element of arr1? Or check for the existence of any common values between the two? Or all? Or check whether the exact sequence of values in arr2 exists within arr1 as a subset? The last option would be analogous to
InStr(str1, str2), but not
str1 contains str2.
Some (not very relevant) discussion here: https://autohotkey.com/boards/viewtopic.php?f=37&t=3847
between probably won't be implemented at any stage. An alternative could be to allow operator chaining as in Python and Perl 6, where
x < y < z is equivalent to
(x < y) and (y < z). However, that would mean that parentheses would change the meaning of
< (which doesn't seem right), such as in
x < y < z vs
(x < y) < z, because the latter case would compare the result of
x < y (true or false) to z. Some users already try
x < y < z and wonder at the result, so if it isn't implemented, maybe chained operators should be detected and cause a warning/error. (But if chained operators would be complicated to detect, cost vs benefit seems to be in favour of implementing chaining.)
Detecting mispelled function names at load time is a useful feature of the language, but currently only applies to direct calls. References such as
"name", if mispelled, will only be detected when the script attempts to call them (which might not be where they are referenced).
Func("name") is optimized (in v2) to resolve at load time, but cannot raise a load time error when the function does not exist because it is not an error. For instance,
name can be defined in an optional
#include *i, and Func is how it would be resolved to be called. If it raised a load time error, this would not work:
if f := Func("name") ; ← load time error f.Call()
Nor would this:
if IsFunc("name") MyRegisterEvent(Func("name")) ; ← load time error
The most obvious solution is to add new syntax for a function reference which would be caught as an error at load time if the function does not exist.
@name, as in
greet := @MsgBox.Bind("Hello").
Func'name', as in
greet := Func"MsgBox".Bind("Hello"). Although not currently valid, it might be difficult to visually distinguish from auto-concat.
Deferred because: I don't like any of the syntax ideas so far. Although more convenient, it still doesn't provide parity with direct calls and variables containing function objects (including callable classes). There might be better ways to detect problems than adding new syntax that can be validated at load-time. The ideas so far do not break compatibility and therefore could be implemented later.
Strings beginning with
Chr(0) are currently
false, because the boolean checks assume a zero-terminated string rather than a binary counted one. Other cases may exist where the string is assumed to be empty.
Any operators or functions which expect a number will read only up to the first binary zero, and therefore may consider a string containing embedded nulls (possibly followed by other non-numeric characters) to be numeric.
In theory, all strings containing binary zero should be considered non-empty and non-numeric.
Deferred because: proper, consistent support for null characters in strings seems to be more work than it is worth (cost outweighs benefit).
When RAlt is pressed or sent, if the active window's keyboard layout has AltGr, the system generates LCtrl-down and LCtrl-up. (It also generates LCtrl-up when switching between keyboard layouts if the previous layout had AltGr, even if the new one also has AltGr.) This has some potentially undesirable consequences:
LCtrl up::even if neither AltGr nor LCtrl was pushed or in the pressed state.
^!) to be triggered instead of hotkeys with just Alt (
<^>!, but are unable to distinguish between this and LCtrl+AltGr.
GetKeyState("LCtrl", "P")reports that LCtrl is pressed when AltGr is pressed, regardless of the real LCtrl.
It is technically possible to identify AltGr's fake LCtrl by utilizing the little-known fact that
KBDLLHOOKSTRUCT::scanCode has the 0x200 bit set for the fake LCtrl events. (KeyHistory doesn't show this because the scan code is truncated to 1 byte [and then 0x100 is added if the 0xE0 flag is set].)
It would only take a few lines of code to mark the fake LCtrl events as "ignored", which would avoid all of the "consequences" listed above, except that it would introduce inconsistency between "hook" hotkeys (which would treat AltGr as RAlt) and "reg" hotkeys (hotkeys with
^!key would be triggered by AltGr+key). AltGr would become just
>! (both of which always use the hook) and
<^>! would mean LCtrl+AltGr. When only AltGr is down, GetKeyState would report LCtrl as physically up but logically down (reflecting the fact that the user hasn't physically pressed LCtrl, but the active window considers Ctrl to be down).
Deferred because: I don't recall seeing any related issues, and I don't use a layout with AltGr. The possibility of treating AltGr as just RAlt (or treating the LCtrl as non-physical/ignored) can be explored after v2.0, perhaps as an optional mode.
The Thread function still has sub-commands.
Deferred because: It's used more rarely than WinSet, Control, Drive, etc. so leaving it with sub-commands (extra quote marks) doesn't seem like an issue.
We seem to lack functions for getting/setting the value of external controls of some types, such as Slider, UpDown, MonthCal (I have not tested ControlSetText on these).
ListViewGetContent has usage virtually identical to ControlGet List (when used on a ListView). It would be ideal to provide a similar interface for both external and script-made ListViews. Due to complexity and lack of interest (by me), this is being deferred indefinitely.