#14797

Korean IME: Backspace during Hangul composition fails to delete the last remaining jamo

Openbo2themaxopened 9d ago

1.3.1 works correctly, so there's a regression that needs to be identified.

Please rerun the tests mentioned in https://github.com/ghostty-org/ghostty/pull/7121 to make sure it doesn't affect others.

Discussed in https://github.com/ghostty-org/ghostty/discussions/13439

Originally posted by shp7724 July 24, 2026

Issue Description

When composing a Hangul syllable with the macOS built-in 2-Set Korean IME, pressing Backspace decomposes the syllable jamo-by-jamo as expected — until only the initial consonant remains. The final Backspace that should delete that last jamo is ignored, leaving the character stuck in the preedit.

This happens directly at the shell prompt as well as inside TUI apps, so it is not app-specific. This machine reproduces it on the latest tip; my previous machine on an older tip build does not (see regression range below).

Expected Behavior

Example: type 안 (jamo sequence ㅇ + ㅏ + ㄴ), then press Backspace 3 times:

  • Expected: 안 → 아 → ㅇ → (empty)

Actual Behavior

  • Actual: 안 → 아 → ㅇ → ㅇ (last Backspace is ignored; the initial consonant is not deleted)

Reproduction Steps

https://github.com/user-attachments/assets/be1d48d5-4516-49ba-8c6b-998420dd26f3

Ghostty Logs

No response

Ghostty Version

### Regression Range

- **Good:** `1.3.2-main+dcc39dcd4` (commit `dcc39dcd4019`, 2026-04-19) — Backspace fully decomposes the syllable, last jamo deletes fine.
- **Bad:** `1.3.2-main+b14d92383` (commit `b14d9238366f`, 2026-07-08) — last jamo cannot be deleted. Still reproduces on latest main.

OS Version Information

macOS 26.5

(Linux only) Display Server

None

(Linux only) Desktop Environment/Window Manager

No response

Minimal Ghostty Configuration

Reproducible with empty configuration

Additional Relevant Configuration

No response

I acknowledge that:

  • I have reviewed the FAQ and confirm that my issue is NOT among them.
  • I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion.
  • I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines.

Activity

6
khuddite7d ago

i am taking a look at the issue 👀

oneqit2d ago

Still reproducible on Ghostty 1.4.0 (stable) on macOS 15.6.1.

jonghyuk-kim2d ago

Also reproduces on macOS 27.0.1 (26A434), Apple Silicon, with Ghostty 1.4.0 (stable) and the built-in 2-Set Korean input source.

Same behavior as described: Backspace removes jamo one at a time, but when only the initial consonant is left in the preedit, the next Backspace does nothing and a second press is needed to remove it.

Seen inside a TUI app (Claude Code). It worked as expected before I updated to 1.4.0.

tobedoit2d ago

Also reproduces on Ghostty 1.4.0 (stable), macOS 27, 2-Set Korean. 전 → 저 → ㅈ, then the next Backspace is swallowed (ㅈ stays in preedit). Worked on 1.3.1.

coldplay1262d ago

Also reproduces on Ghostty 1.4.0 (stable), macOS 27, built-in 2-Set Korean, both at a plain zsh prompt and inside TUIs (omp, Claude Code), with and without tmux.

One extra data point that may help locate it: the last jamo is not just left in the preedit — it is committed to the PTY as text. I logged the bytes an app receives for 하 ㅌ ㅌ Backspace Backspace:

KeyScreenBytes the app received
ㅌ (2nd, composing)핱 + preedit ㅌ핱 (commit of the previous syllable)
Backspace #1cursor moves one cell rightㅌ (U+314C) — the preedit jamo, committed
Backspace #2ㅌ deletedDEL (0x7f)

So on Backspace #1 the IME commits the remaining jamo through insertText, and since d60a16c146 ("macos: avoid replaying keys that commit preedit") the markedTextBefore && keyTextAccumulator nonEmpty branch sends that commit and does not replay Backspace (shouldReplayCommittedPreeditKey only allows arrows). In 1.3.1 that branch required an arrow key, so Backspace took the old path. This matches the regression window in the issue (d60a16c146 is dated 2026-05-01).

I'm testing a local patch that, for Backspace/Forward Delete with marked text, drops the commit when the committed text equals the marked text from before the key (i.e. the user deleted the last jamo), and will report back.

AI disclosure: an AI assistant helped capture the byte log and read the source; I reproduced the bug and reviewed the analysis.

coldplay1261d ago

Follow-up: a local 1.4.0 build with the change below fixes it for me (built-in 2-Set Korean, macOS 27). 하 ㅌ ㅌ ⌫ now deletes the lone jamo; syllable decomposition (갑 → 가 → ㄱ) and arrow-key commits are unchanged.

When Backspace makes the IME commit exactly the marked text that existed before the key, send nothing:

if Ghostty.SurfaceView.isPreeditDeletionCommit(list, markedBefore: markedStringBefore,
                                               key: Ghostty.Input.Key(keyCode: translationEvent.keyCode)) {
    return
}
// isPreeditDeletionCommit: key == .backspace && !markedBefore.isEmpty && committed.joined() == markedBefore

SurfaceViewAppKitTests pass with two added cases. I'm not vouched yet, so I haven't opened a PR.

AI disclosure: Claude Code helped trace the regression and draft the patch; I tested the build myself.