Skip to content

#2004 Build the whole macro line from the Tab menu - #2007

Merged
TimothyLuke merged 3 commits into
TimothyLuke:masterfrom
LarryThiessen:feat-2004-tab-line-builder
Aug 23, 2026
Merged

#2004 Build the whole macro line from the Tab menu#2007
TimothyLuke merged 3 commits into
TimothyLuke:masterfrom
LarryThiessen:feat-2004-tab-line-builder

Conversation

@LarryThiessen

Copy link
Copy Markdown
Contributor

Fixes #2004

Tab in a macro block opened a flat list of spellbook spells and GSE variables and pasted a name at the caret. It saved typing one word; the author still wrote every command, bracket, conditional and reset= by hand — and that is where the mistakes are.

Tab now opens a line builder. The menu stays open while the line is assembled, each pick lands in its canonical place — command > conditionals > reset= > spells — whatever order they are picked in, and entries that would produce an invalid line are greyed with a tooltip saying why.

Undo Last / Boiler Plates / Macros / GSE Variables / Commands /
Conditionals / Reset / Spells / ; / , nil
  • Conditionals accumulate into one always-closed bracket group, rewritten in place ([a][a,b]), with same-family collapse (spec:1 + spec:2spec:1/2) and duplicates deduped. [] closes the run ([stuff][], or /cast []).
  • Reset only on a /castsequence line; values slash-collapse and a single number is kept — a second numeric pick replaces it.
  • ; opens a fresh clause on the same command with its own conditionals and its own reset=; offered only after a spell.
  • , nil only at the end of a clause that has its own reset= and already ends with a spell.
  • Boiler Plates — common whole lines, inserted as written.
  • Macros / GSE Variables stay mutually exclusive with macro text, in both directions.
  • Undo Last walks back a misclick pick by pick.
  • Re-opening the menu on an existing line adopts its group and reset= instead of duplicating them.

Two things make it robust rather than clever:

The text is the single source of truth. Clause state is re-derived from the live text before every conditional and reset pick, so picks that end a clause — or the author typing between picks — cannot leave stale offsets behind. Every read decodes the box first, so a repaint mid-build cannot shift offsets or blind a gate, and edits splice via SetText at tracked offsets rather than trusting the caret of an unfocused box.

Nothing depends on the menu's lifecycle. Blizzard menus release and re-acquire during submenu navigation, and MenuResponse.Refresh does not re-run the root generator, so any pick that changes a gate closes and reopens the menu. The menu is opened through the manager with an explicit anchor on the macro box (MenuManagerMixin:OpenMenu, public via the proxy), so those reopens do not walk it across the screen the way cursor anchoring did — with a fallback to the cursor path if that API is absent.

Editor.lua carries one change: the focus-loss commit repaint is deferred while a session is live, so it cannot recolour the box mid-build. It runs on the next real focus loss once the menu is gone.

Testing

  • luac -p clean on all three files.
  • Placement, collapse and gating rules exercised offline against a standalone Lua build of the same logic (16 cases: merge into an existing group, same-family collapse, duplicate no-op, new group after ;, second conditional into a ; clause's group, reset= into the right clause, number replacement, slash collapse, [] across multi-group and already-closed lines).
  • Soak-tested in a running client through many rounds of live building.

🤖 Generated with Claude Code

LarryThiessen and others added 3 commits August 22, 2026 23:19
Tab in a macro block opened a flat list of spellbook spells and GSE
variables and pasted a name at the caret. It saved typing one word; the
author still wrote every command, bracket, conditional and reset= by
hand.

Tab now opens a line builder. The menu stays open while the line is
assembled, each pick lands in its canonical place -- command,
conditionals, reset=, spells -- whatever order they are picked in, and
entries that would produce an invalid line are greyed with a tooltip
saying why.

  Undo Last / Boiler Plates / Macros / GSE Variables / Commands /
  Conditionals / Reset / Spells / ; / , nil

Conditionals accumulate into one always-closed bracket group, rewritten
in place ([a] -> [a,b]), with same-family collapse (spec:1 + spec:2 ->
spec:1/2) and duplicate picks deduped; [] closes the run. Reset is
offered only on a /castsequence line, slash-collapses its values and
keeps a single number. ";" opens a fresh clause with its own conditionals
and reset=, and ", nil" is offered only at the end of a clause that has
its own reset= and ends with a spell. Macros and GSE Variables stay
mutually exclusive with macro text, in both directions.

Two things make it robust rather than clever:

  * The text is the single source of truth. The clause state is
    re-derived from the live text before every conditional and reset
    pick, so picks that end a clause -- or the author typing between
    picks -- cannot leave stale offsets behind. Every read decodes the
    box first, so a repaint mid-build cannot shift offsets or blind a
    gate, and edits splice via SetText at tracked offsets rather than
    trusting the caret of an unfocused box.

  * Nothing depends on the menu's lifecycle. Blizzard menus release and
    re-acquire during submenu navigation, and MenuResponse.Refresh does
    not re-run the root generator, so any pick that changes a gate closes
    and reopens the menu instead. The menu is opened through the manager
    with an explicit anchor on the macro box, so those reopens do not
    walk it across the screen the way cursor anchoring did.

Editor.lua defers its focus-loss commit repaint while a session is live,
so it cannot recolour the box mid-build; it runs on the next real focus
loss once the menu is gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luacheck reported 28 undefined globals in QoL.lua: the line builder
reaches past MenuUtil (already allowed) to MenuResponse for its pick
return values, and to Menu / MenuVariants / AnchorUtil for the anchored
open, plus GameTooltip_AddNormalLine for the greyed entries' tooltips.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tabbing on a fresh block put the command on a second line: the pick asks
rowBeforeCaret() whether the current row is empty, and on a new block
that row is the "Need Stuff Here" placeholder -- text, so the command
was prefixed with a newline. The splice then replaces the placeholder
outright, leaving that newline stranded at the top of the block.

The placeholder is not content, so report the row as empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TimothyLuke
TimothyLuke merged commit eed7516 into TimothyLuke:master Aug 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tab in the macro-commands box: build the whole line, not just insert a spell name

2 participants