Skip to content

Add keybindings for CTRL, ALT, SHIFT + UP, DOWN, RIGHT, LEFT, HOME, END, BACKSPACE, DELETE & more#3996

Merged
junegunn merged 19 commits intojunegunn:masterfrom
masmu:feature/additional-keybindings
Sep 5, 2025
Merged

Add keybindings for CTRL, ALT, SHIFT + UP, DOWN, RIGHT, LEFT, HOME, END, BACKSPACE, DELETE & more#3996
junegunn merged 19 commits intojunegunn:masterfrom
masmu:feature/additional-keybindings

Conversation

@masmu
Copy link
Contributor

@masmu masmu commented Sep 13, 2024

This PR implements some missing key bindings.

In particular all possible combinations of

  • CTRL
  • ALT
  • SHIFT
  • ALT+SHIFT
  • CTRL+ALT
  • CTRL+SHIFT
  • CTRL+ALT+SHIFT

in conjunction with Up, Down, Left, Right, Home, End, Backspace, Delete, PageUp, PageDown.

I added tests to the LightRenderer beforehand to make sure I wasn't breaking anything, and expanded them when I added new key bindings.

So you can actually do stuff like:

fzf
  --bind 'ctrl-up:up+up+up+up+up'
  --bind 'ctrl-down:down+down+down+down+down'
  --bind 'ctrl-right:forward-word'
  --bind 'ctrl-left:backward-word'
  --bind 'alt-delete:delete-char'
  --bind 'alt-backspace:backward-kill-word'

This PR fixes #1747.

@junegunn
Copy link
Owner

Thanks, I'll review the code when I get some time.

A few things.

  • We also have to update tcell renderer for Windows
  • man page needs to be updated accordingly
  • Terminal emulators I use (kitty, iterm2, etc) don't distinguish between the CTRL-* chords you added from the ones without CTRL. Which terminal emulator do you use on what platform? If these chords are not widely supported by the terminal emulators, we should warn about that on the manual.
  • Allowing all possible names (ctrl-alt-shift, alt-shift-ctrl, shift-ctrl-alt, etc) will make the code harder to maintain. I'd just allow one sequence. i.e. ctrl-alt-shift.

@masmu
Copy link
Contributor Author

masmu commented Sep 17, 2024

No worries, take your time.

We also have to update tcell renderer for Windows

Yep, I checked tcell.go and most of it should be pretty straight forward to implement. My biggest issue is that I do not have any Windows machine to test on. So I can do an implementation, make sure it compiles but that's probably it. Would you mind to assist here and test it? Or is there a way to do this on Linux?

man page needs to be updated accordingly

True. Do you write the man page by hand? I was hoping it might be generated by some docstring, although I didn't find one when I searched for it.

Terminal emulators I use (kitty, iterm2, etc) don't distinguish between the CTRL-* chords you added from the ones without CTRL. Which terminal emulator do you use on what platform? If these chords are not widely supported by the terminal emulators, we should warn about that on the manual.

SGTM. I use gnome-terminal and ddterm both running most of the time zsh + tmux, sometimes just zsh or bash. Platform is Linux.

Allowing all possible names (ctrl-alt-shift, alt-shift-ctrl, shift-ctrl-alt, etc) will make the code harder to maintain. I'd just allow one sequence. i.e. ctrl-alt-shift.

I tried to stick as close to your implementation as possible and saw this case "alt-shift-right", "shift-alt-right": case where you actually support both combinations. But I don't mind removing the redundant ones. Actually I like that choice 😄

@masmu masmu force-pushed the feature/additional-keybindings branch from d7d08bc to aad5e49 Compare September 18, 2024 09:54
@masmu
Copy link
Contributor Author

masmu commented Sep 18, 2024

I made the mentioned changes and the remaining exciting question is if the following block (tcell.go:~390) is correct:

case tcell.KeyBackspace2:
	if ctrlAlt {
		return Event{CtrlAltBackspace, 0, nil}
	}
	if ctrl {
		return Event{CtrlBackspace, 0, nil}
	}
	if alt {
		return Event{AltBackspace, 0, nil}
	}
	return Event{Backspace, 0, nil}

My educated guess is that it is actually not and has to be covered in the following block:

case tcell.KeyCtrlH:
	switch ev.Rune() {
	case 0:
		if ctrl {
			return Event{Backspace, 0, nil}
		}
	case rune(tcell.KeyCtrlH):
		switch {
		case ctrl:
			return keyfn('h')
		case alt:
			return Event{AltBackspace, 0, nil}
		case none, shift:
			return Event{Backspace, 0, nil}
		}
	}

@junegunn
Copy link
Owner

Or is there a way to do this on Linux?

You can run go run -tags tcell main.go.

@masmu
Copy link
Contributor Author

masmu commented Sep 20, 2024

Thanks for the hint!

But I am trying to get my hands on a windows machine as well because it all depends on what the OS is really gonna send. I will report back after that.

@masmu masmu force-pushed the feature/additional-keybindings branch from aad5e49 to d90cff2 Compare September 30, 2024 11:28
@nirodhvana
Copy link

is this ready for merging?

@masmu
Copy link
Contributor Author

masmu commented Oct 2, 2024

Nope, not yet.

The Linux implementation is done. Windows implementation potentially as well, but requires a patch in tcell to make the following shortcuts work:

  • Ctrl-Alt-*
  • Ctrl-Alt-Shift-*

That's why this PR currently points to my own fork of tcell and that keeps this from being reviewed and merged. I created a PR upstream and hope for a quick review.

You can build this PR yourself. That should work.

@normanr
Copy link

normanr commented Apr 20, 2025

Does this need to wait for full tcell support of all modifiers in windows, or could it just be annotated (like kitty, iterm2 are) that it's not supported yet? (context: I was hoping to be able to use shift-pageup/down for the preview panel).

@eMPee584
Copy link

eMPee584 commented Jul 5, 2025

Would you be fine splitting off the part depending on tcell changes into another PR @masmu ?
Seems there are other terminals beyond rxvt sending ^[[1~ for HOME – such as the linux VT & my debian machine's tmux 😅
[EDIT]: strangely, \e[1~ seems to be bound in the source for several years already, so I'm somewhat confused why it does not work here 🤔 [EDIT2]: ah, it does work, just that it's bound to go to line start/end, not list start/end.. 🤦‍♂️

@masmu
Copy link
Contributor Author

masmu commented Jul 7, 2025

I would not mind not adding windows support at all. But that probably would demolish the chances of getting this merged, right @junegunn?

I am gonna try to get things rolling again in tcell/pull/749.

@junegunn
Copy link
Owner

junegunn commented Jul 7, 2025

It's okay as long as we document the limitation. I assume you're a Linux user, so you might not be able to answer this, but I'm wondering how I can test this on macOS.

@masmu
Copy link
Contributor Author

masmu commented Jul 8, 2025

I assume you're a Linux user

Yes.

so you might not be able to answer this, but I'm wondering how I can test this on macOS.

Do you mean how to try out the keybindings? Build the PR, add some bindings a la

./target/fzf-linux_amd64
  --bind 'ctrl-up:up+up+up+up+up'
  --bind 'ctrl-down:down+down+down+down+down'
  --bind 'ctrl-right:forward-word'
  --bind 'ctrl-left:backward-word'
  --bind 'alt-delete:delete-char'
  --bind 'alt-backspace:backward-kill-word'

and see if they work.

Note that macOS might intercept certain keystrokes (like Windows does for Ctrl + Alt + Delete).
Besides that: Your ⌥ or ⌘ key (that depends on your terminal) is being treated as Alt.

@masmu
Copy link
Contributor Author

masmu commented Aug 24, 2025

Good news. tcell 2.9.0 fixed the windows issue with the broken shortcuts for Ctrl-Alt-* and Ctrl-Alt-Shift-*.

Since we are now also having full windows support, I added the remaining tests and updated tcell to 2.9.0.

This can be tested via the following "one-liner":

./target/fzf-linux_amd64 \
    --bind 'up:change-prompt(up)' \
    --bind 'down:change-prompt(down)' \
    --bind 'right:change-prompt(right)' \
    --bind 'left:change-prompt(left)' \
    --bind 'home:change-prompt(home)' \
    --bind 'end:change-prompt(end)' \
    --bind 'delete:change-prompt(delete)' \
    --bind 'page-up:change-prompt(pageup)' \
    --bind 'page-down:change-prompt(pagedown)' \
    \
    --bind 'ctrl-up:change-prompt(ctrl-up)' \
    --bind 'ctrl-down:change-prompt(ctrl-down)' \
    --bind 'ctrl-right:change-prompt(ctrl-right)' \
    --bind 'ctrl-left:change-prompt(ctrl-left)' \
    --bind 'ctrl-home:change-prompt(ctrl-home)' \
    --bind 'ctrl-end:change-prompt(ctrl-end)' \
    --bind 'ctrl-delete:change-prompt(ctrl-delete)' \
    --bind 'ctrl-page-up:change-prompt(ctrl-pageup)' \
    --bind 'ctrl-page-down:change-prompt(ctrl-pagedown)' \
    \
    --bind 'shift-up:change-prompt(shift-up)' \
    --bind 'shift-down:change-prompt(shift-down)' \
    --bind 'shift-right:change-prompt(shift-right)' \
    --bind 'shift-left:change-prompt(shift-left)' \
    --bind 'shift-home:change-prompt(shift-home)' \
    --bind 'shift-end:change-prompt(shift-end)' \
    --bind 'shift-delete:change-prompt(shift-delete)' \
    --bind 'shift-page-up:change-prompt(shift-pageup)' \
    --bind 'shift-page-down:change-prompt(shift-pagedown)' \
    \
    --bind 'alt-up:change-prompt(alt-up)' \
    --bind 'alt-down:change-prompt(alt-down)' \
    --bind 'alt-right:change-prompt(alt-right)' \
    --bind 'alt-left:change-prompt(alt-left)' \
    --bind 'alt-home:change-prompt(alt-home)' \
    --bind 'alt-end:change-prompt(alt-end)' \
    --bind 'alt-delete:change-prompt(alt-delete)' \
    --bind 'alt-page-up:change-prompt(alt-pageup)' \
    --bind 'alt-page-down:change-prompt(alt-pagedown)' \
    \
    --bind 'ctrl-shift-up:change-prompt(ctrl-shift-up)' \
    --bind 'ctrl-shift-down:change-prompt(ctrl-shift-down)' \
    --bind 'ctrl-shift-right:change-prompt(ctrl-shift-right)' \
    --bind 'ctrl-shift-left:change-prompt(ctrl-shift-left)' \
    --bind 'ctrl-shift-home:change-prompt(ctrl-shift-home)' \
    --bind 'ctrl-shift-end:change-prompt(ctrl-shift-end)' \
    --bind 'ctrl-shift-delete:change-prompt(ctrl-shift-delete)' \
    --bind 'ctrl-shift-page-up:change-prompt(ctrl-shift-pageup)' \
    --bind 'ctrl-shift-page-down:change-prompt(ctrl-shift-pagedown)' \
    \
    --bind 'ctrl-alt-up:change-prompt(ctrl-alt-up)' \
    --bind 'ctrl-alt-down:change-prompt(ctrl-alt-down)' \
    --bind 'ctrl-alt-right:change-prompt(ctrl-alt-right)' \
    --bind 'ctrl-alt-left:change-prompt(ctrl-alt-left)' \
    --bind 'ctrl-alt-home:change-prompt(ctrl-alt-home)' \
    --bind 'ctrl-alt-end:change-prompt(ctrl-alt-end)' \
    --bind 'ctrl-alt-delete:change-prompt(ctrl-alt-delete)' \
    --bind 'ctrl-alt-page-up:change-prompt(ctrl-alt-pageup)' \
    --bind 'ctrl-alt-page-down:change-prompt(ctrl-alt-pagedown)' \
    \
    --bind 'shift-alt-up:change-prompt(shift-alt-up)' \
    --bind 'shift-alt-down:change-prompt(shift-alt-down)' \
    --bind 'shift-alt-right:change-prompt(shift-alt-right)' \
    --bind 'shift-alt-left:change-prompt(shift-alt-left)' \
    --bind 'shift-alt-home:change-prompt(shift-alt-home)' \
    --bind 'shift-alt-end:change-prompt(shift-alt-end)' \
    --bind 'shift-alt-delete:change-prompt(shift-alt-delete)' \
    --bind 'shift-alt-page-up:change-prompt(shift-alt-pageup)' \
    --bind 'shift-alt-page-down:change-prompt(shift-alt-pagedown)' \
    \
    --bind 'ctrl-alt-shift-up:change-prompt(ctrl-alt-shift-up)' \
    --bind 'ctrl-alt-shift-down:change-prompt(ctrl-alt-shift-down)' \
    --bind 'ctrl-alt-shift-right:change-prompt(ctrl-alt-shift-right)' \
    --bind 'ctrl-alt-shift-left:change-prompt(ctrl-alt-shift-left)' \
    --bind 'ctrl-alt-shift-home:change-prompt(ctrl-alt-shift-home)' \
    --bind 'ctrl-alt-shift-end:change-prompt(ctrl-alt-shift-end)' \
    --bind 'ctrl-alt-shift-delete:change-prompt(ctrl-alt-shift-delete)' \
    --bind 'ctrl-alt-shift-page-up:change-prompt(ctrl-alt-shift-pageup)' \
    --bind 'ctrl-alt-shift-page-down:change-prompt(ctrl-alt-shift-pagedown)' \
    \
    --bind 'backspace:change-prompt(backspace)' \
    --bind 'ctrl-backspace:change-prompt(ctrl-backspace)' \
    --bind 'alt-backspace:change-prompt(alt-backspace)' \
    --bind 'ctrl-alt-backspace:change-prompt(ctrl-alt-backspace)' \

@junegunn
This is now ready for review.

@junegunn junegunn requested a review from Copilot August 25, 2025 03:02

This comment was marked as outdated.

@junegunn
Copy link
Owner

Passed the tests! I'll review the code when I have some time. Thanks!

@junegunn
Copy link
Owner

junegunn commented Aug 28, 2025

Thanks, I tested your patch using clear_all_shortcuts yes option in Kitty, and everything seems to work well, great job.

One thing I noticed after enabling clear_all_shortcuts in Kitty is that now Kitty recognizes CMD key as super, and sends an ANSI sequence that fzf doesn't recognize.

For example, pressing CMD-a leaves 97;9u on the prompt, CMD-b, 98;9u, etc.

image

Do we want to support this super key as well? (This was actually my first time hearing about it.) Maybe another time?

@masmu
Copy link
Contributor Author

masmu commented Aug 29, 2025

Thanks, I tested your patch using clear_all_shortcuts yes option in Kitty, and everything seems to work well, great job.

Thank you.

For example, pressing CMD-a leaves 97;9u on the prompt, CMD-b, 98;9u, etc.

Sounds like kittys own enhanced keyboard protocol.

Do we want to support this super key as well? (This was actually my first time hearing about it.) Maybe another time?

No, I'm afraid not. That would go far beyond the scope of this PR.

xterm is the de facto standard in terminal emulation. We now have everything what is possible with pure xterm to ensure that it works everywhere. So there should be no exceptions. As long as something implements xterm, it just works. And they all implement xterm first, other stuff maybe if at all.
Everything else is an extension, and even if it is available in large projects such as vim or emacs and supported by some terminals, you cannot rely on it. Sticking with pure xterm also has disadvantages.
Personally, I miss the Shift-*-Backspace key combinations.

Furthermore, the Super key in Linux (and the Windows key in Windows) is assigned to many GUI actions by default. This makes it a poor modifier, as the operating system would often intercept the key combination anyway before it could reach the terminal window. Not sure how the situation is like on Macs in this regard.

@junegunn
Copy link
Owner

No, I'm afraid not. That would go far beyond the scope of this PR.

Agreed. Thanks for the detailed explanation.

Furthermore, the Super key in Linux (and the Windows key in Windows) is assigned to many GUI actions by default. This makes it a poor modifier.

On a related note, when I enable clear_all_shortcuts on Kitty, I'm unable to paste the clipboard content using CMD-V, which is annoying. So I don't know if I'm going to use the option even though it means I can't utilize these new keys in fzf.

@masmu
Copy link
Contributor Author

masmu commented Aug 29, 2025

On a related note, when I enable clear_all_shortcuts on Kitty, I'm unable to paste the clipboard content using CMD-V, which is annoying. So I don't know if I'm going to use the option even though it means I can't utilize these new keys in fzf.

Have you tried it without clear_all_shortcuts? My expectation is that most of them work and some don't because Kitty has bound internal actions to them.

Kitty has a set of default keyboard shortcuts to perform certain actions. For example, Ctrl+Shift+Page Down to scroll down the page. When you press this shortcut, it is not passed on to your shell, but Kitty intercepts it and executes its internal scroll function to visually scroll up the terminal window.
In general, it's good to have a sane set of default keyboard shortcuts, and Ctrl+Shift+Page Down is certainly one of them. However, this would have made testing this new feature difficult, as some would have been shadowed by Kitty's default keyboard shortcuts. Therefore, I suggested clearing all default keyboard shortcuts so that none of them would interfere with the test.
For actual use, you should certainly not use clear_all_shortcuts, but instead unbind the key combos in Kitty that you want to use in fzf.

@junegunn junegunn requested a review from Copilot August 31, 2025 12:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements comprehensive key binding support for all possible combinations of modifier keys (CTRL, ALT, SHIFT) with directional keys (Up/Down/Left/Right), navigation keys (Home/End), editing keys (Backspace/Delete), and page navigation keys (PageUp/PageDown).

  • Adds over 50 new key event constants for modifier key combinations
  • Implements detection and handling logic for these key combinations in both tcell and light renderers
  • Expands test coverage with comprehensive test cases for the new key bindings

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tui/tui.go Adds new EventType constants for all modifier key combinations
src/tui/tcell.go Implements key detection logic for tcell renderer
src/tui/light.go Implements escape sequence parsing for light renderer
src/tui/tcell_test.go Adds comprehensive test cases for tcell key detection
src/tui/light_test.go Adds new test file with extensive light renderer key binding tests
src/tui/eventtype_string.go Updates auto-generated string representation for new event types
src/options.go Adds string-to-event mapping for new key combinations
src/terminal.go Maps ctrl-backspace to backward delete char action
man/man1/fzf.1 Documents all new key binding options
go.mod Updates tcell dependency version

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@junegunn
Copy link
Owner

Oops, tcell 2.9 requires Go 1.23, but fzf is still using 1.20 to support Windows 7.

$ go run -tags tcell main.go
src/tui/tcell.go:10:2: missing go.sum entry for module providing package github.com/gdamore/tcell/v2 (imported by github.com/junegunn/fzf/src/tui); to add:
        go get github.com/junegunn/fzf/src/tui

$ go get github.com/junegunn/fzf/src/tui
go: upgraded golang.org/x/sys v0.30.0 => v0.35.0
go: upgraded golang.org/x/term v0.29.0 => v0.34.0
go: upgraded golang.org/x/text v0.21.0 => v0.28.0

$ go run -tags tcell main.go
golang.org/x/text/encoding/internal/identifier: cannot compile Go 1.23 code
golang.org/x/text/transform: cannot compile Go 1.23 code
github.com/gdamore/tcell/v2/terminfo: cannot compile Go 1.23 code
golang.org/x/sys/unix: cannot compile Go 1.23 code

We'll eventully drop support for Windows 7 and move on.

Microsoft ended security patches for Windows 7 after January 2020

It's been 5 years, so this might be the good time for that. But let me first release a patch version for the pending bug fixes with Go 1.20 for the Windows 7 users, then we'll move on.

@junegunn junegunn added this to the 0.66.0 milestone Aug 31, 2025
@junegunn
Copy link
Owner

https://github.com/junegunn/fzf/releases/tag/v0.65.2

@masmu
Copy link
Contributor Author

masmu commented Sep 1, 2025

We'll eventully drop support for Windows 7 and move on.

I would just like to briefly note that, theoretically, you can stay on tcell 2.8.x, which means you “only” lose the key combinations Ctrl-Alt-* and Ctrl-Alt-Shift-* in Windows. Everything else works as it does now. Linux and MacOSX would function completely.
But I also understand the reasons for discontinuing Windows 7 support. Maintaining different sets of functions for different platforms is very tedious, especially when there are no really good reasons for doing so.

junegunn added this to the 0.66.0 milestone yesterday

Cool. I am looking forward to 0.66.0 😃

Is there anything left for me to do?

@junegunn
Copy link
Owner

junegunn commented Sep 3, 2025

Everything's fine. I just want to wait a bit to see if the community has any feedback on that release.

@junegunn junegunn merged commit 9ed971c into junegunn:master Sep 5, 2025
5 checks passed
@junegunn
Copy link
Owner

junegunn commented Sep 5, 2025

Merged, thanks for the great work!

@masmu
Copy link
Contributor Author

masmu commented Sep 5, 2025

Thank you very much.

And thank you for fzf and your ongoing commitment.

tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Oct 15, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [junegunn/fzf](https://github.com/junegunn/fzf) | minor | `v0.65.2` -> `v0.66.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>junegunn/fzf (junegunn/fzf)</summary>

### [`v0.66.0`](https://github.com/junegunn/fzf/releases/tag/v0.66.0): 0.66.0

[Compare Source](junegunn/fzf@v0.65.2...v0.66.0)

##### Quick summary

This version introduces many new features centered around the new "raw" mode.

| Type        | Class      | Name                | Description                                        |
| :---------- | :--------- | :------------------ | :------------------------------------------------- |
| New         | Option     | `--raw`             | Enable raw mode by default                         |
| New         | Option     | `--gutter CHAR`     | Set the gutter column character                    |
| New         | Option     | `--gutter-raw CHAR` | Set the gutter column character in raw mode        |
| Enhancement | Option     | `--listen SOCKET`   | Added support for Unix domain sockets              |
| New         | Action     | `toggle-raw`        | Toggle raw mode                                    |
| New         | Action     | `enable-raw`        | Enable raw mode                                    |
| New         | Action     | `disable-raw`       | Disable raw mode                                   |
| New         | Action     | `up-match`          | Move up to the matching item                       |
| New         | Action     | `down-match`        | Move down to the matching item                     |
| New         | Action     | `best`              | Move to the  matching item with the best score     |
| New         | Color Name | `nomatch`           | Color for non-matching items in raw mode           |
| New         | Color Attr | `strip`             | Remove original colors                             |
| New         | Env Var    | `FZF_RAW`           | Matching status in raw mode (0, 1, or undefined)   |
| New         | Env Var    | `FZF_DIRECTION`     | `up` or `down` depending on the layout             |
| New         | Env Var    | `FZF_SOCK`          | Path to the Unix domain socket fzf is listening on |
| Enhancement | Key        | `CTRL-N`            | `down` -> `down-match`                             |
| Enhancement | Key        | `CTRL-P`            | `up` -> `up-match`                                 |
| Enhancement | Shell      | `CTRL-R` binding    | Toggle raw mode with `ALT-R`                       |
| Enhancement | Shell      | `CTRL-R` binding    | Opt-out with an empty `FZF_CTRL_R_COMMAND`         |

##### 1. Introducing "raw" mode

![](https://github.com/user-attachments/assets/9640ae11-b5f7-43fb-95f1-c29307fc17c2)

This version introduces a new "raw" mode (named so because it shows the list "unfiltered"). In raw mode, non-matching items stay in their original positions, but appear dimmed. This allows you see surrounding items of a match and better understand the context of it. You can enable raw mode by default with `--raw`, but it's often more useful when toggled dynamically with the `toggle-raw` action.

```sh
tree | fzf --reverse --bind alt-r:toggle-raw
```

While non-matching items are displayed in a dimmed color, they are treated just like matching items, so you can place the cursor on them and perform any action. If you prefer to navigate only through matching items, use the `down-match` and `up-match` actions, which are from now on bound to `CTRL-N` and `CTRL-P` respectively, and also to `ALT-DOWN` and `ALT-UP`.

| Key        | Action       | With `--history` |
| :--------- | :----------- | :--------------- |
| `down`     | `down`       |                  |
| `up`       | `up`         |                  |
| `ctrl-j`   | `down`       |                  |
| `ctrl-k`   | `up`         |                  |
| `ctrl-n`   | `down-match` | `next-history`   |
| `ctrl-p`   | `up-match`   | `prev-history`   |
| `alt-down` | `down-match` |                  |
| `alt-up`   | `up-match`   |                  |

> \[!NOTE]
> `CTRL-N` and `CTRL-P` are bound to `next-history` and `prev-history` when `--history` option is enabled, so in that case, you'll need to manually bind them, or use `ALT-DOWN` and `ALT-UP` instead.

> \[!TIP]
> `up-match` and `down-match` are equivalent to `up` and `down` when not in raw mode, so you can safely bind them to `up` and `arrow` keys if you prefer.
>
> ```sh
> fzf --bind up:up-match,down:down-match
> ```

##### Customizing the behavior

In raw mode, the input list is presented in its original order, unfiltered, and your cursor will not move to the matching item automatically. Here are ways to customize the behavior.

```sh

### When the result list is updated, move the cursor to the item with the best score
### (assuming sorting is not disabled)
fzf --raw --bind result:best

### Move to the first matching item in the original list
### - $FZF_RAW is set to 0 when raw mode is enabled and the current item is a non-match

### - $FZF_DIRECTION is set to either 'up' or 'down' depending on the layout direction
fzf --raw --bind 'result:first+transform:[[ $FZF_RAW = 0 ]] && echo $FZF_DIRECTION-match'
```

##### Customizing the look

##### Gutter

To make the mode visually distinct, the gutter column is rendered in a dashed line using `▖` character. But you can customize it with the `--gutter-raw CHAR` option.

```sh

### Use a thinner gutter instead of the default dashed line
fzf --bind alt-r:toggle-raw --gutter-raw ▎
```

##### Color and style of non-matching items

Non-matching items are displayed in a dimmed color by default, but you can change it with the `--color nomatch:...` option.

```sh
fzf --raw --color nomatch:red
fzf --raw --color nomatch:red:dim
fzf --raw --color nomatch:red:dim:strikethrough
fzf --raw --color nomatch:red:dim:strikethrough:italic
```

For colored input, dimming alone may not be enough, and you may prefer to remove colors entirely. For that case, a new special style attribute `strip` has been added.

```sh
fd --color always | fzf --ansi --raw --color nomatch:dim:strip:strikethrough
```

##### Conditional actions for raw mode

You may want to perform different actions depending on whether the current item is a match or not. For that, fzf now exports `$FZF_RAW` environment variable.

It's:

- Undefined if raw mode is disabled
- `1` if the current item is a match
- `0` otherwise

```sh

### Do not allow selecting non-matching items
fzf --raw --bind 'enter:transform:[[ ${FZF_RAW-1} = 1 ]] && echo accept || echo bell'
```

##### Leveraging raw mode in shell integration

The `CTRL-R` binding (command history) now lets you toggle raw mode with `ALT-R`.

##### 2. Style changes

The screenshot on the right shows the updated gutter style:

![](https://github.com/user-attachments/assets/8ea7b5ef-c99e-4686-905b-22eb078b700a)

This version includes a few minor updates to fzf's classic visual style:

- The gutter column is now narrower, rendered with the left-half block character (`▌`).
- Markers no longer use background colors.
- The `--color base16` theme (alias: `16`) has been updated for better compatibility with both dark and light themes.

##### 3. `--listen` now supports Unix domain sockets

If an argument to `--listen` ends with `.sock`, fzf will listen on a Unix domain socket at the specified path.

```sh
fzf --listen /tmp/fzf.sock --no-tmux

### GET
curl --unix-socket /tmp/fzf.sock http

### POST
curl --unix-socket /tmp/fzf.sock http -d up
```

Note that any existing file at the given path will be removed before creating the socket, so avoid using an important file path.

##### 4. Added options

##### `--gutter CHAR`

The gutter column can now be customized using `--gutter CHAR` and styled with `--color gutter:...`. Examples:

```sh

### Right-aligned gutter
fzf --gutter '▐'

### Even thinner gutter
fzf --gutter '▎'

### Yellow checker pattern
fzf --gutter '▚' --color gutter:yellow

### Classic style
fzf --gutter ' ' --color gutter:reverse
```

##### `--gutter-raw CHAR`

As noted above, the `--gutter-raw CHAR` option was also added for customizing the gutter column in raw mode.

##### 5. Added actions

The following actions were introduced to support working with raw mode:

| Action        | Description                                                                                 |
| :------------ | :------------------------------------------------------------------------------------------ |
| `toggle-raw`  | Toggle raw mode                                                                             |
| `enable-raw`  | Enable raw mode                                                                             |
| `disable-raw` | Disable raw mode                                                                            |
| `up-match`    | Move up to the matching item; identical to `up` if raw mode is disabled                     |
| `down-match`  | Move down to the matching item; identical to `down` if raw mode is disabled                 |
| `best`        | Move to the matching item with the best score; identical to `first` if raw mode is disabled |

##### 6. Added environment variables

##### `$FZF_DIRECTION`

`$FZF_DIRECTION` is now exported to child processes, indicating the list direction of the current layout:

- `up` for the default layout
- `down` for `reverse` or `reverse-list`

This simplifies writing transform actions involving layout-dependent actions like `{up,down}-match`, `{up,down}-selected`, and `toggle+{up,down}`.

```sh
fzf --raw --bind 'result:first+transform:[[ $FZF_RAW = 0 ]] && echo $FZF_DIRECTION-match'
```

##### `$FZF_SOCK`

When fzf is listening on a Unix domain socket using `--listen`, the path to the socket is exported as `$FZF_SOCK`, analogous to `$FZF_PORT` for TCP sockets.

##### `$FZF_RAW`

As described above, `$FZF_RAW` is now exported to child processes in raw mode, indicating whether the current item is a match (`1`) or not (`0`). It is not defined when not in raw mode.

##### `$FZF_CTRL_R_COMMAND`

You can opt-out `CTRL-R` binding from the shell integration by setting `FZF_CTRL_R_COMMAND` to an empty string. Setting it to any other value is not supported and will result in a warning.

```sh

### Disable the CTRL-R binding from the shell integration
FZF_CTRL_R_COMMAND= eval "$(fzf --bash)"
```

##### 7. Added key support for `--bind`

Pull request [#&#8203;3996](junegunn/fzf#3996) added support for many additional keys for `--bind` option, such as `ctrl-backspace`.

##### 8. Breaking changes

##### Hiding the gutter column

In the previous versions, the recommended way to hide the gutter column was to set `--color gutter:-1`. That's because the gutter column was just a space character, reversed. But now that it's using a visible character (`▌`), applying the default color is no longer enough to hide it. Instead, you can set it to a space character.

```sh

### Hide the gutter column
fzf --gutter ' '

### Classic style
fzf --gutter ' ' --color gutter:reverse
```

##### `--color` option

In the previous versions, some elements had default style attributes applied and you would have to explicitly unset them with `regular` attribute if you wanted to reset them. This is no longer needed now, as the default style attributes are applied only when you do not specify any color or style for that element.

```sh

### No 'dim', just red and italic.
fzf --ghost 'Type to search' --color ghost:red:italic
```

##### Compatibility changes

Starting with this release, fzf is built with Go 1.23. Support for some old OS versions has been dropped.

See <https://go.dev/wiki/MinimumRequirements>.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDQuNCIsInVwZGF0ZWRJblZlciI6IjQxLjE0Ni4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add key binding options for {ctrl,alt}-{backspace,del,left,right}

5 participants