Skip to content

Commit

Permalink
Add new ahk_x11-only directive #DefineCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
phil294 committed Dec 10, 2023
1 parent 01b6da5 commit 66c29c2
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 6 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ AHK_X11 can be used completely without a terminal. You can however if you want u
<details><summary>*Click here* to see which commands are implemented and which are missing. Note however that this is not very representative. For example, no `Gui` sub command is included in the listing. For a better overview on what is already done, skim through the <a href="https://phil294.github.io/AHK_X11"><b>full documentation here</b></a>. Generally speaking, everything important is done.</summary>

```diff
DONE 57% (126/221):
DONE 57% (126/222):
+ Else, { ... }, Break, Continue, Return, Exit, GoSub, GoTo, IfEqual, Loop, SetEnv, Sleep, FileCopy,
+ SetTimer, WinActivate, MsgBox, Gui, SendRaw, #Persistent, ExitApp,
+ EnvAdd, EnvSub, EnvMult, EnvDiv, ControlSendRaw, IfWinExist/IfWinNotExist, SetWorkingDir,
Expand All @@ -155,11 +155,12 @@ DONE 57% (126/221):
+ WinWaitClose, WinWaitActive, WinWaitNotActive, DriveSpaceFree, FileGetSize, FileRecycle,
+ FileRecycleEmpty, SplitPath, StringSplit

NEW 4% (10/221): (not part of spec or from a more recent version)
NEW 4% (11/222): (not part of spec or from a more recent version)
@@ Echo, ahk_x11_print_vars, FileRead, RegExGetPos, RegExReplace, EnvGet, Click @@
@@ Eval, ahk_x11_track_performance_start, ahk_x11_track_performance_stop @@
@@ #DefineCommand @@

REMOVED 5% (11/221):
REMOVED 5% (11/222):
# ### Those that simply make no sense in Linux:
# EnvUpdate, PostMessage, RegDelete, RegRead, RegWrite, SendMessage, #InstallKeybdHook,
# #InstallMouseHook, #UseHook, Loop (registry)
Expand All @@ -168,7 +169,7 @@ REMOVED 5% (11/221):
# AutoTrim: It's always Off. It would not differentiate between %a_space% and %some_var%.
# It's possible but needs significant work.

TO DO 32% (71/221): alphabetically
TO DO 32% (71/222): alphabetically
- BlockInput, Control, ControlFocus, ControlGet, ControlGetFocus, ControlMove, DetectHiddenText,
- DetectHiddenWindows, Drive, DriveGet, FileCopyDir, FileCreateShortcut, FileInstall, FileGetAttrib,
- FileGetShortcut, FileGetTime, FileGetVersion, FileMove, FileMoveDir, FileRemoveDir, FormatTime,
Expand Down Expand Up @@ -277,6 +278,7 @@ Like covered above, AHK_X11 is vastly different to modern Windows-AutoHotkey bec
- `#NoEnv` is the default, this means, to access environment variables, you'll have to use `EnvGet`.
- All arguments are always evaluated only at runtime, even if they are static. This can lead to slightly different behavior or error messages at runtime vs. build time.
- Several more small subtle differences highlighted in green throughout the docs page
- There are a few commands present which are missing from Windows AHK, i.e. prominently [`#DefineCommand`](https://phil294.github.io/AHK_X11#h_DefineCommand.htm), [`Echo`](https://phil294.github.io/AHK_X11#Echo.htm) and [`Eval`](https://phil294.github.io/AHK_X11#Eval.htm).

Besides, it should be noted that un[documented](https://phil294.github.io/AHK_X11) == undefined.

Expand Down Expand Up @@ -328,6 +330,19 @@ Besides the [Legacy Syntax](https://www.autohotkey.com/docs/v1/Language.htm#lega
result += %add_second%
Return
```
**or** you can use the special built-in command (AHK_X11-only) [`#DefineCommand`](https://phil294.github.io/AHK_X11#h_DefineCommand.htm) to reduce code repetition:
```ahk
#DefineCommand Add, LblAdd
Add, result, 7, 8
Send, %result%
Return
LblAdd:
%A_Param1% = %A_Param2%
%A_Param1% += %A_Param3%
Return
```
- `my_array := ["one", "two", "three"]` ->
```ahk
my_array1 = one
Expand Down
80 changes: 80 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ <h2>Table of contents </h2>
<li>
<a class="x11" href="#Eval.htm">Eval</a>
</li>
<li>
<a class="x11" href="#h_DefineCommand.htm">#DefineCommand</a>
</li>
<li>
<a class="tbd" href="#ListLines.htm">ListLines</a>
</li>
Expand Down Expand Up @@ -802,6 +805,9 @@ <h2>Table of contents </h2>
<li>
<a class="tbd" href="#h_CommentFlag.htm">#CommentFlag</a>
</li>
<li>
<a class="x11" href="#h_DefineCommand.htm">#DefineCommand</a>
</li>
<li>
<a class="tbd" href="#h_ErrorStdOut.htm">#ErrorStdOut</a>
</li>
Expand Down Expand Up @@ -2173,6 +2179,10 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<td height="16" class="calibre4"><a href="#ExitApp.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ExitApp</a></td>
<td height="16" class="calibre4">Terminates the script unconditionally. </td>
</tr>
<tr class="calibre3">
<td height="16" class="calibre4 x11"><a href="#h_DefineCommand.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">DefineCommand</a></td>
<td height="16" class="calibre4">Registers a new command.</td>
</tr>
<tr class="calibre3">
<td height="16" class="calibre4"><a href="#FileAppend.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">FileAppend</a></td>
<td height="16" class="calibre4">Appends text to a text file.</td>
Expand Down Expand Up @@ -9652,6 +9662,76 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<p class="calibre8">; Run a dynamic command:<br class="calibre12" /> cmd = MsgBox<br class="calibre12" /> Eval, %cmd% Hello</p>
</div>
</div>
<div class="calibreMain x11">
<div class="calibreEbookContent">
<a id="h_DefineCommand.htm" href="#h_DefineCommand.htm">#</a> <h2 class="calibre17">#DefineCommand</h2>
<hr size="2" class="calibre24" />
<p class="calibre8">Registers a new command. Comparable but inferior to "function"s in modern Windows AHK.</p>
<table cellspacing="5" width="100%" class="calibre28">
<tbody class="calibre2">
<tr class="calibre3">
<td height="48" class="calibre4">#DefineCommand, Name, Label</td>
</tr>
</tbody>
</table>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Parameters</strong></p>
<table border="1" bordercolor="#C0C0C0" cellpadding="3" cellspacing="0" width="100%" class="calibre1">
<tbody class="calibre2">
<tr class="calibre3">
<td width="15%" class="calibre4">Name</td>
<td width="85%" class="calibre4">
The name of the new command which you can then invoke in subsequent lines.
</td>
</tr>
<tr class="calibre3">
<td width="15%" class="calibre4">Label</td>
<td width="85%" class="calibre4">
The name of the label or hotkey label to which to jump when the newly registered command is invoked. Internally this behaves like <a href="#Gosub.htm">GoSub</a>, check there for details.
</td>
</tr>
</tbody>
</table>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Remarks</strong></p>
<p class="calibre8">You should use this directive if you need to encapsulate logic that you use multiple times in your script, to prevent repeating yourself. Only do this if you can't find another built-in command that already does what you need.</p>
<p class="calibre8">This directive can be used multiple times to register multiple commands. It will only affect code in subsequent lines. This means that you will likely want to put it at the top of your script. The labels, however, can be placed anywhere in your script.</p>
<p class="calibre8">Inside the respective label, you can access the passed parameters using the pseudo-built-in vars A_Param1, A_Param2 and so on. Check the example below.</p>
<p class="calibre8">This feature hasn't been optimized a lot. It may not be super fast and it's possible to shadow variable names with a custom command (not built-in commands though). Generally however, it should work great for most use cases.</p>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Example</strong></p>
<p class="calibre8"><pre>
; Registering a new custom command: GetMaximumValue, OutputVar [, Value1, Value2, ...]
#DefineCommand GetMaximumValue, LblGetMaximumValue

GetMaximumValue, var1, 7, 8, 26, 12, 15
GetMaximumValue, var2, 4, %var1%

; Prints: 26
MsgBox, Maximum: %var2%
Return

LblGetMaximumValue:
max = 0
; Iterating A_Param1, A_Param2 and so on until no more params are found:
Loop
{
param_index = %A_Index%
; We skip the first param as it's the OutputVar
param_index += 1
StringTrimLeft, value, A_Param%param_index%, 0
If value =
Break
If value > %max%
max = %value%
}
; We have defined OutputVar as our first parameter. This will set the out value:
%A_Param1% = %max%
max =
Return
</pre></p>
</div>
</div>
<div class="calibreMain tbd">
<div class="calibreEbookContent">
<a id="ListLines.htm" href="#ListLines.htm">#</a> <h2 class="calibre17">ListLines</h2>
Expand Down
16 changes: 14 additions & 2 deletions src/build/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module Build
@block_comment = false
@hotstring_default_options = ""
@already_included = [] of Path
@user_defined_command_label_by_name = {} of String => String

def parse_into_cmds!(lines : Indexable(String))
@cmds.clear
Expand Down Expand Up @@ -64,8 +65,7 @@ module Build
raise "Unexpected */" if ! @block_comment
@block_comment = false
elsif @block_comment
#
# This is the "normal" case where 90% of all commands fall into. All other if-clauses
# Below is the "normal" case where 90% of all commands fall into. All other if-clauses
# are special cases.
elsif cmd_class
csv_args = split_args(args, cmd_class.multi_command ? cmd_class.max_args + 1 : cmd_class.max_args)
Expand Down Expand Up @@ -116,6 +116,10 @@ module Build
@runner_settings.x11_grab_from_root = true
elsif line.starts_with?("#!") && line_no == 0 # hashbang
elsif first_word == "#noenv"
elsif first_word == "#definecommand"
split = split_args(args)
raise "#DefineCommand requires two arguments" if split.size < 2
@user_defined_command_label_by_name[split[0].downcase] = split[1].downcase
elsif first_word == "if"
split = args.split(/ |\n/, 3, remove_empty: true)
var_name = split[0]
Expand Down Expand Up @@ -206,6 +210,14 @@ module Build
comma = rest_args.empty? ? "" : ","
add_line "Gui#{sub_cmd}, #{gui_id}#{comma} #{rest_args}", line_no
@runner_settings.persistent = true
elsif label = @user_defined_command_label_by_name[first_word]?
split_args(args).each_with_index do |arg, i|
add_line "SetEnv, A_Param#{i+1}, #{arg}", line_no
end
add_line "GoSub, #{label}", line_no
split_args(args).each_with_index do |arg, i|
add_line "SetEnv, A_Param#{i+1}", line_no
end
elsif first_word.ends_with?(':')
@cmds << Cmd::ControlFlow::Label.new line_no, [first_word[...-1]]
elsif first_word.ends_with?("++")
Expand Down

0 comments on commit 66c29c2

Please sign in to comment.