Commit Graph

8814 Commits

Author SHA1 Message Date
Charles Nicholson
45b8ce842e Cell width calculation from ceil to round, fix horizontal spacing 2025-11-19 10:56:29 -07:00
Mitchell Hashimoto
8d8798bc79 renderer: minor log update, all commented 2025-11-19 06:21:53 -10:00
LN Liberda
3e5c4590da Add system integration for highway 2025-11-19 14:25:35 +01:00
Qwerasd
0ce3d0bd07 remove useless code
the style ID is reset up above
2025-11-18 21:03:42 -07:00
Qwerasd
f9e245ab7f perf: separate clearing graphemes/hyperlinks from updating row flag
This improves the `clearCells` function since it only has to update once
after clearing all of the individual cells, or not at all if the whole
row was cleared since then it knows for sure that it cleared them all.

This also makes it so that the row style flag is properly tracked when
cells are cleared but not the whole row.
2025-11-18 21:03:42 -07:00
Qwerasd
5ffa7f8f45 perf: inline calls to StaticBitSet.isSet in sgr parser 2025-11-18 20:46:00 -07:00
Qwerasd
d14b4cf068 perf: streamline RefCountedSet lookup
+ add branch hint to insert
2025-11-18 20:46:00 -07:00
Qwerasd
81eda848cb perf: add full-page dirty flag
Avoids overhead of marking many rows dirty in functions that manipulate
row positions which dirties all or most of the page.
2025-11-18 20:46:00 -07:00
Qwerasd
30472c0077 perf: replace dirty bitset with a flag on each row
This is much faster for most operations since the row is often already
loaded when we have to mark it as dirty.
2025-11-18 20:46:00 -07:00
Qwerasd
212598ed66 perf: add branch hints based on real world data
+ move stream ESC state entry outside of `nextNonUtf8`
2025-11-18 20:43:31 -07:00
Qwerasd
5744fb042c perf: replace charset EnumArray with bespoke struct 2025-11-18 20:40:26 -07:00
Qwerasd
14771e5009 perf: avoid branch in parser csi param action 2025-11-18 20:40:26 -07:00
Qwerasd
3e8d94bb1c perf: misc inlines and branch hints
Inlined trivial functions, added cold branch hints to error paths, added
likely branch hints to common paths
2025-11-18 20:40:26 -07:00
David Matos
52f94f445d Use ^ssh directly rather than run-external
Modify README
2025-11-18 17:22:31 +01:00
Leah Amelia Chen
5a82e1b119 build/blueprint: explicitly mention git vs tarballs
I am so sick and tired of people complaining that the build instructions
on the website are wrong when they clearly haven't realized the difference
between Git-based and tarball-based builds, so here's the extra work to
make sure people actually realize that
2025-11-18 23:33:15 +08:00
Qwerasd
58c26957b4 perf: improve style hash and eql fns 2025-11-17 21:32:48 -07:00
Qwerasd
9e754f9939 perf: fix accidental overhead in refcountedset 2025-11-17 21:31:40 -07:00
Denys Zhak
e0007a66e7 feat: add sts.testing for comparison, test getDescription 2025-11-18 01:29:59 +01:00
Qwerasd
9ab9bc8e19 perf: small sgr parser improvements 2025-11-17 13:17:31 -07:00
Qwerasd
6d5b4a3426 perf: replace std.debug.assert with inlined version
See doc comment in `quirks.zig` for reasoning
2025-11-17 12:13:56 -07:00
Mitchell Hashimoto
3ff0cddee8 benchmark: add screen-clone for benchmarking the Screen.clone method (#9624)
This adds a benchmark and some test coverage for a `screen-clone`
benchmark. This benchmarks the screen cloning which is a hot spot for
lock contention for the renderer + IO threads. I wasn't able to
meaningfully speed this up, but still want to commit this benchmark.
2025-11-17 08:51:27 -08:00
Mitchell Hashimoto
2f49e0c902 remove screenclone test cause it leaks memory on purpose 2025-11-17 06:42:00 -10:00
Mitchell Hashimoto
09d41fd4af terminal: page tests for full clone 2025-11-17 06:10:38 -10:00
Mitchell Hashimoto
9a46397b59 benchmark: screen clone 2025-11-17 05:08:19 -10:00
Jake Nelson
243d32c82a fix: ColorList.clone not cloning colors_c 2025-11-17 15:59:53 +11:00
Qwerasd
995a7377c1 fix(terminal): avoid lockup caused by 0-length hyperlink
This could cause a 0-length hyperlink to be present in the screen,
which, in ReleaseFast, causes a lockup as the string alloc tries to
iterate `1..0` to allocate 0 chunks.
2025-11-16 19:38:46 -07:00
Denys Zhak
c5ada505af feat: add test for getDescription 2025-11-17 01:22:33 +01:00
Qwerasd
9e44c9c956 fix(terminal): avoid memory corruption in cursorScrollDown
It was previously possible for `eraseRow` to move the cursor pin to a
different page, and then the call to `cursorChangePin` would try to free
the cursor style from that page even though that's not the page it
belongs to, which creates memory corruption in release modes and
integrity violations or assertions in debug mode.

As a bonus, this should actually be faster this way than the old code,
since it avoids needless work that `cursorChangePin` otherwise does.
2025-11-16 12:39:10 -07:00
Qwerasd
bb2455b3fc fix(terminal/stream): handle executing C1 controls
These can be unambiguously invoked in certain parser states, and as such
we need to handle them. In real world use they are extremely rare, hence
the branch hint. Without this, we get illegal behavior by trying to cast
the value to the 7-bit C0 enum.
2025-11-16 12:39:10 -07:00
Qwerasd
00c2216fe1 style: add Offset.Slice.slice helper fn
Makes code that interacts with these so much cleaner
2025-11-16 12:39:10 -07:00
Qwerasd
985e1a3cea test(shaper/coretext): test non-monotonic CoreText output 2025-11-16 12:39:10 -07:00
Qwerasd
712cc9e55c fix(shaper/coretext): handle non-monotonic runs by sorting 2025-11-16 12:39:10 -07:00
Qwerasd
0a7da32c71 fix: drop tmux control parsing immediately if broken 2025-11-16 11:28:04 -07:00
David Matos
45ead5ea99 Provide shell ssh integration for nushell 2025-11-16 10:53:36 +01:00
Mitchell Hashimoto
79af2378d2 terminal: unify all notification sending into the notify command 2025-11-15 19:48:09 -08:00
Mitchell Hashimoto
90a6ea7aa5 terminal: note some search thread TODOs we can address later 2025-11-15 14:03:27 -08:00
Mitchell Hashimoto
f0af63db15 terminal: search thread refresh timer to reconcile state 2025-11-15 13:43:26 -08:00
Mitchell Hashimoto
acab8c90a2 terminal: search.Thread searches viewport and notifies viewport results 2025-11-15 13:30:32 -08:00
Mitchell Hashimoto
99d47a4627 terminal: viewport search 2025-11-15 13:00:58 -08:00
Mitchell Hashimoto
bfa397b196 terminal: search thread active screen reconciliation loop 2025-11-14 21:24:18 -08:00
Mitchell Hashimoto
1867928b84 terminal: search thread search ticking 2025-11-14 21:05:05 -08:00
Mitchell Hashimoto
d1ad32eadd terminal: search.Thread starting search loop 2025-11-14 17:04:34 -08:00
Mitchell Hashimoto
19dfc0aa98 terminal: search.Thread more boilerplate, test starting 2025-11-14 16:29:45 -08:00
Mitchell Hashimoto
de545eeae1 lib-vt: export stream.Action for custom streams 2025-11-14 16:01:57 -08:00
Mitchell Hashimoto
2452026ff3 terminal: kitty limits only if kitty graphics being built 2025-11-14 15:53:00 -08:00
Mitchell Hashimoto
4ba00dbe89 fix harfbuzz 2025-11-14 15:48:06 -08:00
Mitchell Hashimoto
580f9f057b convert t.screen to t.screens.active 2025-11-14 15:40:31 -08:00
Mitchell Hashimoto
3aff5f0aff ScreenSet 2025-11-14 15:08:10 -08:00
Mitchell Hashimoto
368f4f565a terminal: Screen opts is a structure 2025-11-14 14:10:37 -08:00
Mitchell Hashimoto
a5a914c2b8 Considerably more search internals (#9585)
Chugging along towards #189

This adds significantly more internal work for searching. A long time
ago, I added #2885 which had a hint of what I was thinking of. This
simultaneously builds on this and changes direction.

The change of direction is that instead of making PageList fully
concurrency safe and having a search thread access it concurrently, I'm
now making an architectural shift where our search thread will grab the
big lock (blocking all IO/rendering), but with the bet that we can make
our critical areas small enough and time them well enough that the
performance hit while actively searching will be minimal. **Results yet
to be seen, but the path to implement this is much, much simpler.**

## Rearchitecting Search

To that end, this PR builds on #2885 by making `src/terminal/search` and
entire package (rather than a single file).

```mermaid
graph TB
    subgraph Layer5 ["<b>Layer 5: Thread Orchestration</b>"]
        Thread["<b>Thread</b><br/>━━━━━━━━━━━━━━━━━━━━━<br/>• MPSC queue management<br/>• libxev event loop<br/>• Message handling<br/>• Surface mailbox communication<br/>• Forward progress coordination"]
    end
    
    subgraph Layer4 ["<b>Layer 4: Screen Coordination</b>"]
        ScreenSearch["<b>ScreenSearch</b><br/>━━━━━━━━━━━━━━━━━━━━━<br/>• State machine (tick + feed)<br/>• Result caching<br/>• Per-screen (alt/primary)<br/>• Composes Active + History search<br/>• Interrupt handling"]
    end
    
    subgraph Layer3 ["<b>Layer 3: Domain-Specific Search</b>"]
        ActiveSearch["<b>ActiveSearch</b><br/>━━━━━━━━━━━━━━━━━━━━━<br/>• Active area only<br/>• Invalidate & re-search<br/>• Small, volatile data"]
        
        PageListSearch["<b>PageListSearch</b><br/>━━━━━━━━━━━━━━━━━━━━━<br/>• History search (reverse order)<br/>• Separated tick/feed ops<br/>• Immutable PageList assumption<br/>• Garbage pin detection"]
    end
    
    subgraph Layer2 ["<b>Layer 1: Primitive Operations</b>"]
        SlidingWindow["<b>SlidingWindow</b><br/>━━━━━━━━━━━━━━━━━━━━━<br/>• Manual linked list node management<br/>• Circular buffer maintenance<br/>• Zero-allocation search<br/>• Match yielding<br/>• Page boundary handling"]
    end
    
    Thread --> ScreenSearch
    ScreenSearch --> ActiveSearch
    ScreenSearch --> PageListSearch
    ActiveSearch --> SlidingWindow
    PageListSearch --> SlidingWindow
    
    classDef layer5 fill:#0a0a0a,stroke:#ff0066,stroke-width:3px,color:#ffffff
    classDef layer4 fill:#0f0f0f,stroke:#ff6600,stroke-width:3px,color:#ffffff
    classDef layer3 fill:#141414,stroke:#ffaa00,stroke-width:3px,color:#ffffff
    classDef layer2 fill:#1a1a1a,stroke:#00ff00,stroke-width:3px,color:#ffffff
    
    class Thread layer5
    class ScreenSearch layer4
    class ActiveSearch,PageListSearch layer3
    class SlidingWindow layer2
    
    style Layer5 fill:#050505,stroke:#ff0066,stroke-width:2px,color:#ffffff
    style Layer4 fill:#080808,stroke:#ff6600,stroke-width:2px,color:#ffffff
    style Layer3 fill:#0c0c0c,stroke:#ffaa00,stroke-width:2px,color:#ffffff
    style Layer2 fill:#101010,stroke:#00ff00,stroke-width:2px,color:#ffffff
```

Within the package, we have composable layers that let us test each
point:

- `SlidingWindow`: The lowest layer, the caller manually adds linked
list page nodes and it maintains a sliding window we search over,
yielding results without allocation (besides the circular buffers to
maintain the sliding window).
- `PageListSearch`: Searches a PageList structure in reverse order
(assumption: more recent matches are more valuable than older), but
separates out the `tick` (search, but no PageList access) and `feed`
(PageList access, prep data for search but don't search) operations.
This lets us `feed` in a critical area and `tick` outside. **This
assumes an immutable PageList, so this is for history.**
- `ActiveSearch`: Searches only the active area of a PageList. The
expectation is that the active area changes much more regularly, but it
is also very small (relative to scrollback). Throws away and re-searches
the active area as necessary.
- `ScreenSearch`: Composes the previous three components to coordinate
searching an active terminal screen. You'd have one of these per screen
(alt vs primary). This also caches results unlike the other components,
with the expectation that the caller will revisit the results as screens
change (so if you switch from neovim back to your shell and vice versa
with a search active, it won't start over).
- `Thread`: A dedicated search thread that will receive messages via
MPSC queues while managing the forward progress of a `ScreenSearch` and
sending matches back to the surface mailbox for apprt rendering. **The
thread component is not functional, just boilerplate, in this PR.**

ScreenSearch is a state machine that moves in an iterative `tick` +
`feed` fashion. This will let us "interrupt" the search with updates on
the search thread (read our mailbox via libxev loops for example) and
will let us minimize critical areas with locks (only `feed`).

Each component is significantly unit tested, especially around page
boundary cases. Given the complexity, there is no way this is perfect,
but the architecture is such that we can easily add regression tests as
we find issues.

## Other Changes, Notes

The only change to actually used code is that tracked pins in a
`PageList` can now be flagged as "garbage." A garbage tracked pin is one
that had to be moved in a non-sensical way because the previous location
it tracked has been deleted. This is used by the searcher to detect that
our history was pruned.

**If my assumption about the big lock is wrong** and this ends up being
godawful for performance, then it should still be okay because more
granular locking and reference counting such as that down by @dave-fl in
#8850 can be pushed into these components and reused. So this work is
still valuable on its own.

## Future

This PR is still just a bunch of internals, split out into its own PR so
I don't make one huge 10K diff PR. There are a number of future tasks:

- Flesh out `ScreenSearch` and hook it up to `Thread`
- Pull search thread management into `Surface` (or possibly the render
thread or shared render state since active area changes can be
synchronized with renderer frame rebuilds. Not sure yet.)
- Send updates back to the surface thread so that apprts can update UI.
- Apprt actions, input bindings, etc. to hook this all up (the easy
part, really).

The next step is to continue to flesh out the `ScreenSearch` as required
and hook it up to `Thread`.

**AI disclosure:** AI reviewed the code and assisted with some tests,
but didn't write any of the logic or design. This is beyond its ability
(or my ability to spec it out clearly enough for AI to succeed).
2025-11-14 12:38:06 -08:00