1
votes

I have been pointed toward this AutoHotKey module which allows for dynamic hotstrings.

One of the examples is the calculation of percentages while typing (e.g. 5/40% will become 8%). To do this, the following code is necessary:

hotstrings("(\d+)\/(\d+)%", "percent")

percent:
p := Round($1 / $2 * 100)
Send, %p%`%
Return

I want to use this module to replace dots . with middle dots within words. I have figured out how to "find" the text, but not how to replace it correctly. I need to reference the initial text in order to put it in the replacement text. In the above code, using p := Round($1 / $2 * 100) uses the numbers input to calculate the percentage, but I can't figure out how to do the same with letters.

My code is the following:

hotstrings("[a-z]\.[a-z](\.[a-z])*", "word")

word:
a := $1
b := $2
Send, a{U+22C5}b
Return

But this just replaces the whole thing with a single middle dot and doesn't replace the surrounding letters. Also, I don't know how to consider the possibility of multiple dots (a.b.c.d for example). In python, I'd do a for loop but I don't really know AutoHotKey.

How can I do this?

Thanks

1

1 Answers

2
votes

Few problems here.

First one is the regex.
Firstly, you don't really want to think of the regex as if it was matching an infinitely long string of a.b.c.d.e.f.g.h.i.j.k.l.... instead you just want to think of a single case x.y. Those cases can be right next to each other.
So get rid of (\.[a-z])*.

Secondly, you don't have capture groups. Or well, you do have one in there, but I'm assuming you accidentally did it. If you're not yet familiar with Regex capture groups, I'd recommend learning them, they're quite useful in certain cases (like here!).
But anyway, to create capture groups, you just put ( ) around the part of the Regex you want to capture.
So you want to capture the characters before and after the . (or well, actually only the latter one, this approach will have a problem, more on that later). So your Regex would now look like this:
([a-z])\.([a-z])
Upon a match, the hotstrings() function would output two variables, $1 and $2 (that's all they are, names of variables).
When you refer to the variables, $1 gives you the character before the ., and $2 gives you the character after the ..


So now we get onto the second problem, referring to the capture group variables.

a := $1
b := $2
Send, a{U+22C5}b

Here you create the variables a and b for no reason, though that's not an issue of course, but how you try to refer to the variables a and b is a problem.
You're using a send command, so you're in legacy AHK syntax. In legacy AHK syntax you refer to variables by wrapping them around in %%.
So your send command would look like this:
Send, %a%{U+22C5}%b%
But lets not write legacy AHK (even though the hotstrings() function totally is legacy AHK).
To switch over to modern AHK (expression syntax) we would do specify a single % followed up by a space. And then we can do this:
SendInput, % $1 "{U+22C5}" $2
Also skipped defining the useless variables a and b and switched over SendInput due to it being the recommended faster and more reliable send mode.


And now would have an almost working script like so:

hotstrings("([a-z])\.([a-z])", "word")
return

word:
    SendInput, % $1 "{U+22C5}" $2
Return

It just would have the problem of chaining multiple a.b.c.d.e.f.g... not working very well. But that's fine, since the Regex could do with more improvements.

We want to use a positive lookbehind and capture only the character after the . like so:
(?<=[a-z])\.([a-z]) Also, I'd say it would be fitting to replace [a-z] with \w (match any word character). So the Regex and the whole script would be:

hotstrings("(?<=\w)\.(\w)", "word")
return

word:
    SendInput, % "{U+22C5}" $1
Return

And now it should work just as requested.
And if my talks about legacy vs modern AHK confuse you (that's to be expected if you don't know the difference), I'd recommend giving e.g. this a read:
https://www.autohotkey.com/docs/Language.htm