Hi,
I found a composition-time rendering issue with CodeMirror’s built-in
highlightWhitespace() extension on Windows with a Japanese IME.
I’m not sure whether the DOM behavior itself is expected as part of CodeMirror’s composition-preservation strategy, so I’m posting this here before treating it as a bug.
Minimal reproduction
Environment:
- Windows 11
- Chrome 152.0.7977.65 (64-bit)
- ATOK Japanese IME
@codemirror/view6.43.9@codemirror/state6.7.1
The editor has highlightWhitespace() enabled:
import { highlightWhitespace } from "@codemirror/view"
// Include highlightWhitespace() in the editor extensions.
The document contains five ASCII spaces (U+0020):
Steps:
- Place the cursor between the second and third spaces.
- Enable the Japanese IME.
- Start composing text, for example
gross(entered as full-width IME preedit characters). - Leave the composition active without committing it.
Expected
The five whitespace markers remain visually stable while the composition is active.
Actual
Before composition, the five spaces are rendered as five separate
cm-highlightSpace spans.
At compositionstart:
marker.count = 5
After the first composition update:
marker.count = 4
One of the remaining marker spans now contains two spaces and the IME preedit text.
For example, after the first composition update:
marker[0].className = "cm-highlightSpace"
marker[0].textContent = " g"
marker[0].rect.width = 30px
selection.anchor.insideMarker = true
The DOM selection is inside that same cm-highlightSpace span.
The built-in style is still applied:
background-image = radial-gradient(...)
background-repeat = repeat
background-size = auto
Visually, instead of five stable per-space markers, the span that now contains both spaces and the preedit text paints an enlarged dot behind the preedit text.
[attach before / during / after image here]
The circled area shows the span containing the preedit text painting a
single enlarged gradient dot over the larger box
(background-size: auto), instead of one marker per space.
The underlying document is not corrupted. The number of U+0020 spaces remains five before and after composition.
At the compositionend snapshot, the marker count is still 4. In my run,
a further DOM child-list mutation happened about 60 ms later, after which
the normal rendering returned.
Additional observations
I originally encountered this while implementing a custom per-character
Decoration.mark() for U+3000 IDEOGRAPHIC SPACE. I observed the same
high-level behavior there: decorated whitespace and IME preedit text can
end up inside the same mark-decoration span during composition.
I reproduced that custom-decoration behavior with ATOK, Microsoft IME, and Google Japanese Input on Windows.
I also observe span merging in Firefox 154.0.1, but the visual result differs: Firefox does not paint the enlarged gradient, so the merged span simply shows no marker. The Firefox behavior appeared timing-dependent in my tests and I could not reproduce it reliably, so the numbers above are all from Chrome.
Windows US-International dead-key input did not reproduce this in my test, since it did not go through the same IME composition path.
Questions
Is this DOM restructuring around an active composition an expected consequence of CodeMirror’s composition-preservation strategy?
If so, what is the recommended way to implement per-character visual markers that need to remain visually stable while IME composition is active?
If anyone with a Chinese or Korean IME can check whether this reproduces on their setup, that would also help determine whether it is specific to Japanese IMEs.



