Today — insert mode (IRM, CSI 4 h / CSI 4 l) is parsed and ignored, so text inserted into a line overwrites what was there instead of pushing it right. Measured against 0.9.0 on a 20x2 terminal:
$ inspect --size 20x2 sh -c "printf 'abcd\033[1GZZ'" # no insert mode
ZZcd # correct
$ inspect --size 20x2 sh -c "printf 'abcd\033[1G\033[4hZZ'" # IRM on, then ZZ
ZZcd # xterm shows ZZabcd
$ inspect --size 20x2 sh -c "printf 'abcd\033[1G\033[2@ZZ'" # ICH, the other spelling
ZZabcd # correct
The last line is the shape of it: CSI @ (insert blank cells) works, because the backend dispatches @; CSI 4 h does not, because the backend dispatches no ANSI-mode set or reset at all. Its CSI table covers @ A B C D E F G H J K L M P S T X d m r t and the DEC-private ?h / ?l, and nothing else — so every non-private mode reaches an unhandled path and disappears.
Why it is worth fixing — this is not an exotic sequence. Insert mode is smir/rmir in terminfo and both are present in xterm-256color, which is the TERM termlens hands every child by default:
$ infocmp -1 xterm-256color | grep -E '^\s+(smir|rmir)='
rmir=\E[4l,
smir=\E[4h,
An ncurses application calling insch, insstr or winsertln on a terminal that advertises smir uses insert mode rather than CSI @, and which one it picks is ncurses's decision, not the application author's. So a line-editor, a form field, or any widget that inserts a character mid-line renders wrong on the grid — the tail of the line is eaten instead of shifted — and the test asserting on that line fails against an application that is correct, or worse, a snapshot blesses the eaten tail and keeps passing.
It is the same failure class as ESC ( 0 line drawing before #204: a sequence real terminfo entries emit, acknowledged by the parser, dropped before the grid.
Fix — the backend does not implement it, so termlens does, in the layer built for exactly this. emu/seq.rs already tracks state vt100 does not model (the character sets, focus reporting, the cursor shape) and emu/vt100.rs already rewrites the byte stream on the way to the grid — that is how DEC Special Graphics became box drawing.
The mechanism here is different from a glyph substitution: IRM changes what a printable byte does. The cheapest correct shape is to track the flag in the tracker (CSI 4 h sets, CSI 4 l clears, RIS and DECSTR clear it — DECSTR's list already lives in soft_reset), and, while it is set, precede each run of printable bytes handed to the grid with the CSI n @ that reserves room for it. ICH is dispatched by the backend and has the same effect, which is what makes this a rewrite rather than a new grid operation.
Two details worth getting right, both cheap: a run that reaches the right margin must not reserve past it, and the run length is in columns, so a wide character counts twice — emu/vt100.rs already knows how to reason about that from the wide-continuation work in #218.
Out of scope, deliberately: the other ANSI modes vt100 drops (LNM, mode 20, and the rest). This issue is the one with a terminfo capability behind it. Say in the PR whether the tracker's flag is worth exposing on Screen, and decide it either way — "the application put the terminal in insert mode and left it there" is the same shape of assertion as alternate_screen().
Done when — printf 'abcd\033[1G\033[4hZZ' renders ZZabcd; CSI 4 l returns to overwrite; RIS and DECSTR clear the mode; a test in the emulation suite covers all four, including an insert at the right margin and one over a wide character; and the README's Known limitations no longer implies insert mode is unmodelled.
Today — insert mode (
IRM,CSI 4 h/CSI 4 l) is parsed and ignored, so text inserted into a line overwrites what was there instead of pushing it right. Measured against 0.9.0 on a 20x2 terminal:The last line is the shape of it:
CSI @(insert blank cells) works, because the backend dispatches@;CSI 4 hdoes not, because the backend dispatches no ANSI-mode set or reset at all. Its CSI table covers@ A B C D E F G H J K L M P S T X d m r tand the DEC-private?h/?l, and nothing else — so every non-private mode reaches an unhandled path and disappears.Why it is worth fixing — this is not an exotic sequence. Insert mode is
smir/rmirin terminfo and both are present inxterm-256color, which is theTERMtermlens hands every child by default:An ncurses application calling
insch,insstrorwinsertlnon a terminal that advertisessmiruses insert mode rather thanCSI @, and which one it picks is ncurses's decision, not the application author's. So a line-editor, a form field, or any widget that inserts a character mid-line renders wrong on the grid — the tail of the line is eaten instead of shifted — and the test asserting on that line fails against an application that is correct, or worse, a snapshot blesses the eaten tail and keeps passing.It is the same failure class as
ESC ( 0line drawing before #204: a sequence real terminfo entries emit, acknowledged by the parser, dropped before the grid.Fix — the backend does not implement it, so termlens does, in the layer built for exactly this.
emu/seq.rsalready tracks state vt100 does not model (the character sets, focus reporting, the cursor shape) andemu/vt100.rsalready rewrites the byte stream on the way to the grid — that is how DEC Special Graphics became box drawing.The mechanism here is different from a glyph substitution: IRM changes what a printable byte does. The cheapest correct shape is to track the flag in the tracker (
CSI 4 hsets,CSI 4 lclears,RISandDECSTRclear it —DECSTR's list already lives insoft_reset), and, while it is set, precede each run of printable bytes handed to the grid with theCSI n @that reserves room for it.ICHis dispatched by the backend and has the same effect, which is what makes this a rewrite rather than a new grid operation.Two details worth getting right, both cheap: a run that reaches the right margin must not reserve past it, and the run length is in columns, so a wide character counts twice —
emu/vt100.rsalready knows how to reason about that from the wide-continuation work in #218.Out of scope, deliberately: the other ANSI modes vt100 drops (
LNM, mode 20, and the rest). This issue is the one with a terminfo capability behind it. Say in the PR whether the tracker's flag is worth exposing onScreen, and decide it either way — "the application put the terminal in insert mode and left it there" is the same shape of assertion asalternate_screen().Done when —
printf 'abcd\033[1G\033[4hZZ'rendersZZabcd;CSI 4 lreturns to overwrite;RISandDECSTRclear the mode; a test in the emulation suite covers all four, including an insert at the right margin and one over a wide character; and the README's Known limitations no longer implies insert mode is unmodelled.