Skip to content

Scope IME display attribute enumeration to the edited ranges - #11872

Open
akon47 wants to merge 1 commit into
dotnet:mainfrom
akon47:fix/ime-display-attribute-enum-scope
Open

Scope IME display attribute enumeration to the edited ranges#11872
akon47 wants to merge 1 commit into
dotnet:mainfrom
akon47:fix/ime-display-attribute-enum-scope

Conversation

@akon47

@akon47 akon47 commented Aug 27, 2026

Copy link
Copy Markdown

Fixes the typing lag reported in #7397.

Cause

TextServicesDisplayAttributePropertyRanges.OnEndEdit enumerates the display attribute property with a null target range, which enumerates it over the whole document:

displayAttributeProperty.EnumRanges(ecReadOnly, out attributeRangeEnumerator, null);

The property accumulates a range per previously composed run, so both the enumeration and the COM round trip it costs per range grow with document length — on every keystroke.

Only IMEs that set display attributes hit this (for example the modern Korean TSF IME, which underlines the composition). Measured on a 1,200 character RichTextBox, a single OnEndEdit enumerated 1,077 ranges of which exactly 1 carried an attribute, blocking the UI thread for ~3 seconds.

Fix

Use GetPropertyUpdate(editRecord) to obtain the ranges this edit actually changed and enumerate the property within each of them — which is what the base class TextServicesPropertyRanges.OnEndEdit already does. This override had lost that scoping.

GetPropertyUpdate is promoted from private to protected so the override can reuse it.

Measurement

SendInput macro typing 안녕하세요 300 times into a RichTextBox (Windows 11 10.0.26200, .NET 8, x64 Release):

mean / iteration max UI thread stalls
before 181.3 ms 3577.6 ms 13, totalling 15,898 ms
after 100.9 ms 127.4 ms 1, of 53 ms
legacy IME (reference) 101.5 ms 173.1 ms none

The same harness with the IME in English mode showed no stalls before or after, confirming the cost is in the composition path.

Composition underline and the Hanja candidate window continue to render correctly, including mid-document, while scrolled, and immediately after fast typing.

Notes

  • The same code is present on .NET Framework (PresentationFramework 4.8.9340.0), consistent with the original report that 4.8 is affected.
  • Root cause was located with WPF's own IME tracer (IMECompositionTraceTarget) plus stack sampling during the stalls; analysis was done with the help of Claude Code, and every number above is a measurement from an actual run on my machine.
Microsoft Reviewers: Open in CodeFlow

TextServicesDisplayAttributePropertyRanges.OnEndEdit enumerated the display
attribute property with a null target range, which enumerates the property
over the entire document. The property accumulates a range per previously
composed run, so both the enumeration and the COM round trip it costs per
range grow with the length of the document - on every keystroke.

This only shows up with IMEs that set display attributes (for example the
modern Korean TSF IME, which underlines the composition). Measured on a
1,200 character RichTextBox while typing with that IME, a single OnEndEdit
call enumerated 1,077 ranges of which exactly one carried an attribute, and
blocked the UI thread for about 3 seconds.

Use GetPropertyUpdate(editRecord) to obtain the ranges this edit actually
changed and enumerate the property within each of them, which is what the
base class TextServicesPropertyRanges.OnEndEdit already does; this override
had lost that scoping. GetPropertyUpdate is promoted from private to
protected so the override can reuse it.

Measured with an automated SendInput macro typing "안녕하세요 " 300 times
into a RichTextBox (Windows 11 26200, .NET 8, x64 Release):

  before: mean 181.3 ms/iteration, max 3577.6 ms,
          13 UI thread stalls totalling 15,898 ms
  after:  mean 100.9 ms/iteration, max  127.4 ms,
          1 stall of 53 ms

which matches the legacy IME baseline (mean 101.5 ms, no stalls) on the same
harness. The composition underline and the Hanja candidate window continue to
render correctly.

Contributes to dotnet#7397
@akon47
akon47 requested a review from a team August 27, 2026 04:23
@akon47
akon47 requested a review from a team as a code owner August 27, 2026 04:23
@dotnet-policy-service dotnet-policy-service Bot added PR metadata: Label to tag PRs, to facilitate with triage Community Contribution A label for all community Contributions labels Aug 27, 2026
@akon47

akon47 commented Aug 27, 2026

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution A label for all community Contributions PR metadata: Label to tag PRs, to facilitate with triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant