Closes#18930
### Summary
- Fix fast Macbook trackpad/mouse scrolling in terminal applications
with mouse mode enabled
### The Problem
Scrolling with a trackpad in tmux, neovim, or any terminal app that
enables mouse mode was too fast. A gentle swipe would send me through
hundreds of lines, making these apps practically unusable in Zed's
terminal.
### Root Cause
When the terminal is in mouse mode, we send escape sequences to report
scroll events to the application. The bug was in `scroll_report()`:
ca47822667/crates/terminal/src/terminal.rs (L1983-L1988)
That `max(scroll_lines, 1)` meant we'd send **at least 1 scroll event
even when `scroll_lines` was 0**.
ca47822667/crates/terminal/src/mappings/mouse.rs (L96)
On macOS, trackpad gestures fire many small pixel deltas due to scroll
acceleration. Each tiny movement triggered a scroll event, even though
we hadn't accumulated enough pixels for a full line yet. This is a known
issue alacritty/alacritty#2869 - macOS sends fractional line deltas
(like 0.1) instead of whole lines.
### The Fix
Don't send mouse reports when no full line has accumulated
This aligns with Alacritty's approach - accumulate partial scroll
amounts and only report when complete lines are ready.
6ee6e53ee3/alacritty/src/input/mod.rs (L700-L730)
### Testing
Tested trackpad scrolling in:
- tmux (pane navigation, scrollback)
- neovim (buffer scrolling)
- opencode (TUI navigation)
All scroll smoothly now.
### Demo
The demo shows the behavior of the scrolling. I can go fast or I can go
slow
https://github.com/user-attachments/assets/14bfc2f4-f286-4341-bf55-4ced894d63f9
Release Notes:
- Fixed trackpad scrolling being too fast in terminal applications with
mouse mode enabled (tmux, neovim, opencode, etc.)
Closes #ISSUE
Moves the settings content definitions into their own crate, so that
they are compiled+cached separately from settings, primarily to avoid
recompiles due to changes in gpui. In that vain many gpui types such as
font weight/features, and `SharedString` were replaced in the content
crate, either with `*Content` types for font/modifier things, or
`String`/`Arc<str>` for `SharedString`. To make the conversions easy a
new trait method in the settings crate named `IntoGpui::into_gpui`
allows for `into()` like conversions to the gpui types in
`from_settings` impls.
Release Notes:
- N/A
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
## Motivation
This PR unifies the async execution infrastructure between GPUI and
other components that depend on the `scheduler` crate (such as our cloud
codebase). By having a scheduler that lives independently of GPUI, we
can enable deterministic testing across the entire stack - testing GPUI
applications alongside cloud services with a single, unified scheduler.
## Summary
This PR completes the integration of the `scheduler` crate into GPUI,
unifying async execution and enabling deterministic testing of GPUI
combined with other components that depend on the scheduler crate.
## Key Changes
### Scheduler Integration (Phases 1-5, previously completed)
- `TestDispatcher` now delegates to `TestScheduler` for timing, clock,
RNG, and task scheduling
- `PlatformScheduler` implements the `Scheduler` trait for production
use
- GPUI executors wrap scheduler executors, selecting `TestScheduler` or
`PlatformScheduler` based on environment
- Unified blocking logic via `Scheduler::block()`
### Dead Code Cleanup
- Deleted orphaned `crates/gpui/src/platform/platform_scheduler.rs`
(older incompatible version)
## Intentional Removals
### `spawn_labeled` and `deprioritize` removed
The `TaskLabel` system (`spawn_labeled`, `deprioritize`) was removed
during this integration. It was only used in a few places for test
ordering control.
cc @maxbrunsfeld @as-cii - The new priority-weighted scheduling in
`TestScheduler` provides similar functionality through
`Priority::High/Medium/Low`. If `deprioritize` is important for specific
test scenarios, we could add it back to the scheduler crate. Let me know
if this is blocking anything.
### `start_waiting` / `finish_waiting` debug methods removed
Replaced by `TracingWaker` in `TestScheduler` - run tests with
`PENDING_TRACES=1` to see backtraces of pending futures when parking is
forbidden.
### Realtime Priority removed
The realtime priority feature was unused in the codebase. I'd prefer to
reintroduce it when we have an actual use case, as the implementation
(bounded channel with capacity 1) could potentially block the main
thread. Having a real use case will help us validate the design.
## Testing
- All GPUI tests pass
- All scheduler tests pass
- Clippy clean
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ GPUI │
│ ┌──────────────────────┐ ┌────────────────────────────┐ │
│ │ gpui::Background- │ │ gpui::ForegroundExecutor │ │
│ │ Executor │ │ - wraps scheduler:: │ │
│ │ - scheduler: Arc< │ │ ForegroundExecutor │ │
│ │ dyn Scheduler> │ └────────────┬───────────────┘ │
│ └──────────┬───────────┘ │ │
│ │ │ │
│ └──────────┬──────────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Arc<dyn Scheduler> │ │
│ └───────────┬───────────┘ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ PlatformScheduler│ │ TestScheduler │ │
│ │ (production) │ │ (deterministic) │ │
│ └──────────────────┘ └────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
Release Notes:
- N/A
---------
Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Yara <git@yara.blue>
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Previously, if you stopped the terminal prematurely, the agent would
assume the terminal process had timed out. Now it knows what happened
and can see the output:
<img width="718" height="885" alt="Screenshot 2026-01-07 at 12 40 23 AM"
src="https://github.com/user-attachments/assets/a5ea14b2-249c-4ada-9f20-d6b608f829e5"
/>
Release Notes:
- Stopping the terminal tool now allows the agent to see its output up
to that point.
## Problem
When clicking Stop button on a terminal tool call (or using terminal
kill bindings), only the shell process was killed. Child processes (like
`sleep`, `npm run`, etc.) continued running as orphans.
## Solution
On Unix, use `killpg()` instead of `sysinfo::Process::kill()` to
terminate the entire foreground process group.
`tcgetpgrp()` already returns the foreground process group ID, so
`killpg()` is the correct syscall to use here.
## Testing
1. Run a long command in terminal tool (e.g. `sleep 30`)
2. Click Stop button
3. Verify with `ps aux | grep sleep` that the process is actually killed
## Notes
- This fix is Unix-only (Linux, macOS)
- Windows behavior unchanged — may need separate investigation with Job
Objects
## Release Notes
- Fixed terminal Stop button not killing child processes on Unix (Linux,
macOS)
Now that the edit mode in number fields is finally working well, we can
make the UX of editing font sizes much nicer because you can now type
inside the number field :)
https://github.com/user-attachments/assets/8df7c6ee-e82b-4e10-a175-e0ca5f1bab1f
Release Notes:
- settings UI: Improved the UX of editing font size fields as you can
now type the desired value as opposed to just using the
decrement/increment buttons.
Related to
- #44407
This PR further improves performance for regex hyperlink finding by
eliminating unnecessary regex matching. Currently, we repeatedly search
for matches from the start of the line until the match contains the
hovered point. This is only required to support custom regexes which
match strings containing spaces, with multiple matches on a single line.
This isn't actually a useful scenario, and is no longer supported. This
PR changes to only search twice, the first match starting from the start
of the line, and the hovered word (space-delimited). The most dramatic
improvement is for long lines with many words.
In addition to the above changes, this PR:
- Adds test for the scenarios from #44407 and #44510
- Simplifies the logic added in #44407
Performance measurements
For the scenario from #44407, this improves the perf test's iteration
time from 1.22ms to 0.47ms.
main:
| Branch | Command | Iter/sec | Mean [ms] | SD [ms] | Iterations |
Importance (weight) |
|:---|:---|---:|---:|---:|---:|---:|
| main |
terminal_hyperlinks::tests::path::perf::pr_44407_hyperlink_benchmark |
819.64 | 937.60 | 2.20 | 768 | average (50) |
| this PR |
terminal_hyperlinks::tests::path::perf::pr_44407_hyperlink_benchmark |
2099.79 | 1463.20 | 7.20 | 3072 | average (50) |
Release Notes:
- terminal: Improve path hyperlink performance for long lines
Closes#41994
This PR introduces Element-bounded drag tolerance for Ctrl/Cmd+click in
terminal.
Previously, Ctrl/Cmd+click on terminal links required pixel-perfect
accuracy. Any mouse movement during the click would cancel the
navigation, making it frustrating to click on links, especially on
high-DPI displays or with sensitive mice.
Users can now click anywhere within a clickable element (file path, URL,
hyperlink), drag the cursor anywhere within that same element's
boundaries and release to trigger navigation
Implementation:
- Stores detected element metadata (`text` and `grid_range`) on
Ctrl/Cmd+mouse-down
- Tracks cursor position during drag, preserving click state while
within element bounds
- Verifies element match on mouse-up before triggering navigation
- Uses existing `find_from_grid_point()` for element detection
Before:
[before.webm](https://github.com/user-attachments/assets/ee80de66-998e-4d8e-94d0-f5e65eb06d22)
After:
[after.webm](https://github.com/user-attachments/assets/7c9ddd9e-cfc1-4c79-b62c-78e9d909e6f4)
Release Notes:
- terminal: Fixed an issue where `ctrl|cmd+click` on links was very
sensitive to mouse movement. Clicking links now tolerates mouse movement
within the same clickable element, making link navigation more reliable
---------
Co-authored-by: Ben Kunkle <ben@zed.dev>
Related to:
- #44510
- #44407
Previously we were searching for hyperlinks on every scroll, even if Cmd
was not held. With this PR,
- We only search for hyperlinks on scroll if Cmd is held
- We now clear `last_hovered_word` in all cases where Cmd is not held
- Renamed `word_from_position` -> `schedule_find_hyperlink`
- Simplified logic in `schedule_find_hyperlink`
Performance measurements
The test scrolls up and down 20,000x in a loop. However, since this PR
is just removing a code path that was very dependent on the length of
the line in terminal, it's not super meaningful as a comparison. The
test uses a line length of "long line ".repeat(1000), and in main the
performance is directly proportional to the line length, so for
benchmarking it in main it only scrolls up and down 20x. I think all
that is really useful to say is that currently scrolling is slow, and
proportional to the line length, and with this PR it is buttery-smooth
and unaffected by line length. I've included a few data points below
anyway. At least the test can help catch future regressions.
| Branch | Command | Scrolls | Iter/sec | Mean [ms] | SD [ms] |
Iterations | Importance (weight) |
|:---|:---|---:|---:|---:|---:|---:|---:|
| main | tests::perf::scroll_long_line_benchmark | 40 | 16.85 | 712.00 |
2.80 | 12 | average (50) |
| this PR | tests::perf::scroll_long_line_benchmark | 40 | 116.22 |
413.60 | 0.50 | 48 | average (50) |
| this PR | tests::perf::scroll_long_line_benchmark | 40,000 | 9.19 |
1306.40 | 7.00 | 12 | average (50) |
| only overhead | tests::perf::scroll_long_line_benchmark | 0 | 114.29 |
420.90 | 2.00 | 48 | average (50) |
Release Notes:
- terminal: Improved scroll performance
Closes#43345
The list of characters comes from the linkify crate, which is already
used for URL detection in the editor:
5239e12e26/src/url.rs (L228)
Release Notes:
- Improved url links detection in terminals.
---------
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Adds an optional `timeout_ms` parameter to the terminal tool that allows
bounding the runtime of shell commands. When the timeout expires, the
running terminal task is killed and the tool returns with the partial
output captured so far.
## Summary
This PR adds the ability for the agent to specify a maximum runtime when
invoking the terminal tool. This helps prevent indefinite hangs when
running commands that might wait for network, user prompts, or long
builds/tests.
## Changes
- Add `timeout_ms` field to `TerminalToolInput` schema
- Extend `TerminalHandle` trait with `kill()` method
- Implement `kill()` for `AcpTerminalHandle` and `EvalTerminalHandle`
- Race terminal exit against timeout, killing on expiry
- Update system prompt to recommend using timeouts for long-running
commands
- Add test for timeout behavior
- Update `.rules` to document GPUI executor timers for tests
## Testing
- Added `test_terminal_tool_timeout_kills_handle` which verifies that
when a timeout is specified and expires, the terminal handle is killed
and the tool returns with partial output.
- All existing agent tests pass.
Release Notes:
- agent: Added optional `timeout_ms` parameter to the terminal tool,
allowing the agent to bound command runtime and prevent indefinite hangs
Fixes#33958
## Problem
When opening a terminal in Zed, `SHLVL` incorrectly starts at 2 instead
of 1. On `workspace: reload`, it increases by 2 instead of 1.
## Root Cause
1. Zed's `shell_env::capture()` spawns a login shell (`-l -i -c`) to
capture the user's environment, which increments `SHLVL`
2. The captured `SHLVL` is passed through to the PTY options
3. When alacritty_terminal spawns the user's shell, it increments
`SHLVL` again
Result: `SHLVL` = captured value + 1 = 2 (when launched from Finder)
## Solution
Remove `SHLVL` from the environment in `TerminalBuilder::new()` before
passing it to alacritty_terminal. This allows the spawned shell to
initialize `SHLVL` to 1 on its own, matching the behavior of standalone
terminal emulators like iTerm2, Kitty, and Alacritty.
## Testing
- Launch Zed from Finder → open terminal → `echo $SHLVL` → should output
`1`
- Launch Zed from shell → open terminal → `echo $SHLVL` → should output
`1`
- `workspace: reload` → open terminal → `echo $SHLVL` → should remain
`1`
- Tested with bash, zsh, fish
Release Notes:
- Fixed terminal `$SHLVL` starting at 2 instead of 1
([#33958](https://github.com/zed-industries/zed/issues/33958))
Problem statement: When given a line that contained a lot of matches of
your hyperlink regex of choice (thanks to #40305), we would look for
matches
that intersected with currently hovered point. This is *hella*
expensive, because we would re-walk the whole alacritty grid for each
match. With the repro that Joseph shared, we had to go through 4000 such
matches on each frame render.
Problem solution: We now convert the hovered point into a range within
the line (byte-wise) in order to throw away matches that do not
intersect the
hovered range. This lets us avoid performing the unnecessary conversion
when we know it's never going to yield a match range that intersects the
hovered point.
Release Notes:
- terminal: Fixed performance regression when handling long lines.
---------
Co-authored-by: Dave Waggoner <waggoner.dave@gmail.com>
The fix for this is emitting a wake-up event to tell the terminal to
recalculate its search highlights on resize.
Release Notes:
- terminal: Fix bug where search match highlights wouldn't update their
position when resizing the terminal.
Closes:
- #12338
- #40202
1. Adds two new settings which allow customizing the set of regexes used
to identify path hyperlinks in terminal
1. Fixes path hyperlinks for paths containing unicode emoji and
punctuation, for example, `mojo.🔥`
1. Fixes path hyperlinks for Windows verbatim paths, for example,
`\\?\C:\Over\here.rs`.
1. Improves path hyperlink performance, especially for terminals with a
lot of content
1. Replaces existing custom hard-coded default path hyperlink parsing
logic with a set of customizable default regexes
## New settings
(from default.json)
### terminal.path_hyperlink_regexes
Regexes used to identify paths for hyperlink navigation. Supports
optional named capture
groups `path`, `line`, `column`, and `link`. If none of these are
present, the entire match
is the hyperlink target. If `path` is present, it is the hyperlink
target, along with `line`
and `column` if present. `link` may be used to customize what text in
terminal is part of the
hyperlink. If `link` is not present, the text of the entire match is
used. If `line` and
`column` are not present, the default built-in line and column suffix
processing is used
which parses `line:column` and `(line,column)` variants. The default
value handles Python
diagnostics and common path, line, column syntaxes. This can be extended
or replaced to
handle specific scenarios. For example, to enable support for
hyperlinking paths which
contain spaces in rust output,
```
[
"\\s+(-->|:::|at) (?<link>(?<path>.+?))(:$|$)",
"\\s+(Compiling|Checking|Documenting) [^(]+\\((?<link>(?<path>.+))\\)"
],
```
could be used. Processing stops at the first regex with a match, even if
no link is
produced which is the case when the cursor is not over the hyperlinked
text. For best
performance it is recommended to order regexes from most common to least
common. For
readability and documentation, each regex may be an array of strings
which are collected
into one multi-line regex string for use in terminal path hyperlink
detection.
### terminal.path_hyperlink_timeout_ms
Timeout for hover and Cmd-click path hyperlink discovery in
milliseconds. Specifying a
timeout of `0` will disable path hyperlinking in terminal.
## Performance
This PR fixes terminal to only search the hovered line for hyperlinks
and adds a benchmark. Before this fix, hyperlink detection grows
linearly with terminal content, with this fix it is proportional only to
the hovered line. The gains come from replacing
`visible_regex_match_iter`, which searched all visible lines, with code
that only searches the line hovered on (including if the line is
wrapped).
Local benchmark timings (terminal with 500 lines of content):
||main|this PR|Δ|
|-|-|-:|-|
| cargo_hyperlink_benchmark | 1.4 ms | 13 µs | -99.0% |
| rust_hyperlink_benchmark | 1.2 ms | 11 µs | -99.1% |
| ls_hyperlink_benchmark | 1.3 ms | 7 µs | -99.5% |
Release Notes:
- terminal: New settings to allow customizing the set of regexes used to
identify path hyperlinks in terminal
- terminal: Fixed terminal path hyperlinks for paths containing unicode
punctuation and emoji, e.g. mojo.🔥
- terminal: Fixed path hyperlinks for Windows verbatim paths, for
example, `\\?\C:\Over\here.rs`
- terminal: Improved terminal hyperlink performance, especially for
terminals with a lot of content visible
Closes#5130
Release Notes:
- Added setting option for scroll multiplier of the terminal
---------
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Co-authored-by: MrSubidubi <finn@zed.dev>
The "Option" key only exists on a Mac. On other operating systems, it is
always expected that the Alt key generates escaped characters.
Fixes https://github.com/zed-industries/zed/issues/40583
Release Notes:
- N/A
When rerunning a task, our process id fetching seems to sometimes return
the previous terminal's process id when respawning the task, causing us
to kill the new terminal once the previous one drops as we spawn a new
one, then drop the old one. This results in rerun sometimes spawning a
blank task as the terminal immediately exits. The fix here is simple, we
actually want to kill the process running inside the terminal process,
not the terminal process itself when we exit in the terminal.
No relnotes as this was introduced yesterday in
https://github.com/zed-industries/zed/pull/41562
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Attempt 2 for https://github.com/zed-industries/zed/pull/40774
We were spawning the process on the foreground thread before which can
block an arbitrary amount of time. Likewise we no longer block
deserialization on the terminal loading.
Release Notes:
- Improved startup time on systems with slow process spawning
capabilities
Using `shlex` unconditionally is dangerous as it assumes the underlying
shell is POSIX which is not the case for PowerShell, CMD, or Nushell.
Therefore, whenever we want to quote the args we should utilise our
helper `util:🐚:ShellKind::try_quote` which takes into account
which shell is being used to actually exec/spawn the invocation.
Release Notes:
- N/A
---------
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
We were spawning the process on the foreground thread before which can
block an arbitrary amount of time. Likewise we no longer block
deserialization on the terminal loading.
Release Notes:
- Improved startup time on systems with slow process spawning
capabilities
We've been considering removing workspace-hack for a couple reasons:
- Lukas ran into a situation where its build script seemed to be causing
spurious rebuilds. This seems more likely to be a cargo bug than an
issue with workspace-hack itself (given that it has an empty build
script), but we don't necessarily want to take the time to hunt that
down right now.
- Marshall mentioned hakari interacts poorly with automated crate
updates (in our case provided by rennovate) because you'd need to have
`cargo hakari generate && cargo hakari manage-deps` after their changes
and we prefer to not have actions that make commits.
Currently removing workspace-hack causes our workspace to grow from
~1700 to ~2000 crates being built (depending on platform), which is
mainly a problem when you're building the whole workspace or running
tests across the the normal and remote binaries (which is where
feature-unification nets us the most sharing). It doesn't impact
incremental times noticeably when you're just iterating on `-p zed`, and
we'll hopefully get these savings back in the future when
rust-lang/cargo#14774 (which re-implements the functionality of hakari)
is finished.
Release Notes:
- N/A
A small follow-up to the settings refactor of a few weeks ago to move
all the VSCode settings imports
to one place.
This should make it easier to spot missing imports, and easier to test
the importer.
Release Notes:
- N/A
Closes#39614
The `ShellKind` struct is built on Windows' side, meaning that when
connecting to remotes, we fall back to PowerShell construction, even if
the shell program we are spawning is a unix program.
This broke tasks creation since we are using the shell kind to construct
args:
d04ac864b8/crates/project/src/terminals.rs (L149)
In normal terminals this only affected activation scripts (only place
where shell kind is used)
I don't have a Windows machine to test it, so I would appreciate any
help with testing!
Release Notes:
- Fixed an issue where tasks could not be executed in Windows WSL
---------
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Closes https://github.com/zed-industries/zed/issues/39259
- Fixes import of `editor.fontFamily` (we were looking for the wrong
key)
- Adds basic support for the CSS font-family syntax used by VS Code,
including font fallback
Release Notes:
- N/A
Two tweaks were required to ensure we correctly clear the shell after
running an activate script(s):
1. PowerShell upon receiving `\r\n` input, will enter the continuation
mode (>>). To avoid this, we send an "enter" key press instead `\x0d`.
2. In order to clear the terminal _after_ issuing all activation
commands, we need to take into account the asynchronous nature of the
activation process:
- We write the command to run the script to PTY
- We send "enter" (It is now being processed by the shell) At this point
we need to wait for the shell to finish executing before we clear the
terminal. Otherwise we will create a race where we might clear the
terminal _before_ the shell finished executing the activation script(s).
- Write `clear`/`cls` command to PTY
- Send "enter" This way we guarantee that we clear the terminal _after_
all scripts were executed.
Closes#38474
Release Notes:
- N/A
I mistakenly broke this when refactoring settings
Closes#39479
Release Notes:
- Fixed a bug where you could no longer configure `terminal.shell` in
project settings