Fixes#2877
As the comment in the diff states, we rely on `mmap` to zero our memory.
When we reset we are reusing previously allocated memory so we won't hit
an `mmap`. We need to zero the memory ourselves.
This is pretty slow if there is a lot of memory but in every case except
allocation failures, we expect there to be only a few pages allocated.
This is more correct: a pagelist is a linked list of nodes, not pages.
The nodes themselves contain pages but we were previously calling the
nodes "pages" which was confusing, especially as I plan some future
changes to the way pages are stored.
See comments in the test, also this:
> The scenario is if you adjustCapacity a page beyond a std_size then our
> precondition no longer holds. PageList.adjustCapacity makes no guarantee
> that the resulting capacity fits within a standard page size. It is in fact
> documented this way and that it is slow since we have to mmap out of our
> memory pool.
Running alacritty/vtebench on some machines causes Ghostty to fail on
`assert(first != last)` when trying to grow scrollback. We now make sure
we have enough pages before trying to reuse pages.
It's possible for `self.grow` to be called within the body of the
function, and `self.grow` uses `self.cols` to create a new page if
there's no more room in the current one.
If we call `moveLastRowToNewPage` at any point because we failed to copy
some managed memory, it tries to copy managed memory that hasn't been
cloned yet when moving our progress to a new page.
Avoid this by setting our content tag, hyperlink flag, and style id to
indicate no managed memory is present on the cell.
Before, cells that were explicitly set to match the default bg color
were treated as if they did NOT have the default and extending would
occur. We now check the exact RGB of each cell.
I noticed that the HashMap iterator showed up prominently in Instruments when quickly
resizing Ghostty.
I think this is related to the [tombstone issue](https://github.com/ziglang/zig/issues/17851),
where the `next()` function has to skip unused meta-nodes.
In that same issue, Andrew is suggesting that the non-array hashmap might get deleted from the
standard library.
After switching to `AutoArrayHashMapUnmanaged`, iteration barely shows up anymore.
Deletion from the pin list should also be fast as swapRemove is used (order does not need to be preserved).
Question is if insertion performance is negatively affected, though I'm not seeing anything obvious.
Still, checking this PR for any perf regressions might be a good idea.
If this pans out, there are more places where this switch might be beneficial.
There are scenarios where this configuration looks bad. This commit
introduces some heuristics to prevent it. Here are the heuristics:
* Extension is always enabled on alt screen.
* Extension is disabled if a row contains any default bg color. The
thinking is that in this scenario, using the default bg color looks
just fine.
* Extension is disabled if a row is marked as a prompt (using semantic
prompt sequences). The thinking here is that prompts often contain
perfect fit glyphs such as Powerline glyphs and those look bad when
extended.
This introduces some CPU cost to the extension feature but it should be
minimal and respects dirty tracking. This is unfortunate but the feature
makes many terminal scenarios look much better and the performance cost
is minimal so I believe it is worth it.
Further heuristics are likely warranted but this should be a good
starting set.
Two bugs:
1. If our pin is the top page, and self.y == top.y, then x will tell us
the answer. Before, we'd fall through.
2. If our pin is not the top or bottom, but the top == bottom, then we
can't possibly be between. Before, we'd incorrectly check the linked
list starting AFTER top.
Reflow logic now lives inside of ReflowCursor. This fixes multiple
issues with the previous implementation, and is more straightforward
in how it works. The old impl resulted in fragmentation of pages,
leading to unbounded memory growth when resizing repeatedly.
Also improves the preserved_cursor logic in `resizeCols`.
This fixes an issue where when we adjusted the capacity of the page, the
style ref count would be off by one (short by one).
The issue is that when adjusting the capacity of a page, it happens on
PageList which is unware of cursor state and therefore can't ensure to
reference the active style.
This creates an `adjustCapacity` call on `Screen` which can properly
handle this scenario.