Skip to content

fix: multiline/history/completion bug batch (ported from gloathub fork review)#106

Merged
maxlandon merged 6 commits into
masterfrom
dev
Jun 1, 2026
Merged

fix: multiline/history/completion bug batch (ported from gloathub fork review)#106
maxlandon merged 6 commits into
masterfrom
dev

Conversation

@maxlandon

Copy link
Copy Markdown
Member

Batch of fixes surfaced while reviewing the gloathub/go-readline fork's commits-ahead. Each fix is paired with a regression test; the fork's remaining commits were either already implemented in our tree, opinionated default/API changes we declined, or GNU-deviating behavior we keep faithful.

Fixes

  • fix(history): write history again when history-size is setSources.Write's cap guard was inverted (maxEntries >= Len()), so any positive history-size skipped every write. The in-memory source does no trimming of its own, so this guard was the only cap. Corrected to maxEntries > 0 && Len() >= maxEntries.
  • fix(history): make vi-insert down-arrow navigate again — the default vi-insert keymap binds the down arrow to down-line-or-search, but no such command was registered (only up-line-or-search and the unrelated down-line-or-select), so the key did nothing. Added the symmetric downLineOrSearch.
  • fix(color): guard Trim against a non-positive budgetTrim ended in input[:n] and panicked on a negative budget, reachable from completion column math on very narrow terminals. Now returns empty for a non-positive budget, guarding both call sites at the source.
  • fix(emacs): normalise carriage returns in bracketed paste — terminals deliver pasted line breaks as \r/\r\n; inserting them raw corrupted the line buffer and multiline rendering. Converted to \n before insertion.
  • fix(core): stop LineMove landing one column short of line endLineMove applied CheckCommand every step, clamping the cursor off a line's trailing newline even in emacs/vi-insert. Since every caller already routes through Shell.execute (which applies the keymap-aware check), this double-applied the command-mode rule. Dropped the in-loop clamp.

Feature (opt-in, no API/default change)

  • feat(completion): insert common prefix for menu-complete-display-prefix — the option was half-implemented (menu shown, shared prefix never inserted). Completes the GNU-documented semantics. Only runs when the option is set (off by default); InsertCommonPrefix is a method on the internal completion engine, so no public API change.

Testing

New tests cover each fix; the LineMove change was verified to fail against the old code before the fix. Full suite (incl. the display golden-screen tests), go vet, and golangci-lint all clean.

🤖 Generated with Claude Code

maxlandon and others added 6 commits June 1, 2026 08:58
The maxEntries guard in Sources.Write was inverted: it skipped every
write while the source was still under the configured cap, so any
positive history-size value disabled history persistence entirely. The
in-memory source does no trimming of its own, so this guard is the only
cap, and the bug was masked only because the unset default normalises to
-1 (unlimited).

Use `maxEntries > 0 && Len() >= maxEntries` so a positive cap stops
appending once full while unlimited (<= 0) always writes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The default vi-insert keymap binds the down arrow to down-line-or-search,
but no such command was registered (only its up-line-or-search sibling
and the unrelated down-line-or-select existed), so the binding resolved
to nothing and the key did nothing.

Add downLineOrSearch, the symmetric counterpart of upLineOrSearch: move
down a line within the buffer, or search history forward when already on
the last line. A test asserts every arrow-navigation action bound by the
default keymaps resolves to a registered command.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
color.Trim ended in input[:maxPrintableLength], which panics when the
budget is negative. The completion column math reaches this on very
narrow terminals, where the available width is smaller than the trailing
padding (maxDisplayWidth - trailingValueLen < 0).

Treat a non-positive budget as "nothing fits" and return empty, which
guards both group.go call sites at the source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Terminals deliver line breaks inside a bracketed paste as \r or \r\n.
Inserting those raw carriage returns corrupts the line buffer and breaks
multiline display and evaluation. Convert them to \n before inserting the
pasted text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LineMove applied CheckCommand after every step, clamping the cursor off a
line's trailing newline even in emacs and vi-insert modes. Because every
LineMove caller already goes through Shell.execute -- which applies
CheckCommand in vi-command mode and CheckAppend otherwise -- this
double-applied the command-mode rule, leaving the cursor one column short
of the end when moving onto a shorter line.

Drop the in-loop clamp; LineMove now leaves the end-of-line position and
the keymap-aware post-step decides. Tests cover the regression, the
emacs/vi-command clamp contract, and sticky-column round trips.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The menu-complete-display-prefix option was only half-implemented: with it
set, the menu was displayed but the prefix shared by all candidates was
never inserted, so the option did less than GNU readline documents
("display the common prefix of the list of possible completions before
cycling through the list").

Add Engine.InsertCommonPrefix, which extends the typed word with the
longest prefix shared by every candidate (a no-op when they share nothing
beyond what is typed), and call it from complete-word and menu-complete
when the option is set. Behaviour is unchanged when the option is off (its
default), so this only completes the opt-in path. The shared-prefix
comparison is case-sensitive, never inserting characters not shared
verbatim by all candidates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@maxlandon maxlandon merged commit 2e06e5f into master Jun 1, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant