Skip to content

Add every new custom list to the Lists area, not just the last one - #1075

Draft
jasonleenaylor wants to merge 1 commit into
mainfrom
bugfix/lists-area-custom-list-refresh
Draft

Add every new custom list to the Lists area, not just the last one#1075
jasonleenaylor wants to merge 1 commit into
mainfrom
bugfix/lists-area-custom-list-refresh

Conversation

@jasonleenaylor

@jasonleenaylor jasonleenaylor commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The Lists area compared a cached count of ownerless lists against the current one and,
when they differed, appended a single list picked by index. It now compares the full set
of ownerless lists against the tool nodes already in the window configuration, matching on
list guid, and adds only the ones that are missing.

Tools whose list no longer exists are skipped when the display is filled, so a list deleted
in one main window does not break the sidebar in another.

Why it failed

FillListAreaList polled: it counted the ownerless lists and compared that against a cached
m_ccustomLists. On a difference it added customLists[customLists.Count - 1] — one list,
chosen by index. Two problems followed.

Any caller creating more than one unowned list in a session only got one of them in the
sidebar. PrepFLExDBDll/Preparer.cs creates two and ParserCore/XAmplePropertiesPreparer.cs
creates a third, so that is reachable in normal use. The index was unsafe as well:
AllInstances() is backed by a HashSet, so enumeration order is unspecified and the "last"
element is not the newest list.

Because the counter only advanced by one, the counts stayed mismatched and the next refresh
re-added the same list. The tool, clerk, command and context-menu nodes are all appended with
no duplicate guard, so the configuration accumulated duplicates — and since CommandSet is a
Hashtable, the duplicate command id made the third refresh throw
ArgumentException: Item has already been added.

Both counters are gone. The set difference answers the same questions directly, and the guid
key does not depend on enumeration order or on list names, which can collide.

The deletion guard

Deleting a custom list already works and rebuilds the window it was deleted in, which reloads
the configuration from disk. But PropertyTable, Mediator and WindowConfiguration are
per main window, and ReloadAreaTools replaces only the active one. With a second main window
open, that window keeps a tool node whose guid no longer resolves, and the next sidebar refresh
throws KeyNotFoundException out of GetListByGuid.

Five lines skip tool nodes whose list is gone. Pruning the stale nodes properly would mean
disposing the clerk, unregistering the command, and switching away from the active tool — all
of which only matter in the window that is being torn down anyway, so this fix deliberately
stops at the display.

Verification

.\test.ps1 -SkipNative -TestProject "Src/LexText/LexTextDll/LexTextDllTests" — 6/6 pass,
build clean.

Each new test was run against the unfixed code and fails there:

Test Failure without the fix
FillListAreaList_AddsEveryListCreatedSinceLastRefresh Expected: < "SecondListEdit", "ThirdListEdit" > But was: < "ThirdListEdit" >
FillListAreaList_RepeatedRefresh_DoesNotDuplicateConfigNodes ArgumentException : Item has already been added. Key in dictionary: 'CmdJumpToThirdListList'
FillListAreaList_ListDeletedInAnotherWindow_DropsItsTool KeyNotFoundException : Key 9c0d3612-… not found in identity map

Notes for review

  • This issue was discovered when working out why unowned custom lists created by Preparer.cs and XAmplePropertiesPreparer.cswere requiring a full FieldWorks reload to appear.
  • The upstream cause is that liblcm issues no PropChanged when an unowned object is created,
    tracked as Creating an unowned CmPossibilityList issues no PropChanged liblcm#397. Detection here is still poll-on-display.
  • AreaListener.GetToolForList has the same stale-guid exposure through RecordList.cs
    case "unowned", which calls GetObject with no Try variant. Left alone — RecordList.Init
    asserts the result is non-null, so relaxing that shared API is a different risk profile. Worth
    its own ticket.

🤖 Generated with Claude Code


This change is Reviewable

The Lists area compared a cached count of ownerless lists against the
current one and, when they differed, appended a single list picked by
index. It now compares the full set of ownerless lists against the tool
nodes already in the window configuration, matching on list guid, and
adds only the ones that are missing.

Tools whose list no longer exists are skipped when the display is
filled, so a list deleted in one main window does not break the sidebar
in another.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

NUnit Tests

    1 files      1 suites   11m 33s ⏱️
5 781 tests 5 700 ✅ 81 💤 0 ❌
5 790 runs  5 709 ✅ 81 💤 0 ❌

Results for commit 7e704c3.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.79487% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.10%. Comparing base (949453d) to head (7e704c3).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
Src/LexText/LexTextDll/AreaListener.cs 71.79% 4 Missing and 7 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1075      +/-   ##
==========================================
+ Coverage   38.05%   38.10%   +0.04%     
==========================================
  Files        1499     1499              
  Lines      350128   350147      +19     
  Branches    40239    40244       +5     
==========================================
+ Hits       133232   133412     +180     
+ Misses     187609   187458     -151     
+ Partials    29287    29277      -10     
Files with missing lines Coverage Δ
Src/LexText/LexTextDll/AreaListener.cs 46.16% <71.79%> (+13.00%) ⬆️

... and 124 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants