Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
3ac9c7b
Prototype: hand ctrl-r to fzf/atuin history search (CORE-3807)
warp-agent-staging[bot] Aug 25, 2026
44689b4
Address review findings: narrow ctrl-r widget detection, verify hando…
warp-agent-staging[bot] Aug 25, 2026
166d261
Add bash support for ctrl-r external history handoff (fzf) and fix sh…
warp-agent-staging[bot] Aug 25, 2026
c759cca
Fix hide_block to actually trigger a re-draw
warp-agent-staging[bot] Aug 25, 2026
29028e1
Fix atuin ctrl-r invocation for zsh and add atuin support for bash
warp-agent-staging[bot] Aug 25, 2026
0c7bf37
Spike: fish shell_plugins plumbing + external ctrl-r detection/helper…
warp-agent Aug 25, 2026
a57ffb6
Fix ctrl-r widget detection to use exact allowlist and live bash binding
warp-agent-staging[bot] Aug 25, 2026
c8dee34
Fix fish ctrl-r history wrapper: reject-all bug and re-source recursion
warp-agent-staging[bot] Aug 25, 2026
51fb584
Fix fish history wrapper: preserve a hook installed between two sourc…
warp-agent-staging[bot] Aug 25, 2026
279893b
Fix ctrl-r handoff polluting atuin's own history database
warp-agent-staging[bot] Aug 25, 2026
7d9b66d
Fix ctrl-r helper invocation polluting atuin's own history database
warp-agent-staging[bot] Aug 25, 2026
58ce16a
Merge remote-tracking branch 'origin/factory/core-3807-fzf-ctrl-r-han…
warp-agent-staging[bot] Aug 25, 2026
783e173
Clarify atuin cleanup comments after merging with leading-space preve…
warp-agent-staging[bot] Aug 25, 2026
b2cbf5c
Remove atuin search --delete calls: fuzzy match risks deleting unrela…
warp-agent-staging[bot] Aug 25, 2026
1ebf8e9
Detect bash+atuin ctrl-r via atuin's init-time flag on newer atuin
warp-agent Aug 25, 2026
b10681a
Rename FzfCtrlRHandoff feature flag to ShellWidgetHandoff
warp-agent-staging[bot] Aug 25, 2026
52e630a
Add ctrl-t file-search Rust plumbing parallel to ctrl-r
warp-agent-staging[bot] Aug 25, 2026
607d640
Wire the ctrl-t keybinding to the external file-search handoff
warp-agent-staging[bot] Aug 25, 2026
93d4c4d
Add ctrl-t file-search shell detection and helpers for bash, zsh, fish
warp-agent-staging[bot] Aug 25, 2026
f952027
Fix ctrl-t swallowing the key on older fzf: verify the picker functio…
warp-agent-staging[bot] Aug 25, 2026
60068f0
Add missing ExternalCtrlTSelection case to hook-coverage test
warp-agent-staging[bot] Aug 25, 2026
3e526f6
Use the workspace Command wrapper in the fish bootstrap test
warp-agent-staging[bot] Aug 25, 2026
9072aa7
Don't let a hidden block supply the tab's last-completed-command label
warp-agent-staging[bot] Aug 25, 2026
eabcfce
Stop ctrl-t being swallowed, and guard bash's picker before claiming …
warp-agent-staging[bot] Aug 25, 2026
7419eb0
Gate the ctrl-t binding on the ShellWidgetHandoff flag
warp-agent-staging[bot] Aug 25, 2026
315e032
Add ctrl-t handoff regression tests
warp-agent-staging[bot] Aug 25, 2026
a8652c4
Add ctrl-t pty-fallback and cursor-splice regression tests
warp-agent-staging[bot] Aug 25, 2026
a36eaa8
Cover the ctrl-t cancel cursor restore and the flag-off binding contract
warp-agent-staging[bot] Aug 25, 2026
8f861ce
Fish ctrl-t: call the user's real fzf-file-widget instead of reimplem…
warp-agent-staging[bot] Aug 26, 2026
10b3be0
Fix fish ctrl-t cancel losing cursor position; add real-trigger curso…
warp-agent-staging[bot] Aug 26, 2026
7a6a15b
Address review: fix multiline draft corruption, draft-file leak on wr…
warp-agent-staging[bot] Aug 26, 2026
ea48e48
Merge origin/master: reconcile with the terminal->warp_terminal crate…
warp-agent-staging[bot] Aug 26, 2026
3abda41
Fix fish ctrl-r: call fzf-history-widget directly, not hand-built ver…
warp-agent-staging[bot] Aug 26, 2026
6741b36
Fix multiline splitting in ctrl-r/ctrl-t fish selections; add regress…
warp-agent-staging[bot] Aug 26, 2026
4c62d8b
Switch ctrl-t draft file to NUL-delimited format to preserve trailing…
warp-agent-staging[bot] Aug 26, 2026
51da3c1
Fix CI: LF-normalize bootstrap scripts for Windows checkouts; skip ba…
warp-agent-staging[bot] Aug 26, 2026
2a277f3
Gate the bash ctrl-t detection tests on bash 4.3, not 4.0
warp-agent-staging[bot] Aug 26, 2026
a028e97
Gate the bash ctrl-t detection tests on a real bind -x round-trip probe
warp-agent-staging[bot] Aug 26, 2026
6eeb3df
Probe the exact bind -X extraction pipeline, not just a raw listing
warp-agent-staging[bot] Aug 26, 2026
047bcc7
Merge remote-tracking branch 'origin/master' into factory/core-3807-f…
warp-agent-staging[bot] Aug 26, 2026
c174dbb
Merge remote-tracking branch 'origin/master' into factory/core-3807-f…
warp-agent-staging[bot] Aug 26, 2026
3451b91
Replace ctrl-t draft temp file with a hex-encoded argument (CORE-3807)
warp-agent-staging[bot] Aug 26, 2026
76ac780
Add fzf-gated integration tests for ctrl-r and ctrl-t handoff.
warp-agent-staging[bot] Aug 30, 2026
bd0cddc
Use command::blocking::Command in fzf integration tests.
warp-agent-staging[bot] Aug 30, 2026
2ae42a4
Harden fzf integration tests for bash helper preexec.
warp-agent-staging[bot] Aug 30, 2026
cc23ce5
Merge origin/master into factory/core-3807-fzf-ctrl-r-handoff.
warp-agent-staging[bot] Aug 30, 2026
bdfa6b7
Assert fzf accept does not execute the selected command.
warp-agent-staging[bot] Aug 30, 2026
e45376f
Detect fzf ctrl-r/ctrl-t on bash 5.3 bind -X output.
warp-agent-staging[bot] Aug 30, 2026
a9bddde
Skip Windows WSL bash stub in bind -X unit tests.
warp-agent-staging[bot] Aug 30, 2026
d1275cc
Move ShellWidgetHandoff from default/dogfood into Preview.
warp-agent-staging[bot] Aug 30, 2026
fde719e
Retry empty fish commandline read after fzf-history-widget.
warp-agent-staging[bot] Aug 31, 2026
f26515a
EXPERIMENT: revert ShellWidgetHandoff Preview demotion only.
warp-agent-staging[bot] Aug 31, 2026
4e3f093
Wire Preview flag sets into the integration channel.
warp-agent-staging[bot] Aug 31, 2026
a3b4e44
Restore empty fish commandline retry after fzf-history-widget.
warp-agent-staging[bot] Aug 31, 2026
8f277c5
Revert "Restore empty fish commandline retry after fzf-history-widget."
warp-agent-staging[bot] Aug 31, 2026
fe82875
Remove channel-wide Preview/Dogfood wiring from integration binaries.
warp-agent-staging[bot] Aug 31, 2026
04810bc
Merge remote factory/core-3807-fzf-ctrl-r-handoff into local ChannelS…
warp-agent-staging[bot] Aug 31, 2026
54cdabc
Restore empty fish commandline retry after remote revert.
warp-agent-staging[bot] Aug 31, 2026
fc4a368
Restore Preview demotion plus integration channel flag wiring.
warp-agent-staging[bot] Aug 31, 2026
7544c37
Restore bare Integration ChannelState and fish ctrl-r settle.
warp-agent-staging[bot] Aug 31, 2026
2d647e8
Force-enable only ShellWidgetHandoff for the integration channel.
warp-agent-staging[bot] Aug 31, 2026
8d8de59
Omit fzf helper invocations from Warp command history.
warp-agent-staging[bot] Aug 31, 2026
5a371dd
Revert fzf integration tests.
warp-agent-staging[bot] Aug 31, 2026
803dc95
manual cleanup
acarl005 Aug 31, 2026
ddb3a8d
Unify shell-widget handoff pending state and restore master fish decode.
warp-agent-staging[bot] Aug 31, 2026
a5e00f8
more cleanup
acarl005 Aug 31, 2026
6e761fa
Unify ExternalCtrlR/T selection into one shell-widget hook.
warp-agent-staging[bot] Aug 31, 2026
fa8fffd
Remove redundant shell-widget handoff token.
warp-agent-staging[bot] Aug 31, 2026
54e0d16
Make shell-widget selection apply an instance method.
warp-agent-staging[bot] Aug 31, 2026
4152cf5
Inline ctrl-t draft encoding and drop helper tests.
warp-agent-staging[bot] Aug 31, 2026
c5feca6
Drop helper_command param from ctrl-r handoff trigger.
warp-agent-staging[bot] Aug 31, 2026
da4a716
Move ctrl-t helper command const into input.rs.
warp-agent-staging[bot] Aug 31, 2026
807dd86
Replace ShellWidgetHandoffKind with ShellWidgetApplyMode.
warp-agent-staging[bot] Sep 1, 2026
18e3d5b
Unify shell-widget handoff triggers and drop added test comments.
warp-agent-staging[bot] Sep 1, 2026
7848009
Move shell-widget helper command constants into view.rs.
warp-agent-staging[bot] Sep 1, 2026
4c89de0
more manual cleanup
acarl005 Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,7 @@ git_credential_refresh = []
prompt_cache_expiry_warning = []
osc_hyperlinks = []
ctrl_c_cancels_third_party_harness = []
shell_widget_handoff = []

[package.metadata.bundle.bin.warp-oss]
category = "public.app-category.developer-tools"
Expand Down
73 changes: 70 additions & 3 deletions app/assets/bundled/bootstrap/bash_body.sh
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,41 @@ if [ -z "$WARP_BOOTSTRAPPED" ]; then
READLINE_LINE=""
}

# Runs the shell's ctrl-r history widget as a foreground command.
warp_run_external_ctrl_r_widget () {
local result=""
case "$_WARP_EXTERNAL_CTRL_R_WIDGET" in
__fzf_history__)
result="$(__fzf_history__)"
;;
__atuin_history)
# Bypass atuin's own bash key-binding machinery entirely and invoke the underlying
# `atuin search` command directly, exactly as atuin's own integration does
# (__atuin_search_cmd's non-tmux branch).
result="$(ATUIN_SHELL=bash atuin search -i 3>&1 1>&2 2>&3 3>&-)"
# If the user has atuin's enter_accept config on, Enter both selects and runs the
# command, signaled by this prefix; we only ever want the selection, never to run
# it, so strip the prefix in both cases (see atuin's __atuin_history for the same
# check).
result="${result#__atuin_accept__:}"
;;
esac
local warp_escaped_selection="$(warp_escape_json "$result")"
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
}

# Runs the shell's own ctrl-t file-search widget as a foreground command.
warp_run_external_ctrl_t_widget () {
local result=""
case "$_WARP_EXTERNAL_CTRL_T_WIDGET" in
fzf-file-widget)
result="$(__fzf_select__)"
;;
esac
local warp_escaped_selection="$(warp_escape_json "$result")"
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
}

# Check whether the prompt-related variables have OSC prompt marker sequences,
# and if not, wrap them with the appropriate markers so that we can direct the
# prompt bytes to the appropriate grids.
Expand Down Expand Up @@ -1407,13 +1442,13 @@ esac
# rcfiles.
USER_HISTCONTROL="$HISTCONTROL"

# Add a pattern to ignore in-band commands in shell history, while preserving the user's
# Add patterns to ignore in-band commands in shell history, while preserving the user's
# HISTIGNORE value which may been set in an RC file sourced above. It is important to
# ensure that this happens _after_ the user's RC files have been sourced.
if [[ ! -z $HISTIGNORE ]]; then
HISTIGNORE="*warp_run_generator_command*:$HISTIGNORE"
HISTIGNORE="*warp_run_generator_command*:*warp_run_external_ctrl_r_widget*:*warp_run_external_ctrl_t_widget*:$HISTIGNORE"
else
HISTIGNORE="*warp_run_generator_command*"
HISTIGNORE="*warp_run_generator_command*:*warp_run_external_ctrl_r_widget*:*warp_run_external_ctrl_t_widget*"
fi

# If the user has PROMPT_COMMAND set in their bootstrap scripts,
Expand Down Expand Up @@ -1528,6 +1563,38 @@ esac

shell_plugins=()

# Detect whether ctrl-r has been rebound to fzf's or atuin's bash history widget, so Warp
# can hand ctrl-r off to it.
_WARP_EXTERNAL_CTRL_R_WIDGET=""
if [ "$WARP_IN_MSYS2" = false ]; then
warp_ctrl_r_binding="$(bind -X 2>/dev/null | command -p sed -n 's/^"\\C-r"[ :] *"\(.*\)"$/\1/p')"
case "$warp_ctrl_r_binding" in
__fzf_history__|__atuin_history)
_WARP_EXTERNAL_CTRL_R_WIDGET="$warp_ctrl_r_binding"
shell_plugins+=(external_ctrl_r_history)
;;
esac
# atuin >= 18.10 binds ctrl-r through the indirect dispatcher above rather than a plain
# `bind -x`. Instead use atuin's own init-time flag ($__atuin_bind_ctrl_r) plus
# __atuin_history being defined.
if [ -z "$_WARP_EXTERNAL_CTRL_R_WIDGET" ] && [ "$__atuin_bind_ctrl_r" = true ] &&
declare -F __atuin_history >/dev/null; then
_WARP_EXTERNAL_CTRL_R_WIDGET="__atuin_history"
shell_plugins+=(external_ctrl_r_history)
fi

_WARP_EXTERNAL_CTRL_T_WIDGET=""
warp_ctrl_t_binding="$(bind -X 2>/dev/null | command -p sed -n 's/^"\\C-t"[ :] *"\(.*\)"$/\1/p')"
case "$warp_ctrl_t_binding" in
fzf-file-widget)
if declare -F __fzf_select__ >/dev/null; then
_WARP_EXTERNAL_CTRL_T_WIDGET="$warp_ctrl_t_binding"
shell_plugins+=(external_ctrl_t_file)
fi
;;
esac
fi

function warp_bootstrapped () {
local aliases="`alias`"
local env_var_names="`compgen -e`"
Expand Down
130 changes: 129 additions & 1 deletion app/assets/bundled/bootstrap/fish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,114 @@ function warp_escape_json
string join \n $argv | command sed -E 's/(["\\\\])/\\\\\\1/g; s/'\b'/\\\\b/g; s/'\t'/\\\\t/g; s/'\f'/\\\\f/g; s/'\r'/\\\\r/g; $!s/$/\\\\n/' | command tr -d '\n'
end

# Reports the widget `^R` is bound to, if the user has rebound it away from fish's own
# history search. Returns non-zero when `^R` is still on a fish default.
function warp_external_ctrl_r_widget
# fish >= 4.0 renamed key specifications, so `bind` echoes back `ctrl-r` where earlier
# versions echo `\cr`.
set -l widget ""
for binding in (bind \cr 2>/dev/null)
if string match --quiet -- 'bind --preset *' "$binding"
continue
end
# Strip the leading `bind [-M <mode>] <key>`, leaving just the widget/command.
set widget (string replace --regex -- '^bind (-M \S+ +)?\S+ +' '' "$binding")
end
test -n "$widget"; or return 1
echo "$widget"
end

# Reports the widget `^T` is bound to, if the user has rebound it away from fish's default (no
# binding at all). Returns non-zero when `^T` has no non-preset binding.
function warp_external_ctrl_t_widget
set -l widget ""
for binding in (bind \ct 2>/dev/null)
if string match --quiet -- 'bind --preset *' "$binding"
continue
end
set widget (string replace --regex -- '^bind (-M \S+ +)?\S+ +' '' "$binding")
end
test -n "$widget"; or return 1
echo "$widget"
end

# Runs the shell's own ctrl-r history tool as a foreground command.
function warp_run_external_ctrl_r_widget
set -l result ""
switch "$_WARP_EXTERNAL_CTRL_R_WIDGET"
case 'fzf-history-widget'
test -z "$fish_private_mode"; and builtin history merge
fzf-history-widget
set result (commandline | string collect)
commandline -r ''
case '_atuin_search'
# atuin writes its TUI to stdout and the selection to fd 3, so the two are swapped here to
# leave the UI on the terminal and capture only the selection.
set -l output (ATUIN_SHELL_FISH=t ATUIN_LOG=error atuin search -i 3>&1 1>&2 2>&3 | string collect)
# atuin prefixes the selection with __atuin_accept__: when `enter_accept` is on and the
# user pressed enter. Warp always inserts without executing, so the prefix is dropped.
set result (string replace "__atuin_accept__:" "" -- "$output" | string collect)
end
set -l warp_escaped_selection (warp_escape_json "$result")
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
end

function warp_ctrl_t_widget_result
test "$argv[1]" = "$argv[2]"; or string collect -- "$argv[2]"
end

# Runs fzf directly against a find-style command as a foreground command.
function warp_run_external_ctrl_t_widget
set -l result ""
switch "$_WARP_EXTERNAL_CTRL_T_WIDGET"
case 'fzf-file-widget'
set -l warp_ctrl_t_parts (string split -m 1 -- ':' "$argv[1]")
set -l char_cursor $warp_ctrl_t_parts[1]
set -l original_line (warp_hex_decode_string $warp_ctrl_t_parts[2] | string collect --no-trim-newlines --allow-empty)
commandline -r -- $original_line
commandline -C -- $char_cursor
fzf-file-widget
set -l cl_readback (commandline | string collect)
set result (warp_ctrl_t_widget_result "$original_line" "$cl_readback")
commandline -r ''
end
set -l warp_escaped_selection (warp_escape_json "$result")
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
end

# Exclude the ctrl-r/ctrl-t external handoff helpers (see warp_run_external_ctrl_r_widget/
# warp_run_external_ctrl_t_widget above) from the user's history.
#
# fish only supports a single fish_should_add_to_history function (unlike zsh's array of
# zshaddhistory hooks or bash's PROMPT_COMMAND-style stacking), so compose with any
# user-defined one -- e.g. from a plugin sourced in config.fish before this bootstrap script
# runs -- rather than clobbering it, following the same backup pattern warp_update_prompt_vars
# uses for fish_prompt.
#
# warp_original_fish_should_add_to_history must exist and be safe to call *before* we install our
# own wrapper below. This bootstrap script can run more than once in the same fish process (a
# shell reload, or a nested fish subshell), and a user or plugin can define or replace
# fish_should_add_to_history at any point, including between two of our sourcings -- so on every
# run, re-derive the backup from whatever fish_should_add_to_history currently is, unless that's
# already our own wrapper from a previous run (identified by the warp_run_external_ctrl_r_widget
# sentinel in its body), in which case the existing backup -- the last real hook we captured, or
# the accept-everything default if none ever existed -- is left alone. Backing up our own wrapper
# as if it were the original would make every history check call itself.
if functions -q fish_should_add_to_history
and not functions fish_should_add_to_history | string match --quiet -- '*warp_run_external_ctrl_r_widget*'
functions -q warp_original_fish_should_add_to_history; and functions -e warp_original_fish_should_add_to_history
functions -c fish_should_add_to_history warp_original_fish_should_add_to_history
else if not functions -q warp_original_fish_should_add_to_history
function warp_original_fish_should_add_to_history
return 0
end
end
function fish_should_add_to_history
string match --quiet -- '*warp_run_external_ctrl_r_widget*' $argv[1]; and return 1
string match --quiet -- '*warp_run_external_ctrl_t_widget*' $argv[1]; and return 1
warp_original_fish_should_add_to_history $argv
end

function warp_bootstrapped
set -l histfile_directory
set histfile_directory "$XDG_DATA_HOME"
Expand All @@ -564,6 +672,26 @@ function warp_bootstrapped
set vi_mode_enabled "1"
end

set -l shell_plugins
set -g _WARP_EXTERNAL_CTRL_R_WIDGET ""
set -l warp_ctrl_r_widget (warp_external_ctrl_r_widget)
switch "$warp_ctrl_r_widget"
case 'fzf-history-widget' '_atuin_search'
set -g _WARP_EXTERNAL_CTRL_R_WIDGET "$warp_ctrl_r_widget"
set -a shell_plugins external_ctrl_r_history
end

set -g _WARP_EXTERNAL_CTRL_T_WIDGET ""
set -l warp_ctrl_t_widget (warp_external_ctrl_t_widget)
switch "$warp_ctrl_t_widget"
case 'fzf-file-widget'
if functions -q fzf-file-widget
set -g _WARP_EXTERNAL_CTRL_T_WIDGET "$warp_ctrl_t_widget"
set -a shell_plugins external_ctrl_t_file
end
end
set -l escaped_shell_plugins (warp_escape_json $shell_plugins)

set -l kernel_name (uname)
if test -n "$kernel_name"
if [ "$kernel_name" = "Darwin" ]
Expand Down Expand Up @@ -593,7 +721,7 @@ function warp_bootstrapped
# part of its builtins (e.g. "for", "while", etc.).
set -l escaped_editor (warp_escape_json "$EDITOR")
set -l escaped_shell_path (warp_escape_json (status fish-path))
set -l escaped_json "{\"hook\": \"Bootstrapped\", \"value\": {\"histfile\": \"$escaped_histfile\", \"session_id\": $WARP_SESSION_ID, \"shell\": \"fish\", \"home_dir\": \"$HOME\", \"path\": \"$PATH\", \"editor\": \"$escaped_editor\", \"abbreviations\": \"$escaped_abbr\", \"aliases\": \"$escaped_aliases\", \"function_names\": \"$function_names\", \"env_var_names\": \"$env_var_names\", \"builtins\": \"$escaped_builtins\", \"keywords\": \"\", \"shell_version\": \"$FISH_VERSION\", \"vi_mode_enabled\": \"$vi_mode_enabled\", \"os_category\": \"$os_category\", \"linux_distribution\": \"$linux_distribution\", \"wsl_name\": \"$WSL_DISTRO_NAME\", \"shell_path\": \"$escaped_shell_path\"}}"
set -l escaped_json "{\"hook\": \"Bootstrapped\", \"value\": {\"histfile\": \"$escaped_histfile\", \"session_id\": $WARP_SESSION_ID, \"shell\": \"fish\", \"home_dir\": \"$HOME\", \"path\": \"$PATH\", \"editor\": \"$escaped_editor\", \"abbreviations\": \"$escaped_abbr\", \"aliases\": \"$escaped_aliases\", \"function_names\": \"$function_names\", \"env_var_names\": \"$env_var_names\", \"builtins\": \"$escaped_builtins\", \"keywords\": \"\", \"shell_version\": \"$FISH_VERSION\", \"shell_plugins\": \"$escaped_shell_plugins\", \"vi_mode_enabled\": \"$vi_mode_enabled\", \"os_category\": \"$os_category\", \"linux_distribution\": \"$linux_distribution\", \"wsl_name\": \"$WSL_DISTRO_NAME\", \"shell_path\": \"$escaped_shell_path\"}}"
warp_send_json_message $escaped_json
end

Expand Down
73 changes: 72 additions & 1 deletion app/assets/bundled/bootstrap/zsh_body.sh
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,48 @@ if [[ -z $WARP_BOOTSTRAPPED ]]; then
}
zle -N warp_report_input

# Runs the shell's own ctrl-r history widget as a foreground command.
function warp_run_external_ctrl_r_widget () {
local result=""
case "$_WARP_EXTERNAL_CTRL_R_WIDGET" in
fzf-history-widget)
result="$(fc -rl 1 \
| command -p awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print cmd }' \
| fzf --scheme=history --tiebreak=index +m)"
;;
atuin-search|atuin-search-viins|atuin-search-vicmd|_atuin_search_widget)
# atuin writes its TUI to stdout; under plain command substitution that's a pipe, and
# its cursor-position query (\x1b[6n) has nothing to answer it, so it bails during
# startup. Swap stdout/stderr through fd 3, matching atuin's own zsh integration
# (_atuin_search -> __atuin_search_cmd), so the TUI reaches the tty while the
# selection is still captured via command substitution.
result="$(ATUIN_SHELL=zsh atuin search -i 3>&1 1>&2 2>&3 3>&-)"
# If the user has atuin's enter_accept config on, Enter both selects and runs the
# command, signaled by this prefix; we only ever want the selection, never to run it,
# so strip the prefix in both cases (see atuin's _atuin_search for the same check).
result="${result#__atuin_accept__:}"
;;
esac
local warp_escaped_selection="$(warp_escape_json "$result")"
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
}

# Runs the shell's own ctrl-t file-search widget as a foreground command.
function warp_run_external_ctrl_t_widget () {
local result=""
case "$_WARP_EXTERNAL_CTRL_T_WIDGET" in
fzf-file-widget)
if (( $+functions[__fzf_select] )); then
result="$(__fzf_select)"
else # fzf < 0.48
result="$(__fsel)"
fi
;;
esac
local warp_escaped_selection="$(warp_escape_json "$result")"
warp_send_json_message "{ \"hook\": \"ExternalShellWidgetSelection\", \"value\": { \"buffer\": \"$warp_escaped_selection\", \"session_id\": $WARP_SESSION_ID } }"
}

function clear() {
warp_send_json_message "{\"hook\": \"Clear\", \"value\": {\"session_id\": $WARP_SESSION_ID}}"
}
Expand Down Expand Up @@ -1279,7 +1321,8 @@ esac
# See https://zsh.sourceforge.io/Doc/Release/Functions.html for more context
# on the zshaddhistory hook.
_warp_zshaddhistory() {
_is_warp_generator_command "$1"
_is_warp_generator_command "$1" && [[ "$1" != *"warp_run_external_ctrl_r_widget"* ]] && \
[[ "$1" != *"warp_run_external_ctrl_t_widget"* ]]
}

# Register this zshaddhistory hook after the user's RC files have been sourced,
Expand Down Expand Up @@ -1353,6 +1396,34 @@ esac
shell_plugins+=(vi)
fi

# Detect whether ctrl-r has been rebound to fzf's or atuin's history widget.
_WARP_EXTERNAL_CTRL_R_WIDGET=""
warp_ctrl_r_binding="$(bindkey -M main '^R' 2>/dev/null)"
if [[ "$warp_ctrl_r_binding" == '"^R" '* ]]; then
warp_ctrl_r_widget="${warp_ctrl_r_binding#\"^R\" }"
case "$warp_ctrl_r_widget" in
fzf-history-widget|atuin-search|atuin-search-viins|atuin-search-vicmd|_atuin_search_widget)
_WARP_EXTERNAL_CTRL_R_WIDGET="$warp_ctrl_r_widget"
shell_plugins+=(external_ctrl_r_history)
;;
esac
fi

# Detect whether ctrl-t has been rebound to fzf's file-search widget.
_WARP_EXTERNAL_CTRL_T_WIDGET=""
warp_ctrl_t_binding="$(bindkey -M main '^T' 2>/dev/null)"
if [[ "$warp_ctrl_t_binding" == '"^T" '* ]]; then
warp_ctrl_t_widget="${warp_ctrl_t_binding#\"^T\" }"
case "$warp_ctrl_t_widget" in
fzf-file-widget)
if (( $+functions[__fzf_select] )) || (( $+functions[__fsel] )); then
_WARP_EXTERNAL_CTRL_T_WIDGET="$warp_ctrl_t_widget"
shell_plugins+=(external_ctrl_t_file)
fi
;;
esac
fi

if kernel_name="$(uname)"; then
if [[ "$kernel_name" == "Darwin" ]]; then
os_category="MacOS"
Expand Down
2 changes: 2 additions & 0 deletions app/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ fn enabled_features() -> HashSet<FeatureFlag> {
FeatureFlag::TerminalLifecycleRecovery,
#[cfg(feature = "ctrl_c_cancels_third_party_harness")]
FeatureFlag::CtrlCCancelsThirdPartyHarness,
#[cfg(feature = "shell_widget_handoff")]
FeatureFlag::ShellWidgetHandoff,
]);

flags
Expand Down
10 changes: 9 additions & 1 deletion app/src/terminal/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use warp_terminal::event::{ExecutedExecutorCommandEvent, ParseGeneratorOutpu
use warp_util::lazy::Lazy;

use super::history::HistoryEntry;
use super::model::ansi::FinishUpdateValue;
use super::model::ansi::{ExternalShellWidgetSelectionValue, FinishUpdateValue};
use super::model::block::BlockId;
use super::model::lifecycle::LifecycleRecoveryRecord;
use super::model::session::{SessionId, SessionInfo};
Expand Down Expand Up @@ -128,6 +128,7 @@ pub enum Event {
/// Emitted when the assisted auto-update has completed and we're ready to
/// relaunch the app.
FinishUpdate(FinishUpdateValue),
ExternalShellWidgetSelection(ExternalShellWidgetSelectionValue),
TextSelectionChanged,
ShellSpawned(ShellType),
ImageReceived {
Expand Down Expand Up @@ -475,6 +476,13 @@ impl Debug for Event {
)
}
Event::FinishUpdate(data) => write!(f, "FinishUpdate({})", data.update_id),
Event::ExternalShellWidgetSelection(data) => {
write!(
f,
"ExternalShellWidgetSelection(buffer_len: {})",
data.buffer.len()
)
}
Event::TextSelectionChanged => write!(f, "TextSelectionChanged"),
Event::ShellSpawned(shell_type) => write!(f, "ShellSpawned({shell_type:?})"),
Event::ImageReceived { image_id, .. } => {
Expand Down
Loading
Loading