* 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
- foot.ini.5: mention location of example config file
- foot.1: replace outdated (or incomplete) information about the
location of the config file with references to foot.ini.5
- foot.1 and foot.ini.5: conform to scdoc specification, thus surround each
header with (at least) one blank line. additionally consistently use exactly
one line before/after each header (was sometimes two before)
- foot.1: some parts of the keybindings had their own section, move into
KEYBOARD SHORTCUTS section
- foot.1: move EXIT STATUS section to the end where it is commonly found
- foot.1: copy information about config file handling from the beginning of
foot.ini.5 into the CONFIGURATION section of foot.1
- INSTALL.md: foot.ini is no longer included in the documentation
- meson.build: do not bundle foot.ini with documentation anymore, see also
https://codeberg.org/dnkl/foot/pulls/1015Closes#1002
The match logic uses the last start coordinate to determine which end
points in the selection to update. This sometimes fails when the start
coordinate has been changed by e.g. a key binding - the new start
coordinate is incorrectly matched against the old-but-modified start
coordinate, causing foot to e.g. *not* upate the selection start
coordinate.
Example:
$ echo 'test\n\test\ntest'
Then do a scrollback search for 'test. The first match is found
correctly (the last 'test'), but searching for the previous match
(ctrl+r) does not select the middle 'test'.
Fix by passing the search direction to search_find_next(), and have
_it_ calculate the coordinate to start search. There are three possibilities:
* forward
* backward
* "backward", but at the same position
The first two are used when searching for next/prev match with ctrl+s
and ctrl+r. The last one is used when the search criteria is
updated. In this case, we don't want to move to the previous match,
*unless* the current match no longer matches.
When determining whether a keysym is a modifier (to skip hiding the
pointer when [mouse].hide-when-typing=yes), use the same matching
logic xkbcommon does.
This function, xkb_keysym_is_modifier() is unfortunately not part of
the public API, which is why we copy it instead.