On Wed, Sep 09, 2026 at 06:09:05PM +0100, José Matos wrote:
> It is that time of the year again. :-)

.. before I grab my coffee ... ;)

 Claude Code v2.1.236

$  check it out: 
https://www.mail-archive.com/[email protected]/msg226791.html

* Fetch(https://www.mail-archive.com/[email protected]/msg226791.html)
  ->  Received 9.6KB (200 OK)

  Ran 20 shell commands 

* Read it. Short version: that assert cannot happen on master - it is 
2.5.x-only, and the culprit is the IM acceptance hook, not the
  paste.

  Mechanism

  GuiApplication::processKeySym() sets command_phase_ and emits 
keyCommandStateSet() before dispatching the key. That signal is
  connected to every GuiWorkArea (GuiWorkArea::init(), GuiWorkArea.h:107 
re-emits it to its own GuiInputMethod). So one keystroke
  anywhere makes every open pane/tab query its own BufferView cursor.

  In 2.5.x toggleInputMethodAcceptance() still carries the pre-Qt-6.11 
workaround:
    
  if (guiApp->platformName() != "cocoa")
        d->im_off_in_math_ =
                d->cur_->getFont().language()->inputMethodOffInMath();

  Cursor::getFont() indexes paragraph data (text.getPar(sl.pit()), 
par.getFont(..., pos, ...)). A cursor that has not been sanitized
  since the last edit is exactly what José hit: pos: 33 size: 1. The function's 
own next comment already admits the cursor is a lagging
  cache - but inset() only touches the slice, while getFont() walks the 
fontlist.

  Master dropped that block in 2ca8f53038 (Yokota, 2026-06-20, "Remove an old 
workaround" - QInputMethod::locale() now works on Linux),
  which is not in 2.5.x. Hence 2.5.3 asserts and 2.6 does not. 

  Worth noting the latent worse case: if the stale pit_ is out of range instead 
of just pos, there is no assert at all - getPar(pit) is
  a plain operator[] on the paragraph vector. Same family as the closed #6896 / 
#7694 split-view asserts, resurrected by the new
  per-keypress broadcast.

  Minimal 2.5.x fix (avoid paragraph indexing; use the cursor's cached font):
 
  --- a/src/frontends/qt/GuiInputMethod.cpp
  +++ b/src/frontends/qt/GuiInputMethod.cpp
  @@ -134,13 +134,16 @@
   void GuiInputMethod::toggleInputMethodAcceptance()
   {
        if (guiApp->platformName() != "cocoa")
                // Since QInputMethod::locale() doesn't work on other systems, 
use
                // language at the cursor point to infer the current input 
language
                // as a second best
  -             d->im_off_in_math_ =
  -                     d->cur_->getFont().language()->inputMethodOffInMath();
  +             // Do not use Cursor::getFont() here: this runs for every work
  +             // area on every key press, before dispatch, so a cursor that
  +             // has not been sanitized yet can point outside its paragraph.
  +             d->im_off_in_math_ =
  +                     
d->cur_->real_current_font.language()->inputMethodOffInMath();

        // note that d->cur_->inset() is a cache so it lags from actual key 
moves
        bool enabled = !((d->im_off_in_math_
                          && d->cur_->inset().currentMode() == Inset::MATH_MODE)
                         || guiApp->isInCommandMode());

  The alternative is to just backport 2ca8f53038 to 2.5.x, which removes the 
call outright; the cost is relying on
  QInputMethod::locale() for the older Qt versions 2.5.x still supports (an 
IM-in-math behaviour regression, not a crash). I would take
  the one-liner above for 2.5.3 and leave the workaround removal to master.

  Want me to try reproducing under the Xvfb harness (split view, 
multi-paragraph paste at end of paragraph, then any keypress) before
  you reply on the list?


$ yep

  -> Read 10 files, listed 3 directories, ran 37 shell commands 

* Reproduced deterministically, and the fix holds. Everything ran headless via 
~/.claude/lyx/xvfb-lyx.sh; nothing is left running.

  Repro (in the repo: im-stale-cursor-test/repro.sh + docA.lyx/docB.lyx, 
XVFBLYX_LYX=<build>/src/lyx ./repro.sh)

  1. Open docB (par 0 = 40 chars, par 1 = "y"); view-split - now docB has two 
work areas.
  2. In pane 2, move the cursor to par 0, pos 10, then switch that pane's tab 
to docA. docB's pane-2 work area is hidden, its cursor
     frozen at pos 10.
  3. In pane 1 (docB), paste a 2-paragraph clipboard at the start of par 0, 
whose first paragraph is 1 char. Par 0 is now size 1.
  4. Press any key -> processKeySym() broadcasts keyCommandStateSet(), the 
hidden work area's GuiInputMethod walks its stale cursor into
     paragraph data, dead.
    
  Results, same sequence throughout:

  ??????????????????????????????????????????????????????????????????????????
  ?                  build                   ?           outcome           ?
  ??????????????????????????????????????????????????????????????????????????
  ? 2.5.x (.testbuilds/2.5-fixed, 2.5.0-114) ? crash, twice out of twice   ?
  ??????????????????????????????????????????????????????????????????????????
  ? same, no split view (single work area)   ? survives                    ?
  ??????????????????????????????????????????????????????????????????????????
  ? master (src/lyx, 86310d6fcc)             ? survives                    ?
  ??????????????????????????????????????????????????????????????????????????
  ? 2.5.x + the real_current_font one-liner  ? survives, keys keep landing ?
  ??????????????????????????????????????????????????????????????????????????

  One correction/addition to what I said earlier. On my build (libstdc++ 
assertions on) it dies earlier than on José's Fedora build:

  basic_string.h:1221: ... [with _CharT = wchar_t]: Assertion '__pos <= size()' 
failed.

  That is Paragraph::isSeparator() - return d->text_[pos] == ' ';, unguarded - 
reached from the if (pos > 0) block of Cursor::getFont(),
  before the LBUFERR in getFontSettings() that José saw. So without 
_GLIBCXX_ASSERTIONS the first thing this does is a silent 
  out-of-bounds read on the paragraph's docstring; the printed assert is the 
polite version of it. Worth saying in the reply - it makes
  it a bit more than a cosmetic assert. 
  
  Also latent, in both branches: GuiInputMethod::inputMethodQuery()'s 
Qt::ImPreferredLanguage case still does
  d->cur_->getFont().language() (2.5.x:1056, master:1148). Only queried for the 
focused work area, so much less exposed, but it is the
  same dereference.


$ Jose likes emoticons, give him some nice ascii art for good night

* Here you go - drop this at the bottom of the reply:

        *        .        *     .        *      .
     .      _____                    *        .    *
       *   |     |   .         *          .
          _|_____|_        .        *        .    * 
     *   |         |  .        zZz          .
        _|_________|_    *   zZz    *   .        *
       |             |     zZz    .        *
       |   L y X     |  .      *      .       *
       |_____________|      .      *       . 
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     Good night, Jose!  (-_-) zZz
     The cursor sleeps too... in another work area.
       \(^_^)/   <- your paragraph, still size 1
        (>_<)    <- the stale cursor, pos 33
     Sweet dreams, and may your pos never exceed size.  ;-)


--
Can forward repro docs and script if anyone wants. P(/C)
-- 
lyx-devel mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-devel

Reply via email to