Skip to content

Fix Korean IME overlay hiding characters to the right of cursor#20041

Open
drvoss wants to merge 3 commits intomicrosoft:mainfrom
drvoss:fix/korean-ime-composition-insert-rendering
Open

Fix Korean IME overlay hiding characters to the right of cursor#20041
drvoss wants to merge 3 commits intomicrosoft:mainfrom
drvoss:fix/korean-ime-composition-insert-rendering

Conversation

@drvoss
Copy link
Copy Markdown
Contributor

@drvoss drvoss commented Mar 31, 2026

This commit fixes a regression in v1.24 where composing a Korean
character between two existing characters caused the character
immediately to the right of the cursor to be visually hidden during the
composition.

The tsfPreview overlay in _PaintBufferOutput writes the in-progress
composition text at the cursor column using ReplaceText
(overwrite-style). This is correct for Chinese/Japanese IME (which
replaces a candidate region), but wrong for Korean IME, which inserts
the composed syllable between existing characters.

The fix walks the original row (saved in scratch) from the cursor
position forward, absorbing up to W whitespace columns (where W =
composition width in columns) and copying non-whitespace glyphs to the
right of the composition text. This renders the composition as an
insertion while preserving TUI box borders when trailing whitespace
padding is present.

Key properties of the algorithm:

  • Whitespace budget: exactly W columns of whitespace may be
    absorbed.
  • Early exit: when the budget reaches 0, srcCol == dstCol and the
    remaining content in r is already correct, so the loop terminates
    immediately.
  • Wide character aware: NavigateToNext handles 2-column Korean/CJK
    glyphs correctly.
  • Visual-only: r is fully restored after each frame via
    wil::scope_exit.

Example:

Original row:  가 나 □ □ │   (□=space, │=TUI border, composition width W=2)
Without fix:   가 [ㄷ]       나 hidden
With fix 2-1:  가 [ㄷ] 나 │  (2 spaces absorbed, border stays)

Validation Steps Performed

  1. Korean IME: compose between and → display shows 가ㄷ나, visible.
  2. TUI box (vim prompt with padding): compose inside padded text box → border preserved in place.
  3. Composition at end of line (no chars to right): unaffected.
  4. English and other IME input: not affected (no active composition row).

Closes #20040
Refs #19738
Refs #20039

drvoss added 3 commits March 30, 2026 17:23
…cursor

When composing a new character between two existing characters, the in-progress
composition text was drawn over the character immediately to the right of the
cursor, visually hiding it during composition. This happened because the
tsfPreview overlay used an overwrite-style render, writing the composition text
at the cursor column without preserving the displaced content.

The fix copies the original characters from that position back into the visual
row at the position just after the composition text ends, changing the
appearance from overwrite to insert. The buffer itself is not modified; the
change is local to the temporary row modification that is restored after each
paint frame.

This regression was introduced by commit a3d508a (Remove
TF_TMAE_UIELEMENTENABLEDONLY, microsoft#19738), which caused the Korean IME to switch
from IMM32 (which renders its own floating composition window) to TSF (which
uses the terminal's inline tsfPreview rendering), exposing this rendering flaw.
Reverts the change that shifted existing characters to the right of the
cursor when rendering an in-progress TSF composition (tsfPreview). The
approach was an unconditional rightward shift of all content from the
cursor position, which caused box-drawing characters in TUI applications
to be displaced and rendered at wrong positions or cut off entirely.

Example of the breakage in a TUI text box:
  Before (correct):  |Hello [ㄷ]     |
  After  (broken):   |Hello [ㄷ]Worl|d  (border shifted off screen)

The correct fix requires consuming an equal amount of whitespace to the
right of the composition before shifting, so that box borders remain at
their original column positions when trailing whitespace is available.
This improved approach will be implemented separately as Fix 2-1.

Reviewer feedback:
  When you're in a TUI application, there may be a box around the text
  field. Shifting text from the cursor to the right means that the right
  border (and anything else in the TUI app) also shifts to the right.
  One solution for this is that we try to remove an equal amount of
  whitespace to the right of the composition.
…cursor

When composing a new Korean character between existing text, the in-progress
composition overlay was drawn using an overwrite-style render that hid any
character immediately to the right of the cursor. This is visually incorrect
for Korean IME, which inserts rather than replaces.

The fix copies original characters from the cursor position back to the right
of the composition text, using a whitespace-absorption algorithm so that TUI
box borders stay at their original column positions when trailing whitespace
is available. Up to W whitespace columns (where W is the composition width)
are consumed silently; remaining non-whitespace content is shifted rightward.
If no whitespace is available, the shift is unavoidable (border may move),
but this is the best achievable behavior for fixed-width lines.

The rendering change is purely visual: the buffer row is restored in full
after each paint frame by the existing wil::scope_exit restore mechanism.

This approach was suggested by a reviewer as a refinement to a prior attempt
that shifted all content unconditionally and broke TUI box borders.

Closes microsoft#20040
@DHowett
Copy link
Copy Markdown
Member

DHowett commented Mar 31, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@DHowett DHowett changed the title Fix Korean IME composition overlay hiding characters to the right of cursor Fix Korean IME overlay hiding characters to the right of cursor Mar 31, 2026
Copy link
Copy Markdown
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compositionRow code is now rather lengthy with this new code. I think it makes sense to hoist it into its own dedicated function if possible. But the changes overall look good to me. 🙂

Let me think about this over the next few days! I'll check this PR out locally.

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.

Korean IME (v1.24+): Composing character visually hides the character to its right

3 participants