When true, selection_find_word_boundary_right() behaves as before - it
stops as soon as it encounters a character that isn’t of the
same *type* as the “initial” character (the last character in the
selection).
Take this, for example:
The Quick Brown Fox
The selection will first stop at the end of “the”, then just *before*
“quick”, then at the end of “quick”. Then just *before* “brown”, and
then at the end of “brown”, and so on.
This suits mouse selections pretty good. But when
selection_find_word_boundary_right() is used to extend a search match,
it’s better to ignore space-to-word character transitions. That is, we
want
The Quick Brown Fox
to first extend to the end of “the”, then immediately to the end of
“quick”, then to the end of “brown”, and so on.
Setting the ‘stop_to_space_to_word_boundary’ argument to false results
in latter behavior.
This is now done by search, when executing the
“extend-to-word-boundary” and “extend-to-next-whitespace” key
bindings.
We now bind ctrl+v, ctrl+shift+v, ctrl+y and XF86Paste to pasting from
the clipboard into the scrollback search buffer.
Why all these? Because we can, and because all are common shortcuts
for pasting:
* ctrl+v: “normal” apps use this by default
* ctrl+shift+v: used in terminals (including foot)
* ctrl+y: Emacs
* XF86Paste: special keyboard key, for pasting
find_next() did not always terminate correctly, causing
search_matches_next() to never terminate, which finally leads to an
infinite loop when rendering the search overlay surface, while finding
all matches to highlight.
The problem is that find_next(), after having found the initial
matching characters, enters a nested while loop that tries to match
the rest of the search criteria. This inner while loop did not check
if we’ve reached the last cell, and happily continued past
it (eventually wrappping around the scrollback buffer).
Closes#1047
5c4ddebc3c refactored
search_update_selection(), specifically, the logic that moves the
viewport.
It did so by converting the absolute row number (of the match) to
scrollback relative coordinates. This way we could ensure the viewport
wasn’t moved “too much” (e.g. beyond the scrollback start).
However, grid_row_abs_to_sb() and grid_row_sb_to_abs() doesn’t take a
partially filled scrollback into account. This means the row (numbers)
it returns may refer to *uninitialized* rows.
Since:
* The match row itself is valid (we *know* it has text on it)
* We *subtract* from it, when setting the new viewport (to center the
match on the screen).
it’s only the *upper* part of the new viewport that may be
uninitialized. I.e. we may have adjusted it too much.
So, what we need to do is move the viewport forward until its *first*
row is initialized. Then we know the rest will be too.
Unmapping a sub-surface in Sway does not damage the underlying
surface.
As a result, the OSC-555 escape (“flash”) will leave yellow margins on
~every second frame.
Out of sway, river, weston and mutter, only Sway needs this
workaround.
This is a workaround for https://github.com/swaywm/sway/issues/6960Closes#1046
Unmapping a sub-surface in Sway does not damage the underlying
surface.
As a result, "committing" a scrollback search will typically leave
most of the foot window dimmed.
It can be seen when "cancelling" a search as well, but there it's less
obvious - only the margins are left dimmed. This is because cancelling
a search damaged the current viewport (something that shouldn't be
needed).
Out of sway, river, weston and mutter, only Sway needs this
workaround.
This is a workaround for https://github.com/swaywm/sway/issues/6960
* When extending the selection to the next word boundary, ensure the
row numbers are valid:
- use selection_get_end() when retrieving the current end
coordinate. This alone fixes a crash where we previously would
crash in an out-of-bounds array access in grid->rows[], due to
term->selection.coords.end being unbounded.
- ensure the new end coordinate is bounded before and after calling
selection_find_word_boundary_right().
* When moving the viewport (to ensure a new match is visible), make
sure we don’t end up with the match outside the new viewport.
Under certain, unusual, circumstances, the moved viewport _still_
did not contain the match. This resulted in assertions triggering
later, that assumed the match(es) are *always* visible.
It’s fairly easy to trigger this one by running foot with e.g.
foot -o scrollback.lines=0 --window-size-chars 25x3
and then hitting enter a couple of times, to fill the scrollback
history (which should consist of a single row in the example above),
and the do a scrollback search for (part of) the prompt, and keep
searching backward until it crashes.
This would happen if calculated (new) viewport had to be adjusted
(for example, to ensure it didn’t go past the scrollback end).
This patch changes the logic used when calculating the new
viewport. Instead of calculating the wanted viewport (match is
vertically centered) and then trying to adjust it to ensure the new
viewport is valid, start with a “safe” new viewport value, and then
determine how much we can move it, if at all, to center the match.
This is done by using scrollback relative coordinates. In this
coordinate system, the new viewport must be
>= 0, and < grid->num_lines - term->rows
This makes it very easy to limit the amount by which the viewport is
adjusted.
As a side-effect, we can remove all the old re-adjustment logic.
* The match iterator no longer special cases the primary match. This
was needed before, when the search iterator did not handle
overlapping matches correctly. Now that we do, the iterator is
guaranteed to find the primary match, and thus we no longer need to
special case it.
This fixes a bug where the primary match was returned twice, due to
the logic checking if a secondary match is the same as the primary
match was flawed...
Closes#1036
Internally, selection coordinates are *unbounded* (that is, the row
numbers may be larger than grid->num_rows) while a selection is
ongoing. Only after it has been finalized are the coordinates bounded.
This means it isn’t safe to use term->selection.coords.* directly.
These functions convert row numbers between absolute coordinates and
“scrollback relative” coordinates.
Absolute row numbers can be used to index into the grid->rows[] array.
Scrollback relative numbers are ordered with the *oldest* row first,
and the *newest* row last. That is, in these coordinates, row 0 is the
*first* (oldest) row in the scrollback history, and row N is the
*last* (newest) row.
Scrollback relative numbers are used when we need to sort things after
their age, when determining if something has scrolled out, or when
limiting an operation to ensure we don’t go past the scrollback
wrap-around.
As this means the last call to sarch_matches_next() found a match at
the bottom of the view, and then set the iter’s *next* start position
to a row outside the view.
This is fine, but we need to handle it, by immediately stopping the
iter.
When bumping the iter’s start.row, we’re working with view-local
coordinates. That is, 0 >= row < term->rows.
This means it is wrong to and with grid->num_rows - 1, because a),
‘row’ should **never** be that big. And b), if we do, we’ll just end
up in an infinite loop, where the next call to matches_next() just
starts over from the beginning again (and eventually hitting the exact
same place that got us started).
Our CSDs, the search-box and URL labels are all implemented using
sub-surfaces, synchronized with the main grid.
This means we *must* commit the main surface as well, when updating
one of these sub-surfaces.
The logic for doing so in the frame callback was flawed, and only
triggered when the main grid was actually dirty.
That is, e.g. search box updates that did not also resulted in grid
updates (for example - pasting a search criteria that doesn’t match),
did not result in a UI refresh.
Closes#1040