Hotstrings and options¶
Trigger representations¶
The generic hotstring model keeps trigger meaning separate from AutoHotkey source spelling.
Each initialized Hotstring stores two
trigger representations:
semantic_trigger— the actual characters AutoHotkey should recognize;ahk_trigger— the deterministic, minimally escaped spelling used when rendering AHK source.
The constructor argument trigger is an InitVar, so it is not retained as a
third ambiguous representation after initialization.
By default, Hotstring and candidate classes interpret constructor trigger
as semantic text. ExistingHotstring
receives trigger text extracted from an AHK source file, so it shadows the class
policy TRIGGER_INPUT_IS_AHK_SOURCE = True. The base
Hotstring.__post_init__()
still owns the same resolution algorithm for every subclass:
flowchart LR
A["Constructor trigger"] -->|"apply class policy"| B["semantic_trigger"]
B -->|"encode canonically"| C["ahk_trigger"]
When
CHECK_TRIGGER_ROUND_TRIP is
enabled, initialization also verifies the conversion invariant:
ahk_to_semantic_trigger(ahk_trigger) == semantic_trigger
This is an internal consistency check. A failure indicates a bug in the conversion contract rather than invalid user input.
Trigger conversion and canonicalization¶
semantic_to_ahk_trigger()
produces one deterministic, minimally escaped AHK representation.
ahk_to_semantic_trigger()
performs the reverse semantic conversion: it interprets an AHK source spelling
and returns the trigger characters that spelling represents. It may therefore
accept multiple valid source spellings that have the same semantic result.
During canonical encoding, characters which always require source escaping, such as a literal backtick or supported control characters, are escaped unconditionally. Colons and semicolons are escaped only when their source context requires it:
:is escaped only as needed to prevent an unescaped::sequence inside the trigger or against the declaration delimiter;;is escaped only when a literal source space immediately precedes it and it would otherwise begin a comment.
Consequently, multiple valid source spellings can decode to the same semantic trigger. For example:
foo:bar
foo`:bar
both decode to:
foo:bar
and re-encode canonically as the minimally escaped form:
foo:bar
The helpers are therefore intentionally asymmetric with respect to source spelling. The supported semantic round trip is:
ahk_to_semantic_trigger(semantic_to_ahk_trigger(value)) == value
for every semantic trigger accepted by the encoder. Re-encoding arbitrary AHK source is allowed to canonicalize optional or unnecessary escapes.
Case-insensitive matching key¶
Conflict detection operates on semantic trigger text, never on escaped AHK
source spelling. Each hotstring therefore caches a derived
case_insensitive_semantic_trigger_key.
On Windows the key is produced with the Microsoft CRT under an explicit C
locale to follow AutoHotkey's case-insensitive comparison basis as closely as
possible. On non-Windows systems, str.lower() is used as a deterministic
fallback. The key is runtime-derived state and is not persisted in the source
cache.
Hotstring rendering¶
Hotstring.render(content=None)
renders the canonical option declaration and ahk_trigger, never the
constructor input.
content means everything emitted after the declaration's second ::. It is
therefore intentionally broader than an inline replacement RHS: depending on
AutoHotkey syntax and options, it may be replacement text, executable content,
or multiline block content.
The generic
Hotstring.to_ahk_string_literal()
helper separately handles AHK double-quoted string syntax. It escapes literal
backticks and quotes plus all supported AHK control escapes (r, n, b,
t, v, a, and f). Trigger encoding and quoted-string encoding remain
separate because their syntax rules are different.
Declared option state¶
HotstringOptions represents what is
explicitly declared on one hotstring. Every omitted option uses the shared
sentinel:
InheritedState.INHERIT
This is distinct from the actual semantic value of the option. For example,
CaseMode contains only real case modes,
while inheritance is represented by
InheritedState rather than by a
synthetic CaseMode.INHERIT member.
Two-state settings use the SettingState
members SettingState.ENABLED and SettingState.DISABLED. The * option is
exposed semantically as ending_character_optional, so its mapping is direct:
| Declaration | Semantic state |
|---|---|
* |
SettingState.ENABLED |
*0 |
SettingState.DISABLED |
| omitted | InheritedState.INHERIT |
declaration()
serializes the parsed semantic state back to one canonical option string.
Repeated or contradictory source options therefore collapse to the last
effective value rather than being reproduced verbatim.
Resolved option state¶
ResolvedHotstringOptions is
a separate dataclass rather than a subclass of
HotstringOptions. Its fields
contain only concrete values; none are typed with
InheritedState.
ResolvedHotstringOptions.from_options()
constructs a resolved object from:
- one parsed
HotstringOptionsdeclaration; and - the fully resolved defaults applicable at that declaration position.
Explicit values override those defaults. InheritedState.INHERIT leaves the
corresponding default unchanged. This makes resolution context-sensitive
without making HotstringOptions itself aware of file position,
#Hotstring, or other sources of defaults.
Send mode¶
The declared SendMode enum represents the
three actual hotstring send-mode choices:
INPUT
PLAY
EVENT
The declaration mapping is:
| Hotstring option | Declared value |
|---|---|
SI |
SendMode.INPUT |
SP |
SendMode.PLAY |
SE |
SendMode.EVENT |
| omitted | InheritedState.INHERIT |
Input mode has two distinct effective fallback behaviors, so the resolved model
uses a separate four-state
ResolvedSendMode enum:
class ResolvedSendMode(Enum):
INPUT_WITH_PLAY_FALLBACK = auto()
INPUT_WITH_EVENT_FALLBACK = auto()
PLAY = auto()
EVENT = auto()
ResolvedHotstringOptions.send_mode is therefore typed as
ResolvedSendMode.
The important mappings are:
| Effective source | Resolved value |
|---|---|
explicit SI |
ResolvedSendMode.INPUT_WITH_PLAY_FALLBACK |
| AutoHotkey built-in default | ResolvedSendMode.INPUT_WITH_EVENT_FALLBACK |
SP |
ResolvedSendMode.PLAY |
SE |
ResolvedSendMode.EVENT |
InheritedState.INHERIT does not inherently mean
INPUT_WITH_EVENT_FALLBACK. It means to use the currently applicable hotstring
default. If inheritance eventually reaches AutoHotkey's untouched built-in
default, the resulting resolved value is
ResolvedSendMode.INPUT_WITH_EVENT_FALLBACK. If an applicable default has
already selected SI, SP, or SE, the inherited value resolves according
to that default instead.
Candidate hierarchy¶
The generic model hierarchy is:
Candidate hotstring class hierarchy
classDiagram
direction TB
Hotstring <|-- CandidateHotstring
CandidateHotstring <|-- AutoCorrect2CandidateHotstring
class CandidateHotstring {
<<abstract>>
}
CandidateHotstring stores the
semantic replacement and requires a concrete subclass to derive the
AutoHotkey content corresponding to that replacement. The public
Hotstring.render() contract remains
inherited unchanged, so the hierarchy does not narrow the method signature.
AutoCorrect2CandidateHotstring
supplies the AutoCorrect2-specific mapping: the replacement is converted to a
complete escaped AutoHotkey string literal and wrapped in f(...).