Add GB2312 encoding, and build both encoding lists from one source - #689
Merged
Conversation
added 2 commits
July 30, 2026 09:49
Closes #688. GB2312 (code page 936) is now offered both as the Preferences default encoding and as a row in View > Encoding, so a simplified-Chinese log can be read without relying on the machine default. The issue also reported "two utf-8 encoding". The encoding menu built its row labels from Encoding.<X>.HeaderName in the constructor, after the localized resource labels had been applied. Encoding.Default is UTF-8 on .NET, so the "ANSI" row rendered as "utf-8" next to the "UTF8" row, which rendered as "utf-8" as well - two rows applying the same encoding under the same name. The ANSI row is dropped (the Preferences combo lost the same duplicate in #673, for the same reason) and the HeaderName overwrite with it, so the remaining rows keep their translated labels. Row check state is now matched by code page rather than by runtime type and equality. That ordering had a defect of its own: clicking UTF8 applies UTF-8 without a BOM, which compares equal to Encoding.Default, so the checkmark landed on the ANSI row instead. Pinned by tests at every layer the choice passes through: the offered list, the menu check state, the settings JSON and .lxp round trips, the reader stack (GB2312 is the first offered encoding that is neither single-byte nor Unicode, so the direct reader's byte-position tracking is asserted against the system reader on mixed ASCII/Chinese lines).
The Preferences default-encoding combo box and the View > Encoding menu each carried their own list of encodings, and they had drifted: the menu lacked windows-1250 and windows-1252, and carried an "ANSI" row that the combo did not. Adding GB2312 for #688 meant editing both, plus a third place - the check-state lookup that maps the encoding a file is read with back onto a menu row. EncodingRegistry.OfferedEncodings is now the one list, and it documents the invariants both UIs depend on (resolvable by its own header name, no two entries sharing a code page, append rather than sort). Adding an encoding is a one-line change there. The menu rows are built from it at runtime by EncodingMenuBuilder rather than declared one by one in the designer. Each row carries the Encoding it stands for in its Tag, so the click handler applies whatever the clicked row carries (one handler, was five) and the check-state lookup compares against it (was a cascade naming every encoding). Rows are labelled with their header name, the same text the combo box shows, which retires five resource keys that were dead anyway - the constructor overwrote them before anyone saw them. Also fixes what GB2312 exposed in the legacy and XML readers, which advance their position per character by a per-encoding step: GetPosIncPrecomputed credited every encoding that is not UTF-8 or UTF-16 with one byte per character. GB2312 is the first offered encoding that is neither single-byte nor Unicode, so the position drifted on the first Chinese character and every later line began at the wrong offset. The step is now keyed on Encoding.IsSingleByte, and 0 ("measure the character") covers the variable-width case. Test coverage follows the seam: the list invariants are asserted once against the registry instead of per dialog, and the menu fixture is filled by the real builder, so it can no longer describe a menu the application does not build.
Comment on lines
+35
to
+49
| foreach (var encoding in EncodingRegistry.OfferedEncodings) | ||
| { | ||
| var row = new ToolStripMenuItem(encoding.HeaderName) | ||
| { | ||
| Name = RowName(encoding), | ||
| Tag = encoding, | ||
| // Carried over from the designer-declared rows, which set both on every encoding row. | ||
| BackColor = SystemColors.Control, | ||
| ForeColor = SystemColors.ControlDarkDark | ||
| }; | ||
|
|
||
| row.Click += onRowClick; | ||
|
|
||
| _ = encodingMenu.DropDownItems.Add(row); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #688.
What was reported
Both halves turned out to be real.
GB2312 was missing. It is now offered as the Preferences default encoding and as a row in View → Encoding.
The "two utf-8 encoding" reproduced.
LogTabWindow's constructor overwrote each encoding row's label withEncoding.<X>.HeaderName, after the localized resource labels had been applied.Encoding.Defaultis UTF-8 on .NET, so the "ANSI" row rendered asutf-8directly above the "UTF8" row, which rendered asutf-8too — two rows, same label, same encoding. The ANSI row is dropped (the Preferences combo lost the identical duplicate in #673, for the same reason) and theHeaderNameoverwrite with it.A third defect fell out while pinning the above: check state was matched by runtime type and equality, testing
Equals(Encoding.Default)beforeis UTF8Encoding. Clicking "UTF8" applies UTF-8 without a BOM, which compares equal toEncoding.Default— so the checkmark landed on the ANSI row.One offered-encoding list
The Preferences combo and the encoding menu each carried their own list, and they had drifted: the menu lacked windows-1250 and windows-1252, and carried the ANSI row Preferences did not. Adding an encoding meant editing both, plus a third place — the lookup mapping a file's encoding back onto a menu row.
EncodingRegistry.OfferedEncodingsis now the one list, documenting the invariants both UIs depend on (resolvable by its own header name, no two entries sharing a code page, append rather than sort). Adding an encoding is a one-line change there.Menu rows are built from it at runtime by
EncodingMenuBuilderinstead of being declared one-by-one in the designer. Each row carries theEncodingit stands for in itsTag, so the click handler applies whatever the clicked row carries (one handler, was five) and the check-state lookup compares against it (was a cascade naming every encoding, plus a field per row). Rows are labelled with their header name — the same text the combo shows — which retires five resource keys across en/de/zh-CN that were dead anyway, overwritten before anyone saw them.A bug GB2312 exposed
GetPosIncPrecomputedcredited every encoding that is not UTF-8 or UTF-16 with one byte per character. That is the step the legacy and XML readers advance their byte position by. GB2312 is the first offered encoding that is neither single-byte nor Unicode (one byte per ASCII character, two per Chinese one), so the position drifted on the first Chinese character and every later line began at the wrong offset — picking GB2312 withReaderType.Legacywould have produced a garbled file. The step is now keyed onEncoding.IsSingleByte, with0("measure the character") covering the variable-width case.Behaviour changes worth knowing
Encoding.Default, which on .NET is UTF-8 without a BOM — byte-identical to what the surviving UTF-8 row applies.us-ascii/iso-8859-1/utf-8/utf-16/windows-1250/windows-1252/gb2312instead ofASCII/ANSI/ISO-8859-1/UTF8/Unicode, matching the Preferences combo.Testing
1557 tests pass, build clean with no new warnings. Tests were written red-first at each layer the choice passes through: the offered list, menu check state, the settings JSON and
.lxpround trips, and the reader stack — where the direct reader's byte positions are asserted against the system reader on mixed ASCII/Chinese lines, and the legacy reader's per-line positions againstGetByteCount. Both position tests were confirmed to fail without theGetPosIncPrecomputedfix.The list invariants are asserted once against the registry rather than per dialog, and the controller's menu fixture is filled by the real builder, so it cannot describe a menu the application does not build.
Note: code page 936 on Windows is really GBK, a GB2312 superset — it decodes strictly more than requested, so nothing is at risk.