Random feature? Shift+Space to expand hotstrings?

Discuss the future of the AutoHotkey language
sirksel
Posts: 222
Joined: 12 Nov 2013, 23:48

Random feature? Shift+Space to expand hotstrings?

Post by sirksel » 07 Mar 2021, 16:55

I've been updating my codebase all weekend for @lexikos' most excellent recent updates to the language. They are fantastic! If you haven't tried the new build, you definitely should. Anyhow, as I was refactoring things...

It dawned on me that it might be more efficient if there were a built-in way to use Shift+Space to expand hotstrings. The keys are extremely easily reached and logical for an expansion, and very often unused/underused, at least by me. Inadvertent hotstring expansion happens often when I'm using hotstrings as intended (e.g. without solely obscure end characters), and might it be a pain point that could be fixed?

I am writing this not as much as an "ask for help" but more as a feature discussion. I've noticed
earlier posts on the topic in "ask for help", but these seem to be pretty ugly workarounds for something that seems like it might be a good idea. It might even be something that could be highlighted in the docs as a core example of how to avoid unwanted expansion, if there were a built-in way to handle it.

Would adding an option that would enable SendKeys-style key notation be hard to do, or would it conflict with other features? If there's an easy way already included in the options, I'd be interested in knowing. Many thanks.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Random feature? Shift+Space to expand hotstrings?

Post by lexikos » 12 Mar 2021, 07:09

Hotstrings are formed by text (character strings), and Shift+Space does not imply any text different to Space.

If you want a hotstring to be triggered only by Shift+Space, I think the easiest method is this:

Code: Select all

#HotIf GetKeyState("Shift")
:*:x1 ::Example #1  ; Space is included in the hotstring.
This only works because Shift+Space still produces a space.

A more general workaround is to send an end char. For example (save as UTF-8):

Code: Select all

#Hotstring EndChars ⌨
#Hotstring O

$+Space::{
	SendLevel 1
	Send "⌨"
}

::x1::Example #1
If you want to allow other end chars and not omit them, you can just leave out the O option and simulate one of those end chars instead.

There are at least two drawbacks:
  • Shift+Space will always be blocked and can't be used for some other purpose (even when it is not preceded by a legitimate hotstring).
  • If no hotstring is triggered, or if the end char is not omitted, Shift+Space will produce a character that might be unwanted.
One workaround is to detect whether a hotstring was triggered. If not, delete the unwanted end char and potentially take some other action. For example:

Code: Select all

$+Space::{
	SendLevel 1
	Send "⌨"
	Sleep 50
	if !(A_ThisHotkey ~= "^:.*:" && A_PriorKey = "Space")
		Send "{BS}"
}
But this won't be 100% reliable, and may cause further complications.

Of course, you don't need a workaround if you want +Space to always produce the end char (for example, if you want +Space to produce a non-breaking space, you can use that as the end char).

More generally, the end key-combo might have some effect which you want to avoid when triggering a hotstring, but otherwise keep. Perhaps each context where the key-combo is used could be detected with #HotIf and excluded from firing the end char hotkey, but that might be difficult or impossible, or maybe there's some context where you want to use the key-combo for both purposes.


There have been requests for allowing Send syntax within the hotstring's trigger, so a hotstring can be triggered by a sequence of key combos that don't normally produce text. This cannot be done, because hotstring recognition is based on a single buffer containing text produced by keypresses, relying on the premise that these keypresses can be allowed to have their normal effect, then be undone by backspacing.

However, the hotstring end char is blocked (not backspaced) when backspacing is to be done or the O option was used. This can only be done by the hotstring recognizer, because only it knows that the keypress just triggered a hotstring. It's just a keypress at this stage and hasn't yet produced text, so recognizing other key combos for hotstring ending would certainly be feasible. Input and InputHook already use the concept of end keys, using Send syntax.


There might be cases where one wants to trigger hotstring expansion without pressing any keyboard keys. Adding something like Hotstring("End") for this purpose should be feasible. (There is already Hotstring("Reset") to reset the hotstring recognizer.) This would avoid the complications of #InputLevel vs. SendLevel or unintended effects of sending the end char or attempting to backspace it, but not the issues of hotkey (non)suppression.

Code: Select all

#Requires ???
^Space::Hotstring "End"
In theory, giving it a return value indicating whether a hotstring was recognized might also solve the issue of whether to suppress the hotkey:

Code: Select all

#Requires ???
#HotIf Hotstring("End")
^Space::return
However, I don't think the hook would handle this kind of reentry well. Also, this is noncompliant with an important recommendation:
Note: Scripts should not assume that the expression is only evaluated when the key is pressed (see below).
Specifically, pressing just Space in the following example would end hotstrings (search for a & b:: in the #HotIf documentation for details):

Code: Select all

#Requires ???
Space::Send " "
#HotIf Hotstring("End")
Space & b::return
Perhaps a couple of extra functions would allow the most flexibility (and I'm starting to think Hotstring() shouldn't have more than one "mode"):

Code: Select all

#Requires ???
Space::Send " "
#HotIf HotstringPending()
Space & b::HotstringEnd()
... but I can't yet imagine how HotstringPending could be used on its own.
Post Reply

Return to “AutoHotkey Development”