ADR 0138: Two paper cuts¶
Status¶
Accepted
Context¶
ROADMAP keeps a list of things too small to schedule and too real to
close. Two of them had been on it long enough to be worth doing rather
than re-reading: Ctrl+D not wrapping, and the buffer bar not eliding.
Neither is a feature. Both are cases where the editor does something defensible and slightly wrong, every time, forever.
Decision¶
Ctrl+D wraps¶
"Add a caret at the next occurrence" searched forward and stopped at the end of the buffer. So the command's usefulness depended on where in the file you happened to start: the same keystroke found every use of a name declared at the top, and none of one declared at the bottom. Renaming a local in a function near the end of a file was the case that made it obvious.
Two passes now — forward from the last caret, then from the top up to the word it started from.
An occurrence that already has a caret is skipped rather than
re-added. normalizeCursors() would drop the duplicate, so adding it
would look like the key had done nothing, while occurrences were still
left to take.
Deciding whether an occurrence is taken turned out to be the only
subtle part. The obvious test — is there a caret at the word's end —
is wrong, because it is only true for the carets this command adds.
The one you started from is wherever you left it, usually mid-word, so
the first wrap re-added the caret's own word. The test is whether any
caret lies anywhere in [start, end].
The buffer bar elides¶
A long filename made its tab as wide as the name, pushing the others out
of the strip. Names are shortened to about 22 characters of the current
font, middle-elided: both ends of a filename carry meaning, and
eliding the tail turns editor_viewport_render.cpp and
editor_viewport_input.cpp into the same tab.
The shortening happens in relayout() rather than in paintEvent(), so
the width a tab reserves is the width it draws. Doing it at paint time
would have left every long tab full-width with a short name in it.
The limit is in pixels, measured through QFontMetrics, so it follows
the configured font size instead of a character count that means
something different at every size.
Consequences¶
Thirteen tests, in two suites. elideTabName() and maxTabNameWidth()
are free functions in a bufferbar namespace precisely so the first
can be tested without constructing a widget; the strip's actual drawing
is still checked by looking at it.
The multi-cursor tests needed cursorCount() and cursorOffsets() on
EditorViewport — the caret set had no public shape to assert on, which
is the same reason this behaviour was wrong for as long as it was. They
also have to call registerCommands() first: a bare viewport has no
command registry, so runCommandByName() quietly returns false, which
cost a debugging pass.
Both changes were verified by reverting them — one pass instead of two fails two of the wrap tests.
Still on the list: the highlight capture styles remain hardcoded. The sticky column came off it immediately afterwards, and turned out not to be the multi-cursor paper cut it was filed as — ADR 0139.