Repository navigation
Conversation
Groundwork for the Lexicon menu PRs that follow: each of them will add answerers to the authority introduced here and switch over the menus those answerers complete, so this change defines the framework they will build on. The bridge now resolves an owned menu id through the XWindow's loaded configuration and walks the menu XML itself. Until now an owned id was still populated through xCore's ChoiceGroup, which kept every authority coupled to ChoiceBase (and so to a live XWindow) and tied the hidden-adapter retirement to xCore's population rules. The earlier authorities never needed a menu definition of their own: each owns a fixed list of menu ids and only answers a leaf it is handed. The new LexiconMenuAuthority owns by predicate instead, deciding each id when asked, from the menu's contents: every leaf's message must have an answerer, and the menu may carry neither a list submenu nor an item no authority can answer. That decision has to be made before any menu is built and from the same tree the bridge then renders. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1177 +/- ##
==========================================
+ Coverage 39.33% 39.51% +0.18%
==========================================
Files 1533 1537 +4
Lines 353646 354685 +1039
Branches 40869 41092 +223
==========================================
+ Hits 139094 140160 +1066
+ Misses 185220 185137 -83
- Partials 29332 29388 +56
🚀 New features to boost your workflow:
|
|
One thing to share @mark-sil . Up to you if you think it needs addressing in this pull request.
|
thejambi
left a comment
There was a problem hiding this comment.
and you can consider addressing the other comment I left.
@thejambi reviewed 15 files and all commit messages, made 1 comment, and resolved 1 discussion.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on mark-sil).
The menu walk now marks a list at the root of a menu as inline, so its items splice into the menu as ChoiceGroup.PopulateFromList shows them, instead of nesting under the menu's label. No shipped context menu has a list at its root, and no authority owns a menu with a list, so nothing rendered differently; the comment beside the case also cited IsAListGroup, which tests the behavior attribute rather than list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Thanks, both points are right: the root list is now marked inline so its items splice flat, and the comment cites |
thejambi
left a comment
There was a problem hiding this comment.
@thejambi reviewed 1 file and all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on mark-sil).
Start here:
ResolveMenuandConvertOwnedinSrc/xWorks/Avalonia/Hosting/XCoreMenuBridge.cs, the walk that replacesChoiceGroupon owned menus. ThenLexiconMenuAuthority.Owns, the predicate the next PRs feed.What it does. Stage 2, PR 1 of the hidden-DataTree retirement: the groundwork the Lexicon menu PRs build on. An owned menu id is now built from its menu XML through the XWindow's loaded configuration and command set; no
ChoiceGroup, noChoiceBaseand no mediator call on an owned path.IDetailMenuAuthority.Buildtakes aDetailMenuLeaf(the resolvedCommand, its message and label); the five shipped authorities migrated.LexiconMenuAuthorityarrives with no answerers and owns by predicate: every leaf's message answered, no list submenu, nothing unanswerable. The owned-population hook #1153 added toChoiceGroupis removed. No user-visible change is intended.The question to settle. Does the walk see what
ChoiceGroup.Populatesaw? A test compares the two for every context-menu id in the window configuration: top-level leaves equal by id and label, nested leaves a subset (the mediator path drops a submenu no colleague shows), list submenus equal. It found two things xCore tolerates until display that the walk now tolerates too: property-toggle items (PaneBar-*,mnuBrowseHeader) and an item naming a command that is commented out (mnuDataTree-FeatureStructure-Feature, Grammar). Both becomeUnanswerableentries, so no authority can own such a menu and it stays on the mediator path.Where to look.
ResolveItem: the precedence ofChoiceBase.Make, item label over command label, localized the same way.ConvertOwned: the LT-8791 empty-submenu drop, inline splice, list viaBuildListand separator trimming, pinned by the existing owned-menu tests.LexiconMenuAuthority.Owns: the pinned owned-id list is empty;LexiconMenuAuthorityTestsflips the predicate with fake answerers.ChoiceGroup: a pure revert of the LT-22691: Let a menu authority answer a whole menu id, submenus included #1153 threading.Mediator.CommandSetis read as a registry; the no-mediator spy tests still pass.Deliberately not here. Any answerer (PRs A, B and C1 follow). A native property-toggle answerer. Caching resolved menus per window. A shared
LoadRecordEditViewtest helper.Verification. Build and both hygiene gates clean. 76 Hosting fixture tests and 19 xCoreInterfaces tests green. Manual parity pass against the WinForms menus in Lexicon Edit: owned label menus, chip menus, writing-system toggles, and the Lexeme Form and sense-header menus still on the mediator path. Not run: the full suite; a localized UI, since only
strings-en.xmlships.Next: approve, or say if the
ChoiceGrouprevert should be its own PR.Reading this a year from now -- start here
This PR is stage 2, PR 1 of the plan that retires the hidden WinForms
DataTreethe Avalonia detail view keeps alive to answer context-menu commands. Stage 1 answered six ids with five authorities that own by hard-coded id:mnuReorderVector(#1143),mnuDataTree-Objectand-Help(#1161),mnuReferenceChoices(#1172),mnuDataTree-MultiStringSlice(#1169),mnuEnvReferenceChoicesand the two environment insert menus (#1175). Stage 2 answers the ~30 Lexicon menus through one authority parameterized by each command's message and XML parameters; PRs A, B and C1 add its first answerers, C2, D1 and D2 follow, and E finishes withmnuDataTree-Sense. The plan of record lives in gitignored working notes underDocs/migration/working/, so the reasoning is recorded here rather than in the tree.Decisions, and why
Command lookup stays on
Mediator.CommandSet. It is a hashtable built once at window load, read exactly asCommandChoice.CommandObjectreads it; no message is sent. TheCmObjectUijump methods take aCommand, so the resolved command has to reachReferenceItemMenuAuthorityregardless.Buildtakes aDetailMenuLeafcarrying theCommand. The cluster keys answers on the command's message and<parameters>, and the reference-item authority needs theCommandobject itself; a command id and label alone would not have served either.Owns(string)and the staticOwnsAllare unchanged. The bridge exposes the resolved tree and the cluster authority takes a resolver delegate at construction. ChangingOwnsto take the menu node would have touched every authority and forced the window intoOwnsAll.The predicate is message-level. An answerer that cannot act on the current row returns a disabled item, which is the disable-and-log rule already in force. Row-conditional ownership stays where it already lives, on the multi-string authority.
The walk tolerates what
ChoiceGrouptolerates. A property item, an undefined command or an unknown element becomes anUnanswerableentry rather than an exception, becauseOwnsnow runs the walk for every unowned id a row binds, and a throw there would remove the whole row menu where today the menu still shows. Only a truly malformed<item>throws, asChoiceBase.Makedoes.Test files follow the code, not the PR schedule.
LexiconMenuAuthorityTests.csholds the pinned owned-id list and the cluster's contracts; rule classes keep one test file each underFdoUiTests/DetailRules/; aLexiconMenuExecutionTests.csarrives with the first end-to-end mutation check. The five migrations stayed inDetailObjectCommandExecutionTests.cs.Paths not taken
Owns(menuId, menuNode). Rejected: every authority andOwnsAllwould change for the benefit of one.ChoiceGroupfor menus nobody owns.ConfigurationExceptioninsideOwns. Rejected: exceptions as flow control, and it would have hidden a genuinely unknown menu id.CmdDataTree-Delete-FeatureStructure-Feature. Not done: commented out since before the truncated history; the author ruled no ticket and no change.Preflight review details
Code Review Summary
Branch: LT-22691m
Base: main (merge base dc0004c, 1 commit ahead)
Date: 2026-10-08
Review model: Claude Fable 5.1 (Claude Code)
Files changed: 15
Overview
Stage 2, PR 1 of the hidden-DataTree menu-id retirement (LT-22691): groundwork the
Lexicon menu PRs build on. An owned context-menu id is now built from its menu XML
through the XWindow's loaded configuration and command set, with no
ChoiceGroup,no
ChoiceBaseand no mediator call on an owned path.IDetailMenuAuthority.Buildtakes a
DetailMenuLeaf; the five shipped authorities migrated.LexiconMenuAuthorityarrives with no answerers and owns by predicate. The owned-population hook #1153 added
to
ChoiceGroupis removed. No user-visible behaviour change is intended; the authorconfirmed parity manually against the WinForms menus.
The analysis found no Critical or Important issue. The equivalence test over every
context-menu id surfaced two configuration facts the walk now tolerates as xCore does:
property-toggle items in
PaneBar-*andmnuBrowseHeader, and an item inmnuDataTree-FeatureStructure-Featurenaming a command that is commented out inGrammar/DataTreeInclude.xml. The author ruled the latter needs no ticket or change.Contract/API Changes
IDetailMenuAuthority.Build(string, ChoiceBase)->Build(string, DetailMenuLeaf).All implementers are in xWorks and xWorksTests; updated.
ChoiceGroup.PopulateNow(bool)(public, added by LT-22691: Let a menu authority answer a whole menu id, submenus included #1153 on 2026-09-25) removed. Nocaller remains in the repository; the parameterless
PopulateNow()is unchanged.DetailMenuLeaf,DetailMenuDefinition,DetailMenuEntry;new public
XCoreMenuBridge.ResolveMenu(XWindow, string).Findings
Critical - Must address before merge
None.
Important - Should address before merge
None.
Minor - Consider
(deferred: a structured kindDetailMenuDefinition.Unanswerableis free text that the tests parse(
UndefinedCommandId, the "property" prefix filter)would remove the coupling; the dependent tests fail loudly if the text changes, so
nothing breaks silently)
(deferred: configuration is immutable per window, so a per-window cache isLexiconMenuAuthorityre-walks the XML for every unowned id on every menurequest
possible; the cost is one XML walk per id per right-click and is not measurable today)
(deferred: theLoadRecordEditViewis copied into a seventh Hosting fixtureshared home is
XWorksAppTestBase; out of scope for a groundwork PR)ResolveEntriesthrew for any child element other thanitemormenu(fixed during review: an unknown element becomes an
Unanswerableentry so themenu stays on the mediator path, as
ChoiceGroupkeeps it)ResolveMenuignored alistattribute on the root menu node (fixed duringreview: a list-populated root is one list entry; after Zach's review comment it is
marked inline so its items splice flat, as
ChoiceGroup.PopulateFromListshows them,and the comment no longer cites
IsAListGroup, which testsbehavior, notlist)ReferenceItemMenuAuthority.ToolOfindexedParameters[0]with no nullcheck (fixed during review: a command with no parameters element yields null)
Required Validation / Evidence
Run:
.\build.ps1 -CommentHygiene -TokenHygiene: clean..\test.ps1 -TestProject Src/xWorks/xWorksTests -TestFilteroverDetailObjectCommandExecutionTests,LexiconMenuAuthorityTests,DetailEditorParityTests,DetailCommandAdapterHardeningTests,DetailWritingSystemStateTests: 76 passed, 0 failed..\test.ps1 -TestProject Src/XCore/xCoreInterfaces/xCoreInterfacesTests: 19 passed.WinForms menus: owned label menus (multi-string rows, Scientific Name, Semantic
Domains, Complex Forms, Subentries, allomorph Environments), chip menus, writing-system
toggles, and the Lexeme Form and sense-header controls on the mediator path.
Not run: the full test suite; a localized UI (only
strings-en.xmlships), so labellocalization through the walk is covered by code equivalence only.
Positive Observations
ResolveMenu_ReadsWhatChoiceGroupPopulates_ForEveryContextMenuIdcompares the walkwith
ChoiceGroup.Populatefor every context-menu id in the window configuration.LexiconMenuAuthorityTestsmakes each later PR's diff show themenus it switched over.
ChoiceGroupchange is a pure revert of the LT-22691: Let a menu authority answer a whole menu id, submenus included #1153 threading.Interview Notes
CreateMenuAuthoritylists one authority per line with a matching summary; the "No answerer exists yet"
sentence was removed from
LexiconMenuAuthority.Mediator.CommandSet(a registry read, not a message);Buildtakes a leaf carryingthe resolved
Command;Ownskeeps its signature and the cluster authority takes aresolver delegate; the predicate is message-level; the authority is named
LexiconMenuAuthority; test files follow the code (LexiconMenuAuthorityTests.csnow,
LexiconMenuExecutionTests.cswhen a track needs it).CmdDataTree-Delete-FeatureStructure-Featurereference: it has been that way sincebefore the truncated history.
In-Review Quality Check
The three review fixes were made before the commit; build, hygiene gates and the
targeted tests were rerun after them (76 and 19 passed).
Suggested Review Focus
XCoreMenuBridge.ResolveItemreproducesChoiceBase.Makeprecedence and labellocalization.
XCoreMenuBridge.ConvertOwnedkeeps the LT-8791 empty-submenu drop, inline spliceand separator trimming.
LexiconMenuAuthority.Ownsdeclines a menu with a list submenu or an unanswerableitem.
ChoiceGroupdiff is a revert and nothing else.🤖 Generated with Claude Code
This change is