OpenTUI 0.6.1
In 0.6.0, EditBuffer.setText("a\fb") throws NativeError. The editor places
its cursor by display cell, and a control character took no cells, so the
cursor could not move past it or delete it. In 0.5.14, which accepted
controls, the cursor in "a\fb" could not reach column 0, Backspace stopped at
"a\f", and rendering "A\x07B\x1bC\fD" wrote raw BEL and ESC bytes to the
terminal. Rejecting the text stopped that, but every application then had to
remove these characters before it could edit a string.
In 0.6.1, editors keep control characters and show them the way Vim and
Neovim do. The text "a\fb\0c\x1bd\x85e\x7ff" shows as a^Lb^@c^[d<85>e^?f. A
C0 control or DEL is caret notation in two cells, and a C1 control is
lowercase hex in four cells. The notation changes only how the text looks. The
cursor stops before each control, one Backspace deletes it, and getText(),
undo, and selection copy return the original characters. Tab, carriage
return, and line feed work as before. Read-only text, such as
TextRenderable, still gives controls no cells.
This has one cost. An editor offset is no longer the same as a JavaScript
string width. Bun.stringWidth("\f") is 0, but ^L takes two cells. Code
that calculates editor offsets from string widths, such as code that places an
extmark after a match, must ask the buffer instead. measureText() measures
text the way the buffer lays it out, and getTextRange() converts an offset
back to a string index:
buffer.setText("\f@file")
const text = buffer.getText()
const offset = buffer.measureText(text.slice(0, text.indexOf("@"))) // 2: ^L takes two cells
buffer.getTextRange(0, offset).length // 1: the index of "@"
Added
- core:
EditBuffer.measureText(text)returns the display offset at the end oftextas the buffer would lay it out. It counts each line break as one. See Editing buffers and views. (#1636) - native:
ot_edit_buffer_measure_text()gives the same measurement through the C interface. See Editing buffers and views. (#1636)
Changed
- core:
EditBuffer.setText(),insertText(), andreplaceText(), TextareainitialValue,Input.value, and placeholders accept control characters and do not throwNativeError. An editor shows C0 controls and DEL as^@to^_and^?in two cells, and C1 controls as<80>to<9f>in four cells. See Editing buffers and views. (#1636) - core: Paste in
TextareaandInputkeeps control characters. It removes terminal escape sequences, including a lone ESC and an incomplete sequence, the same way on Bun and Node. Core no longer depends onstrip-ansi. See Editing buffers and views. (#1636)
Fixed
- core: Extmarks and the
Inputcursor use native display offsets. As a result, they keep the correct position in text that contains control characters. See Editing buffers and views. (#1636)
API changes#
@opentui/core#
Added
method EditBuffer.measureText(text: string): numbermethod FFIRenderLib.contextEditBufferMeasureText(context: NativeContextHandle, editBuffer: ContextEditBufferHandle, bytes: Uint8Array): number
opentui.h#
Added
function ot_edit_buffer_measure_text(ot_context *, const ot_handle *edit_buffer, const uint8_t *bytes, uint32_t byte_count, uint32_t *out_width): ot_status