m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-11 12:53:48 -05:00

Compare commits

...

52 Commits

Author SHA1 Message Date
Junegunn Choi
afc955771c Update CHANGELOG 2025-09-17 19:37:33 +09:00
Junegunn Choi
c134c6f898 Update man page 2025-09-17 19:35:43 +09:00
Junegunn Choi
95697b96f8 Doc 2025-09-16 21:40:24 +09:00
Junegunn Choi
3809ebb0c8 Use grey background color 2025-09-16 21:37:00 +09:00
Junegunn Choi
ca9b4d5a49 Use white foreground color 2025-09-16 21:23:24 +09:00
Junegunn Choi
04888f5b94 Make info yellow, no italics 2025-09-16 21:23:24 +09:00
Junegunn Choi
3072b46218 Adjust default themes
Motivation:

`--color 16` can be a better default than `dark` or `light`, since it uses
the colors defined by the current theme. This usually blends in more
naturally and works well in both light and dark modes.

However, some elements were previously hard-coded with white or black
foreground colors, which can cause rendering issues in certain terminal
themes.

* 16
  * Avoid using black or white foreground colors, so it works better with
    both dark and light themes
  * Display 'info' in italic to better separate it from the other parts
* dark / light
  * Display 'info' in italic for consistency
2025-09-16 21:23:24 +09:00
Junegunn Choi
a67aa85820 Style change: thinner gutter column (#4521) 2025-09-16 21:22:56 +09:00
dependabot[bot]
c5cabe1691 Bump github.com/charlievieth/fastwalk from 1.0.13 to 1.0.14 (#4522)
Bumps [github.com/charlievieth/fastwalk](https://github.com/charlievieth/fastwalk) from 1.0.13 to 1.0.14.
- [Release notes](https://github.com/charlievieth/fastwalk/releases)
- [Commits](https://github.com/charlievieth/fastwalk/compare/v1.0.13...v1.0.14)

---
updated-dependencies:
- dependency-name: github.com/charlievieth/fastwalk
  dependency-version: 1.0.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 01:19:14 +09:00
Junegunn Choi
cbed41cd82 No emoji 2025-09-14 21:08:48 +09:00
Junegunn Choi
6684771cbf Fix CTRL-Z for tcell renderer by using the official API
See https://github.com/gdamore/tcell/pull/431
2025-09-14 11:41:12 +09:00
Junegunn Choi
f5f894ea47 Fix rendering of multiple OSC 8 links in a single line
Fix #4517
2025-09-14 11:26:47 +09:00
junegunn
a0a334fc8d Deploying to master from @ junegunn/fzf@ae12e94b1f 🚀 2025-09-07 00:02:03 +00:00
Massimo Mund
ae12e94b1f Add sub-word actions (#3997)
Add `backward-subword`, `forward-subword`, `kill-subword`, `backward-kill-subword` actions.
2025-09-05 19:38:22 +09:00
Massimo Mund
9ed971cc90 Add keybindings for CTRL, ALT, SHIFT + UP, DOWN, RIGHT, LEFT, HOME, END, BACKSPACE, DELETE & more (#3996)
* Added tests for `LightRenderer`

* Added common SHIFT, ALT and ALT+SHIFT key sequences

* Added common CTRL key sequences

* Added common CTRL+ALT, CTRL+SHIFT, CTRL+ALT+SHIFT key sequences

* Added proper xterm META modifier handling

according to defc6dd568/input.c (L357-L375)

* Fix `ctrl-backspace` and `ctrl-alt-backspace`

* Fix broken tcell tests on windows by swallowing Resize events

* Added tests for  FullscreenRenderer

* Removed own fork of tcell and updated tcell to 2.9.0

tcell 2.9.0 is needed for `Ctrl-Alt-*` and `Ctrl-Alt-Shift-*` shortcuts in Windows

* Replace conditional checks with switch statements to improve readability

* Replace long conditionals with constant slices to improve readability

* Bind `ctrl-bspace` (`ctrl-h`) to `backward-delete-char` by default

Since we now distinguish between Backspace and Ctrl-Backspace, Ctrl-Backspace should trigger the same action as Backspace by default. In that way nothing changes for the user but you can bind other actions to Ctrl-Backspace when desired.
2025-09-05 14:56:51 +09:00
Junegunn Choi
129cb23078 Require Go 1.23 2025-09-05 14:45:17 +09:00
dependabot[bot]
d22812e917 Bump github.com/gdamore/tcell/v2 from 2.8.1 to 2.9.0 (#4503)
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.8.1 to 2.9.0.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md)
- [Commits](https://github.com/gdamore/tcell/compare/v2.8.1...v2.9.0)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-version: 2.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 14:39:37 +09:00
Charlie Vieth
10d712824a mod: update charlievieth/fastwalk to v1.0.13 and min Go version to 1.21 (#4508)
This commit updates github.com/charlievieth/fastwalk to v1.0.13 which
addresses fastwalk issue #61. It also updates the minimum supported Go
version to 1.21 (up from 1.20) since that is now the minimum version
supported by fastwalk.
2025-09-04 22:04:52 +09:00
Junegunn Choi
de4059c8fa Update README 2025-09-03 08:18:57 +09:00
Junegunn Choi
416aff86e9 0.65.2 2025-08-31 22:18:44 +09:00
zhedazijingang
59dc7f178f refactor: replace []byte(fmt.Sprintf) with fmt.Appendf (#4507)
Signed-off-by: zhedazijingang <unwrap_or_else@outlook.com>
2025-08-31 22:01:35 +09:00
junegunn
a3c9f8bfee Deploying to master from @ junegunn/fzf@5546c65491 🚀 2025-08-31 00:02:03 +00:00
Junegunn Choi
5546c65491 Fix rendering of items with tabs when using a non-default ellipsis
Fix #4505
2025-08-27 23:31:31 +09:00
junegunn
f2179f015c Deploying to master from @ junegunn/fzf@9a53d84b9c 🚀 2025-08-24 00:02:30 +00:00
Junegunn Choi
9a53d84b9c Update README.md 2025-08-22 22:51:04 +09:00
Junegunn Choi
0a8ff7899c Do not unset FZF_DEFAULT_* variables when using winpty
Fix #4497
Fix #4400
2025-08-22 19:24:01 +09:00
xty
f9d7877d8b [bash 3] Fix CTRL-T and ALT-C to preserve the last yank (#4496) 2025-08-19 23:31:02 +09:00
Peter Sideris
9fe9976591 Fix a typo in man page (#4495) 2025-08-19 23:25:57 +09:00
Chayoung You
de1824f71d [install] Support old uname in macOS (#4492) 2025-08-17 11:54:32 +09:00
dependabot[bot]
19a9296c47 Bump actions/checkout from 4 to 5 (#4485)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 21:17:29 +09:00
Ioannis Pinakoulakis
49967f3d45 Use fixed-length array when possible (#4488) 2025-08-15 21:16:41 +09:00
longhutianjie
978b6254c7 chore: remove redundant word in comment (#4490)
Signed-off-by: longhutianjie <keplrnewton@icloud.com>
2025-08-14 13:26:29 +09:00
Junegunn Choi
1afd143810 Fix incorrect truncation of --info-command with --info=inline-right
Fix #4479
2025-08-08 18:51:24 +09:00
Junegunn Choi
e5cd7f0a3a 0.65.1 2025-08-03 14:41:56 +09:00
junegunn
51d3940c63 Deploying to master from @ junegunn/fzf@179aec1578 🚀 2025-08-03 00:02:30 +00:00
Junegunn Choi
179aec1578 Fix '--color nth:regular' not to reset ANSI attributes of the original text 2025-08-03 00:54:26 +09:00
Junegunn Choi
af0014aba8 Fix a bug where you cannot unset the default --nth using change-nth 2025-08-03 00:29:05 +09:00
Junegunn Choi
da3d995709 Fix $FZF_CLICK_{HEADER,FOOTER}_WORD with ANSI colors and tabs 2025-08-02 16:47:09 +09:00
Junegunn Choi
04c4269db3 0.65.0 2025-07-27 10:39:41 +09:00
junegunn
78f238294f Deploying to master from @ junegunn/fzf@354d0468c1 🚀 2025-07-27 00:02:23 +00:00
LangLangBart
354d0468c1 fix(shell): check for mawk existence before version check (#4468)
close #4463
2025-07-25 17:33:18 +09:00
Junegunn Choi
4efcc344c3 Add 'trigger(KEY_OR_EVENT[,...])' action 2025-07-23 19:41:06 +09:00
Junegunn Choi
5818b58350 Better fix for #4465 - remove unnecessary erase 2025-07-23 19:30:52 +09:00
Junegunn Choi
7941129cc4 Add 'click-footer' event 2025-07-22 23:24:23 +09:00
Junegunn Choi
069d71a840 Fix rendering error when hiding a preview window without border
This was a regression introduced in cdcab267.

Fix #4465
2025-07-22 19:23:10 +09:00
Junegunn Choi
08027e7a79 Fix --no-header-lines-border behavior
It should be different from --header-lines-border=none according to the
man page. It should merge two headers unlike the latter.
2025-07-22 19:16:55 +09:00
Junegunn Choi
ead302981c Add support for {*n} and {*nf} placeholder
Close #4458
2025-07-20 10:53:58 +09:00
junegunn
fe0ffa14ff Deploying to master from @ junegunn/fzf@821b8e70a8 🚀 2025-07-20 00:02:23 +00:00
Junegunn Choi
821b8e70a8 [neovim] Fix margin background color when &winborder is used
Fix #4453
2025-07-19 16:19:48 +09:00
Jaseem Abid
8ceda54c7d Fix a typo in README.md (#4459) 2025-07-16 23:19:43 +09:00
junegunn
84e515bd6e Deploying to master from @ junegunn/fzf@dea1df6878 🚀 2025-07-13 00:02:29 +00:00
Junegunn Choi
dea1df6878 Add missing mention of 'bg-cancel' to the man page 2025-07-12 20:09:54 +09:00
42 changed files with 1994 additions and 483 deletions

View File

@@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@@ -9,6 +9,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: 'Checkout Repository' - name: 'Checkout Repository'
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 'Dependency Review' - name: 'Dependency Review'
uses: actions/dependency-review-action@v4 uses: actions/dependency-review-action@v4

View File

@@ -18,14 +18,14 @@ jobs:
build: build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "1.20" go-version: "1.23"
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1

View File

@@ -15,14 +15,14 @@ jobs:
build: build:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "1.20" go-version: "1.23"
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout 🛎️ - name: Checkout 🛎️
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Generate Sponsors 💖 - name: Generate Sponsors 💖
uses: JamesIves/github-sponsors-readme-action@v1 uses: JamesIves/github-sponsors-readme-action@v1

View File

@@ -6,5 +6,5 @@ jobs:
name: Spell Check with Typos name: Spell Check with Typos
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: crate-ci/typos@v1.29.4 - uses: crate-ci/typos@v1.29.4

View File

@@ -1,2 +1,2 @@
golang 1.20.14 golang 1.23.12
ruby 3.4.1 ruby 3.4.1

View File

@@ -6,7 +6,7 @@ Build instructions
### Prerequisites ### Prerequisites
- Go 1.20 or above - Go 1.23 or above
### Using Makefile ### Using Makefile
@@ -41,6 +41,20 @@ make release
> --profile-block /tmp/block.pprof --profile-mutex /tmp/mutex.pprof > --profile-block /tmp/block.pprof --profile-mutex /tmp/mutex.pprof
> ``` > ```
Running tests
-------------
```sh
# Run go unit tests
make test
# Run integration tests (requires to be on tmux)
make itest
# Run a single test case
ruby test/runner.rb --name test_something
```
Third-party libraries used Third-party libraries used
-------------------------- --------------------------

View File

@@ -1,6 +1,92 @@
CHANGELOG CHANGELOG
========= =========
0.66.0
------
- Style changes
- Updated `--color base16` (alias: `16`) theme so that it works better with both dark and light themes.
- Narrowed the gutter column by using the left-half block character (`▌`).
- Removed background colors from markers.
- Added `--gutter CHAR` option for customizing the gutter column. Some examples using [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_characters):
```sh
# Right-aligned gutter
fzf --gutter '▐'
# Even thinner gutter
fzf --gutter '▎'
# Checker
fzf --gutter '▚'
# Dotted
fzf --gutter '▖'
# Full-width
fzf --gutter '█'
# No gutter
fzf --gutter ' '
```
0.65.2
------
- Bug fixes and improvements
- Fix incorrect truncation of `--info-command` with `--info=inline-right` (#4479)
- [install] Support old uname in macOS (#4492)
- [bash 3] Fix `CTRL-T` and `ALT-C` to preserve the last yank (#4496)
- Do not unset `FZF_DEFAULT_*` variables when using winpty (#4497) (#4400)
- Fix rendering of items with tabs when using a non-default ellipsis (#4505)
- **This is the final release to support Windows 7.**
- Future versions will be built with the latest Go toolchain, which has dropped support for Windows 7.
0.65.1
------
- Fixed incorrect `$FZF_CLICK_HEADER_WORD` and `$FZF_CLICK_FOOTER_WORD` when the header or footer contains ANSI escape sequences and tab characters.
- Fixed a bug where you cannot unset the default `--nth` using `change-nth` action.
- Fixed a highlighting bug when using `--color fg:dim,nth:regular` pattern over ANSI-colored items.
0.65.0
------
- Added `click-footer` event that is triggered when the footer section is clicked. When the event is triggered, the following environment variables are set:
- `$FZF_CLICK_FOOTER_COLUMN` - clicked column (1-based)
- `$FZF_CLICK_FOOTER_LINE` - clicked line (1-based)
- `$FZF_CLICK_FOOTER_WORD` - the word under the cursor
```sh
fzf --footer $'[Edit] [View]\n[Copy to clipboard]' \
--with-shell 'bash -c' \
--bind 'click-footer:transform:
[[ $FZF_CLICK_FOOTER_WORD =~ Edit ]] && echo "execute:vim \{}"
[[ $FZF_CLICK_FOOTER_WORD =~ View ]] && echo "execute:view \{}"
(( FZF_CLICK_FOOTER_LINE == 2 )) && (( FZF_CLICK_FOOTER_COLUMN < 20 )) &&
echo "execute-silent(echo -n \{} | pbcopy)+bell"
'
```
- Added `trigger(...)` action that triggers events bound to another key or event.
```sh
# You can click on each key name to trigger the actions bound to that key
fzf --footer 'Ctrl-E: Edit / Ctrl-V: View / Ctrl-Y: Copy to clipboard' \
--with-shell 'bash -c' \
--bind 'ctrl-e:execute:vim {}' \
--bind 'ctrl-v:execute:view {}' \
--bind 'ctrl-y:execute-silent(echo -n {} | pbcopy)+bell' \
--bind 'click-footer:transform:
[[ $FZF_CLICK_FOOTER_WORD =~ Ctrl ]] && echo "trigger(${FZF_CLICK_FOOTER_WORD%:})"
'
```
- You can specify a series of keys and events
```sh
fzf --bind 'a:up,b:trigger(a,a,a)'
```
- Added support for `{*n}` and `{*nf}` placeholder.
- `{*n}` evaluates to the zero-based ordinal index of all matched items.
- `{*nf}` evaluates to the temporary file containing that.
- Bug fixes and improvements
- [neovim] Fixed margin background color when `&winborder` is used (#4453)
- Fixed rendering error when hiding a preview window without border (#4465)
- fix(shell): check for mawk existence before version check (#4468)
- Thanks to @LangLangBart and @akinomyoga
- Fixed `--no-header-lines-border` behavior (08027e7a)
0.64.0 0.64.0
------ ------
- Added `multi` event that is triggered when the multi-selection has changed. - Added `multi` event that is triggered when the multi-selection has changed.

File diff suppressed because one or more lines are too long

12
go.mod
View File

@@ -1,20 +1,20 @@
module github.com/junegunn/fzf module github.com/junegunn/fzf
require ( require (
github.com/charlievieth/fastwalk v1.0.12 github.com/charlievieth/fastwalk v1.0.14
github.com/gdamore/tcell/v2 v2.8.1 github.com/gdamore/tcell/v2 v2.9.0
github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741 github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741
github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-isatty v0.0.20
github.com/rivo/uniseg v0.4.7 github.com/rivo/uniseg v0.4.7
golang.org/x/sys v0.30.0 golang.org/x/sys v0.35.0
golang.org/x/term v0.29.0 golang.org/x/term v0.34.0
) )
require ( require (
github.com/gdamore/encoding v1.0.1 // indirect github.com/gdamore/encoding v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.28.0 // indirect
) )
go 1.20 go 1.23.0

52
go.sum
View File

@@ -1,10 +1,9 @@
github.com/charlievieth/fastwalk v1.0.12 h1:pwfxe1LajixViQqo7EFLXU2+mQxb6OaO0CeNdVwRKTg= github.com/charlievieth/fastwalk v1.0.14 h1:3Eh5uaFGwHZd8EGwTjJnSpBkfwfsak9h6ICgnWlhAyg=
github.com/charlievieth/fastwalk v1.0.12/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI= github.com/charlievieth/fastwalk v1.0.14/go.mod h1:diVcUreiU1aQ4/Wu3NbxxH4/KYdKpLDojrQ1Bb2KgNY=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU= github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw= github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741 h1:7dYDtfMDfKzjT+DVfIS4iqknSEKtZpEcXtu6vuaasHs= github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741 h1:7dYDtfMDfKzjT+DVfIS4iqknSEKtZpEcXtu6vuaasHs=
github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741/go.mod h1:6EILKtGpo5t+KLb85LNZLAF6P9LKp78hJI80PXMcn3c= github.com/junegunn/go-shellwords v0.0.0-20250127100254-2aa3b3277741/go.mod h1:6EILKtGpo5t+KLb85LNZLAF6P9LKp78hJI80PXMcn3c=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -14,35 +13,20 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -50,38 +34,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.64.0 version=0.65.2
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2
@@ -164,7 +164,7 @@ download() {
} }
# Try to download binary executable # Try to download binary executable
archi=$(uname -smo) archi=$(uname -smo 2>/dev/null || uname -sm)
binary_available=1 binary_available=1
binary_error="" binary_error=""
case "$archi" in case "$archi" in

View File

@@ -1,4 +1,4 @@
$version="0.64.0" $version="0.65.2"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -11,7 +11,7 @@ import (
"github.com/junegunn/fzf/src/protector" "github.com/junegunn/fzf/src/protector"
) )
var version = "0.64" var version = "0.65"
var revision = "devel" var revision = "devel"
//go:embed shell/key-bindings.bash //go:embed shell/key-bindings.bash

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf\-tmux 1 "Jul 2025" "fzf 0.64.0" "fzf\-tmux - open fzf in tmux split pane" .TH fzf\-tmux 1 "Aug 2025" "fzf 0.65.2" "fzf\-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf\-tmux - open fzf in tmux split pane fzf\-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Jul 2025" "fzf 0.64.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Aug 2025" "fzf 0.65.2" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -246,11 +246,11 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
.RS .RS
.B BASE SCHEME: .B BASE SCHEME:
(default: \fBdark\fR on 256-color terminal, otherwise \fB16\fR; If \fBNO_COLOR\fR is set, \fBbw\fR) (default: \fBdark\fR on 256-color terminal, otherwise \fBbase16\fR; If \fBNO_COLOR\fR is set, \fBbw\fR)
\fBdark \fRColor scheme for dark 256-color terminal \fBdark \fRColor scheme for dark terminal
\fBlight \fRColor scheme for light 256-color terminal \fBlight \fRColor scheme for light terminal
\fB16 \fRColor scheme for 16-color terminal \fBbase16 \fRColor scheme using base 16 colors (alias: \fB16\fR)
\fBbw \fRNo colors (equivalent to \fB\-\-no\-color\fR) \fBbw \fRNo colors (equivalent to \fB\-\-no\-color\fR)
.B COLOR NAMES: .B COLOR NAMES:
@@ -1046,7 +1046,7 @@ are not affected by \fB\-\-with\-nth\fR. ANSI color codes are processed even whe
.TP .TP
.BI "\-\-footer\-border" [=STYLE] .BI "\-\-footer\-border" [=STYLE]
Draw border around the header section. \fBline\fR style draws a single Draw border around the footer section. \fBline\fR style draws a single
separator line between the footer and the list section. separator line between the footer and the list section.
.TP .TP
@@ -1473,12 +1473,22 @@ e.g.
.br .br
\fIalt\-right\fR \fIalt\-right\fR
.br .br
\fIalt\-home\fR
.br
\fIalt\-end\fR
.br
\fIalt\-backspace\fR (\fIalt\-bspace\fR \fIalt\-bs\fR)
.br
\fIalt\-delete\fR
.br
\fIalt\-page\-up\fR
.br
\fIalt\-page\-down\fR
.br
\fIalt\-enter\fR \fIalt\-enter\fR
.br .br
\fIalt\-space\fR \fIalt\-space\fR
.br .br
\fIalt\-backspace\fR (\fIalt\-bspace\fR \fIalt\-bs\fR)
.br
\fItab\fR \fItab\fR
.br .br
\fIshift\-tab\fR (\fIbtab\fR) \fIshift\-tab\fR (\fIbtab\fR)
@@ -1505,6 +1515,26 @@ e.g.
.br .br
\fIpage\-down\fR (\fIpgdn\fR) \fIpage\-down\fR (\fIpgdn\fR)
.br .br
\fIctrl\-up\fR
.br
\fIctrl\-down\fR
.br
\fIctrl\-left\fR
.br
\fIctrl\-right\fR
.br
\fIctrl\-home\fR
.br
\fIctrl\-end\fR
.br
\fIctrl\-backspace\fR (\fIctrl\-bspace\fR \fIctrl\-bs\fR)
.br
\fIctrl\-delete\fR
.br
\fIctrl\-page\-up\fR
.br
\fIctrl\-page\-down\fR
.br
\fIshift\-up\fR \fIshift\-up\fR
.br .br
\fIshift\-down\fR \fIshift\-down\fR
@@ -1513,8 +1543,16 @@ e.g.
.br .br
\fIshift\-right\fR \fIshift\-right\fR
.br .br
\fIshift\-home\fR
.br
\fIshift\-end\fR
.br
\fIshift\-delete\fR \fIshift\-delete\fR
.br .br
\fIshift\-page\-up\fR
.br
\fIshift\-page\-down\fR
.br
\fIalt\-shift\-up\fR \fIalt\-shift\-up\fR
.br .br
\fIalt\-shift\-down\fR \fIalt\-shift\-down\fR
@@ -1523,6 +1561,72 @@ e.g.
.br .br
\fIalt\-shift\-right\fR \fIalt\-shift\-right\fR
.br .br
\fIalt\-shift\-home\fR
.br
\fIalt\-shift\-end\fR
.br
\fIalt\-shift\-delete\fR
.br
\fIalt\-shift\-page\-up\fR
.br
\fIalt\-shift\-page\-down\fR
.br
\fIctrl\-alt\-up\fR
.br
\fIctrl\-alt\-down\fR
.br
\fIctrl\-alt\-left\fR
.br
\fIctrl\-alt\-right\fR
.br
\fIctrl\-alt\-home\fR
.br
\fIctrl\-alt\-end\fR
.br
\fIctrl\-alt\-backspace\fR (\fIctrl\-alt\-bspace\fR \fIctrl\-alt\-bs\fR)
.br
\fIctrl\-alt\-delete\fR
.br
\fIctrl\-alt\-page\-up\fR
.br
\fIctrl\-alt\-page\-down\fR
.br
\fIctrl\-shift\-up\fR
.br
\fIctrl\-shift\-down\fR
.br
\fIctrl\-shift\-left\fR
.br
\fIctrl\-shift\-right\fR
.br
\fIctrl\-shift\-home\fR
.br
\fIctrl\-shift\-end\fR
.br
\fIctrl\-shift\-delete\fR
.br
\fIctrl\-shift\-page\-up\fR
.br
\fIctrl\-shift\-page\-down\fR
.br
\fIctrl\-alt\-shift\-up\fR
.br
\fIctrl\-alt\-shift\-down\fR
.br
\fIctrl\-alt\-shift\-left\fR
.br
\fIctrl\-alt\-shift\-right\fR
.br
\fIctrl\-alt\-shift\-home\fR
.br
\fIctrl\-alt\-shift\-end\fR
.br
\fIctrl\-alt\-shift\-delete\fR
.br
\fIctrl\-alt\-shift\-page\-up\fR
.br
\fIctrl\-alt\-shift\-page\-down\fR
.br
\fIleft\-click\fR \fIleft\-click\fR
.br .br
\fIright\-click\fR \fIright\-click\fR
@@ -1547,6 +1651,8 @@ e.g.
.br .br
or any single character or any single character
Note that some terminal emulators may not support \fIctrl-*\fR bindings.
.SS AVAILABLE EVENTS: .SS AVAILABLE EVENTS:
\fIstart\fR \fIstart\fR
.RS .RS
@@ -1682,6 +1788,14 @@ e.g.
)'\fR )'\fR
.RE .RE
\fIclick\-footer\fR
.RS
Triggered when a mouse click occurs within the footer. Sets
\fBFZF_CLICK_FOOTER_LINE\fR and \fBFZF_CLICK_FOOTER_COLUMN\fR environment
variables starting from 1. It optionally sets \fBFZF_CLICK_FOOTER_WORD\fR
if clicked on a word.
.RE
.SS AVAILABLE ACTIONS: .SS AVAILABLE ACTIONS:
A key or an event can be bound to one or more of the following actions. A key or an event can be bound to one or more of the following actions.
@@ -1691,13 +1805,16 @@ A key or an event can be bound to one or more of the following actions.
\fBaccept\-non\-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection) \fBaccept\-non\-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection)
\fBaccept\-or\-print\-query\fR (same as \fBaccept\fR except that it prints the query when there's no match) \fBaccept\-or\-print\-query\fR (same as \fBaccept\fR except that it prints the query when there's no match)
\fBbackward\-char\fR \fIctrl\-b left\fR \fBbackward\-char\fR \fIctrl\-b left\fR
\fBbackward\-delete\-char\fR \fIctrl\-h bspace\fR \fBbackward\-delete\-char\fR \fIctrl\-h ctrl\-bspace bspace\fR
\fBbackward\-delete\-char/eof\fR (same as \fBbackward\-delete\-char\fR except aborts fzf if query is empty) \fBbackward\-delete\-char/eof\fR (same as \fBbackward\-delete\-char\fR except aborts fzf if query is empty)
\fBbackward\-kill\-subword\fR
\fBbackward\-kill\-word\fR \fIalt\-bs\fR \fBbackward\-kill\-word\fR \fIalt\-bs\fR
\fBbackward\-subword\fR
\fBbackward\-word\fR \fIalt\-b shift\-left\fR \fBbackward\-word\fR \fIalt\-b shift\-left\fR
\fBbecome(...)\fR (replace fzf process with the specified command; see below for the details) \fBbecome(...)\fR (replace fzf process with the specified command; see below for the details)
\fBbeginning\-of\-line\fR \fIctrl\-a home\fR \fBbeginning\-of\-line\fR \fIctrl\-a home\fR
\fBbell\fR (ring the terminal bell) \fBbell\fR (ring the terminal bell)
\fBbg\-cancel\fR (cancel background transform processes)
\fBcancel\fR (clear query string if not empty, abort fzf otherwise) \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string) \fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string)
\fBchange\-ghost(...)\fR (change ghost text to the given string) \fBchange\-ghost(...)\fR (change ghost text to the given string)
@@ -1732,10 +1849,12 @@ A key or an event can be bound to one or more of the following actions.
\fBexecute\-silent(...)\fR (see below for the details) \fBexecute\-silent(...)\fR (see below for the details)
\fBfirst\fR (move to the first match; same as \fBpos(1)\fR) \fBfirst\fR (move to the first match; same as \fBpos(1)\fR)
\fBforward\-char\fR \fIctrl\-f right\fR \fBforward\-char\fR \fIctrl\-f right\fR
\fBforward\-subword\fR
\fBforward\-word\fR \fIalt\-f shift\-right\fR \fBforward\-word\fR \fIalt\-f shift\-right\fR
\fBignore\fR \fBignore\fR
\fBjump\fR (EasyMotion-like 2-keystroke movement) \fBjump\fR (EasyMotion-like 2-keystroke movement)
\fBkill\-line\fR \fBkill\-line\fR
\fBkill\-subword\fR
\fBkill\-word\fR \fIalt\-d\fR \fBkill\-word\fR \fIalt\-d\fR
\fBlast\fR (move to the last match; same as \fBpos(\-1)\fR) \fBlast\fR (move to the last match; same as \fBpos(\-1)\fR)
\fBnext\-history\fR (\fIctrl\-n\fR on \fB\-\-history\fR) \fBnext\-history\fR (\fIctrl\-n\fR on \fB\-\-history\fR)
@@ -1808,6 +1927,7 @@ A key or an event can be bound to one or more of the following actions.
\fBtransform\-prompt(...)\fR (transform prompt string using an external command) \fBtransform\-prompt(...)\fR (transform prompt string using an external command)
\fBtransform\-query(...)\fR (transform query string using an external command) \fBtransform\-query(...)\fR (transform query string using an external command)
\fBtransform\-search(...)\fR (trigger fzf search with the output of an external command) \fBtransform\-search(...)\fR (trigger fzf search with the output of an external command)
\fBtrigger(...)\fR (trigger actions bound to a comma-separated list of keys and events)
\fBunbind(...)\fR (unbind bindings) \fBunbind(...)\fR (unbind bindings)
\fBunix\-line\-discard\fR \fIctrl\-u\fR \fBunix\-line\-discard\fR \fIctrl\-u\fR
\fBunix\-word\-rubout\fR \fIctrl\-w\fR \fBunix\-word\-rubout\fR \fIctrl\-w\fR
@@ -1959,7 +2079,8 @@ Transform actions are synchronous, meaning fzf becomes unresponsive while the
command runs. To avoid this, each \fBtransform*\fR action has a corresponding command runs. To avoid this, each \fBtransform*\fR action has a corresponding
\fBbg\-transform*\fR variant that runs in the background. Unless you need to \fBbg\-transform*\fR variant that runs in the background. Unless you need to
chain multiple transform actions where later ones depend on earlier results, chain multiple transform actions where later ones depend on earlier results,
prefer using the \fBbg\fR variant. prefer using the \fBbg\fR variant. To cancel currently running background
transform processes, use \fBbg\-cancel\fR action.
.SS PREVIEW BINDING .SS PREVIEW BINDING

View File

@@ -1027,8 +1027,23 @@ if has('nvim')
let buf = nvim_create_buf(v:false, v:true) let buf = nvim_create_buf(v:false, v:true)
let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts) let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)
let win = nvim_open_win(buf, v:true, opts) let win = nvim_open_win(buf, v:true, opts)
silent! call setwinvar(win, '&winhighlight', 'Pmenu:,Normal:Normal')
call setwinvar(win, '&colorcolumn', '') call setwinvar(win, '&colorcolumn', '')
" Colors
try
call setwinvar(win, '&winhighlight', 'Pmenu:,Normal:Normal')
let rules = get(g:, 'fzf_colors', {})
if has_key(rules, 'bg')
let color = call('s:get_color', rules.bg)
if len(color)
let ns = nvim_create_namespace('fzf_popup')
let hl = nvim_set_hl(ns, 'Normal',
\ &termguicolors ? { 'bg': color } : { 'ctermbg': str2nr(color) })
call nvim_win_set_hl_ns(win, ns)
endif
endif
catch
endtry
return buf return buf
endfunction endfunction
else else

View File

@@ -22,11 +22,11 @@ __fzf_exec_awk() {
# modern point of view. To use a standard-conforming version in Solaris, # modern point of view. To use a standard-conforming version in Solaris,
# one needs to explicitly use /usr/xpg4/bin/awk. # one needs to explicitly use /usr/xpg4/bin/awk.
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
else elif command -v mawk >/dev/null 2>&1; then
# choose the faster mawk if: it's installed && build date >= 20230322 && # choose the faster mawk if: it's installed && build date >= 20230322 &&
# version >= 1.3.4 # version >= 1.3.4
local n x y z d local n x y z d
IFS=' .' read n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
fi fi
fi fi

View File

@@ -47,9 +47,9 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
else elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
fi fi
fi fi
@@ -524,7 +524,7 @@ if ! declare -F __fzf_list_hosts > /dev/null; then
if ($i != "0.0.0.0") if ($i != "0.0.0.0")
print $i print $i
} }
' /etc/hosts 2> /dev/null ' /etc/hosts 2> /dev/null
) )
} }
fi fi

View File

@@ -112,9 +112,9 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
else elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
fi fi
fi fi

View File

@@ -33,9 +33,9 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
else elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
fi fi
fi fi
@@ -125,7 +125,7 @@ bind -m emacs-standard '"\C-z": vi-editing-mode'
if (( BASH_VERSINFO[0] < 4 )); then if (( BASH_VERSINFO[0] < 4 )); then
# CTRL-T - Paste the selected file path into the command line # CTRL-T - Paste the selected file path into the command line
if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"' bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f\C-y\ey\C-_"'
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"' bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"' bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
fi fi
@@ -150,7 +150,7 @@ fi
# ALT-C - cd into the selected directory # ALT-C - cd into the selected directory
if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"' bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d\C-y\ey\C-_"'
bind -m vi-command '"\ec": "\C-z\ec\C-z"' bind -m vi-command '"\ec": "\C-z\ec\C-z"'
bind -m vi-insert '"\ec": "\C-z\ec\C-z"' bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
fi fi

View File

@@ -54,9 +54,9 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
else elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
fi fi
fi fi

View File

@@ -25,153 +25,158 @@ func _() {
_ = x[actBackwardDeleteChar-14] _ = x[actBackwardDeleteChar-14]
_ = x[actBackwardDeleteCharEof-15] _ = x[actBackwardDeleteCharEof-15]
_ = x[actBackwardWord-16] _ = x[actBackwardWord-16]
_ = x[actCancel-17] _ = x[actBackwardSubWord-17]
_ = x[actChangeBorderLabel-18] _ = x[actCancel-18]
_ = x[actChangeGhost-19] _ = x[actChangeBorderLabel-19]
_ = x[actChangeHeader-20] _ = x[actChangeGhost-20]
_ = x[actChangeFooter-21] _ = x[actChangeHeader-21]
_ = x[actChangeHeaderLabel-22] _ = x[actChangeFooter-22]
_ = x[actChangeFooterLabel-23] _ = x[actChangeHeaderLabel-23]
_ = x[actChangeInputLabel-24] _ = x[actChangeFooterLabel-24]
_ = x[actChangeListLabel-25] _ = x[actChangeInputLabel-25]
_ = x[actChangeMulti-26] _ = x[actChangeListLabel-26]
_ = x[actChangeNth-27] _ = x[actChangeMulti-27]
_ = x[actChangePointer-28] _ = x[actChangeNth-28]
_ = x[actChangePreview-29] _ = x[actChangePointer-29]
_ = x[actChangePreviewLabel-30] _ = x[actChangePreview-30]
_ = x[actChangePreviewWindow-31] _ = x[actChangePreviewLabel-31]
_ = x[actChangePrompt-32] _ = x[actChangePreviewWindow-32]
_ = x[actChangeQuery-33] _ = x[actChangePrompt-33]
_ = x[actClearScreen-34] _ = x[actChangeQuery-34]
_ = x[actClearQuery-35] _ = x[actClearScreen-35]
_ = x[actClearSelection-36] _ = x[actClearQuery-36]
_ = x[actClose-37] _ = x[actClearSelection-37]
_ = x[actDeleteChar-38] _ = x[actClose-38]
_ = x[actDeleteCharEof-39] _ = x[actDeleteChar-39]
_ = x[actEndOfLine-40] _ = x[actDeleteCharEof-40]
_ = x[actFatal-41] _ = x[actEndOfLine-41]
_ = x[actForwardChar-42] _ = x[actFatal-42]
_ = x[actForwardWord-43] _ = x[actForwardChar-43]
_ = x[actKillLine-44] _ = x[actForwardWord-44]
_ = x[actKillWord-45] _ = x[actForwardSubWord-45]
_ = x[actUnixLineDiscard-46] _ = x[actKillLine-46]
_ = x[actUnixWordRubout-47] _ = x[actKillWord-47]
_ = x[actYank-48] _ = x[actKillSubWord-48]
_ = x[actBackwardKillWord-49] _ = x[actUnixLineDiscard-49]
_ = x[actSelectAll-50] _ = x[actUnixWordRubout-50]
_ = x[actDeselectAll-51] _ = x[actYank-51]
_ = x[actToggle-52] _ = x[actBackwardKillWord-52]
_ = x[actToggleSearch-53] _ = x[actBackwardKillSubWord-53]
_ = x[actToggleAll-54] _ = x[actSelectAll-54]
_ = x[actToggleDown-55] _ = x[actDeselectAll-55]
_ = x[actToggleUp-56] _ = x[actToggle-56]
_ = x[actToggleIn-57] _ = x[actToggleSearch-57]
_ = x[actToggleOut-58] _ = x[actToggleAll-58]
_ = x[actToggleTrack-59] _ = x[actToggleDown-59]
_ = x[actToggleTrackCurrent-60] _ = x[actToggleUp-60]
_ = x[actToggleHeader-61] _ = x[actToggleIn-61]
_ = x[actToggleWrap-62] _ = x[actToggleOut-62]
_ = x[actToggleMultiLine-63] _ = x[actToggleTrack-63]
_ = x[actToggleHscroll-64] _ = x[actToggleTrackCurrent-64]
_ = x[actTrackCurrent-65] _ = x[actToggleHeader-65]
_ = x[actToggleInput-66] _ = x[actToggleWrap-66]
_ = x[actHideInput-67] _ = x[actToggleMultiLine-67]
_ = x[actShowInput-68] _ = x[actToggleHscroll-68]
_ = x[actUntrackCurrent-69] _ = x[actTrackCurrent-69]
_ = x[actDown-70] _ = x[actToggleInput-70]
_ = x[actUp-71] _ = x[actHideInput-71]
_ = x[actPageUp-72] _ = x[actShowInput-72]
_ = x[actPageDown-73] _ = x[actUntrackCurrent-73]
_ = x[actPosition-74] _ = x[actDown-74]
_ = x[actHalfPageUp-75] _ = x[actUp-75]
_ = x[actHalfPageDown-76] _ = x[actPageUp-76]
_ = x[actOffsetUp-77] _ = x[actPageDown-77]
_ = x[actOffsetDown-78] _ = x[actPosition-78]
_ = x[actOffsetMiddle-79] _ = x[actHalfPageUp-79]
_ = x[actJump-80] _ = x[actHalfPageDown-80]
_ = x[actJumpAccept-81] _ = x[actOffsetUp-81]
_ = x[actPrintQuery-82] _ = x[actOffsetDown-82]
_ = x[actRefreshPreview-83] _ = x[actOffsetMiddle-83]
_ = x[actReplaceQuery-84] _ = x[actJump-84]
_ = x[actToggleSort-85] _ = x[actJumpAccept-85]
_ = x[actShowPreview-86] _ = x[actPrintQuery-86]
_ = x[actHidePreview-87] _ = x[actRefreshPreview-87]
_ = x[actTogglePreview-88] _ = x[actReplaceQuery-88]
_ = x[actTogglePreviewWrap-89] _ = x[actToggleSort-89]
_ = x[actTransform-90] _ = x[actShowPreview-90]
_ = x[actTransformBorderLabel-91] _ = x[actHidePreview-91]
_ = x[actTransformGhost-92] _ = x[actTogglePreview-92]
_ = x[actTransformHeader-93] _ = x[actTogglePreviewWrap-93]
_ = x[actTransformFooter-94] _ = x[actTransform-94]
_ = x[actTransformHeaderLabel-95] _ = x[actTransformBorderLabel-95]
_ = x[actTransformFooterLabel-96] _ = x[actTransformGhost-96]
_ = x[actTransformInputLabel-97] _ = x[actTransformHeader-97]
_ = x[actTransformListLabel-98] _ = x[actTransformFooter-98]
_ = x[actTransformNth-99] _ = x[actTransformHeaderLabel-99]
_ = x[actTransformPointer-100] _ = x[actTransformFooterLabel-100]
_ = x[actTransformPreviewLabel-101] _ = x[actTransformInputLabel-101]
_ = x[actTransformPrompt-102] _ = x[actTransformListLabel-102]
_ = x[actTransformQuery-103] _ = x[actTransformNth-103]
_ = x[actTransformSearch-104] _ = x[actTransformPointer-104]
_ = x[actBgTransform-105] _ = x[actTransformPreviewLabel-105]
_ = x[actBgTransformBorderLabel-106] _ = x[actTransformPrompt-106]
_ = x[actBgTransformGhost-107] _ = x[actTransformQuery-107]
_ = x[actBgTransformHeader-108] _ = x[actTransformSearch-108]
_ = x[actBgTransformFooter-109] _ = x[actTrigger-109]
_ = x[actBgTransformHeaderLabel-110] _ = x[actBgTransform-110]
_ = x[actBgTransformFooterLabel-111] _ = x[actBgTransformBorderLabel-111]
_ = x[actBgTransformInputLabel-112] _ = x[actBgTransformGhost-112]
_ = x[actBgTransformListLabel-113] _ = x[actBgTransformHeader-113]
_ = x[actBgTransformNth-114] _ = x[actBgTransformFooter-114]
_ = x[actBgTransformPointer-115] _ = x[actBgTransformHeaderLabel-115]
_ = x[actBgTransformPreviewLabel-116] _ = x[actBgTransformFooterLabel-116]
_ = x[actBgTransformPrompt-117] _ = x[actBgTransformInputLabel-117]
_ = x[actBgTransformQuery-118] _ = x[actBgTransformListLabel-118]
_ = x[actBgTransformSearch-119] _ = x[actBgTransformNth-119]
_ = x[actBgCancel-120] _ = x[actBgTransformPointer-120]
_ = x[actSearch-121] _ = x[actBgTransformPreviewLabel-121]
_ = x[actPreview-122] _ = x[actBgTransformPrompt-122]
_ = x[actPreviewTop-123] _ = x[actBgTransformQuery-123]
_ = x[actPreviewBottom-124] _ = x[actBgTransformSearch-124]
_ = x[actPreviewUp-125] _ = x[actBgCancel-125]
_ = x[actPreviewDown-126] _ = x[actSearch-126]
_ = x[actPreviewPageUp-127] _ = x[actPreview-127]
_ = x[actPreviewPageDown-128] _ = x[actPreviewTop-128]
_ = x[actPreviewHalfPageUp-129] _ = x[actPreviewBottom-129]
_ = x[actPreviewHalfPageDown-130] _ = x[actPreviewUp-130]
_ = x[actPrevHistory-131] _ = x[actPreviewDown-131]
_ = x[actPrevSelected-132] _ = x[actPreviewPageUp-132]
_ = x[actPrint-133] _ = x[actPreviewPageDown-133]
_ = x[actPut-134] _ = x[actPreviewHalfPageUp-134]
_ = x[actNextHistory-135] _ = x[actPreviewHalfPageDown-135]
_ = x[actNextSelected-136] _ = x[actPrevHistory-136]
_ = x[actExecute-137] _ = x[actPrevSelected-137]
_ = x[actExecuteSilent-138] _ = x[actPrint-138]
_ = x[actExecuteMulti-139] _ = x[actPut-139]
_ = x[actSigStop-140] _ = x[actNextHistory-140]
_ = x[actFirst-141] _ = x[actNextSelected-141]
_ = x[actLast-142] _ = x[actExecute-142]
_ = x[actReload-143] _ = x[actExecuteSilent-143]
_ = x[actReloadSync-144] _ = x[actExecuteMulti-144]
_ = x[actDisableSearch-145] _ = x[actSigStop-145]
_ = x[actEnableSearch-146] _ = x[actFirst-146]
_ = x[actSelect-147] _ = x[actLast-147]
_ = x[actDeselect-148] _ = x[actReload-148]
_ = x[actUnbind-149] _ = x[actReloadSync-149]
_ = x[actRebind-150] _ = x[actDisableSearch-150]
_ = x[actToggleBind-151] _ = x[actEnableSearch-151]
_ = x[actBecome-152] _ = x[actSelect-152]
_ = x[actShowHeader-153] _ = x[actDeselect-153]
_ = x[actHideHeader-154] _ = x[actUnbind-154]
_ = x[actBell-155] _ = x[actRebind-155]
_ = x[actExclude-156] _ = x[actToggleBind-156]
_ = x[actExcludeMulti-157] _ = x[actBecome-157]
_ = x[actAsync-158] _ = x[actShowHeader-158]
_ = x[actHideHeader-159]
_ = x[actBell-160]
_ = x[actExclude-161]
_ = x[actExcludeMulti-162]
_ = x[actAsync-163]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync" const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 249, 269, 283, 298, 313, 333, 353, 372, 390, 404, 416, 432, 448, 469, 491, 506, 520, 534, 547, 564, 572, 585, 601, 613, 621, 635, 649, 660, 671, 689, 706, 713, 732, 744, 758, 767, 782, 794, 807, 818, 829, 841, 855, 876, 891, 904, 922, 938, 953, 967, 979, 991, 1008, 1015, 1020, 1029, 1040, 1051, 1064, 1079, 1090, 1103, 1118, 1125, 1138, 1151, 1168, 1183, 1196, 1210, 1224, 1240, 1260, 1272, 1295, 1312, 1330, 1348, 1371, 1394, 1416, 1437, 1452, 1471, 1495, 1513, 1530, 1548, 1562, 1587, 1606, 1626, 1646, 1671, 1696, 1720, 1743, 1760, 1781, 1807, 1827, 1846, 1866, 1877, 1886, 1896, 1909, 1925, 1937, 1951, 1967, 1985, 2005, 2027, 2041, 2056, 2064, 2070, 2084, 2099, 2109, 2125, 2140, 2150, 2158, 2165, 2174, 2187, 2203, 2218, 2227, 2238, 2247, 2256, 2269, 2278, 2291, 2304, 2311, 2321, 2336, 2344} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 331, 351, 371, 390, 408, 422, 434, 450, 466, 487, 509, 524, 538, 552, 565, 582, 590, 603, 619, 631, 639, 653, 667, 684, 695, 706, 720, 738, 755, 762, 781, 803, 815, 829, 838, 853, 865, 878, 889, 900, 912, 926, 947, 962, 975, 993, 1009, 1024, 1038, 1050, 1062, 1079, 1086, 1091, 1100, 1111, 1122, 1135, 1150, 1161, 1174, 1189, 1196, 1209, 1222, 1239, 1254, 1267, 1281, 1295, 1311, 1331, 1343, 1366, 1383, 1401, 1419, 1442, 1465, 1487, 1508, 1523, 1542, 1566, 1584, 1601, 1619, 1629, 1643, 1668, 1687, 1707, 1727, 1752, 1777, 1801, 1824, 1841, 1862, 1888, 1908, 1927, 1947, 1958, 1967, 1977, 1990, 2006, 2018, 2032, 2048, 2066, 2086, 2108, 2122, 2137, 2145, 2151, 2165, 2180, 2190, 2206, 2221, 2231, 2239, 2246, 2255, 2268, 2284, 2299, 2308, 2319, 2328, 2337, 2350, 2359, 2372, 2385, 2392, 2402, 2417, 2425}
func (i actionType) String() string { func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) { if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@@ -52,7 +52,7 @@ func TestChunkList(t *testing.T) {
// Add more data // Add more data
for i := 0; i < chunkSize*2; i++ { for i := 0; i < chunkSize*2; i++ {
cl.Push([]byte(fmt.Sprintf("item %d", i))) cl.Push(fmt.Appendf(nil, "item %d", i))
} }
// Previous snapshot should remain the same // Previous snapshot should remain the same
@@ -86,7 +86,7 @@ func TestChunkListTail(t *testing.T) {
}) })
total := chunkSize*2 + chunkSize/2 total := chunkSize*2 + chunkSize/2
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
cl.Push([]byte(fmt.Sprintf("item %d", i))) cl.Push(fmt.Appendf(nil, "item %d", i))
} }
snapshot, count, changed := cl.Snapshot(0) snapshot, count, changed := cl.Snapshot(0)

View File

@@ -59,7 +59,7 @@ Usage: fzf [options]
GLOBAL STYLE GLOBAL STYLE
--style=PRESET Apply a style preset [default|minimal|full[:BORDER_STYLE] --style=PRESET Apply a style preset [default|minimal|full[:BORDER_STYLE]
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors --color=COLSPEC Base scheme (dark|light|base16|bw) and/or custom colors
--no-color Disable colors --no-color Disable colors
--no-bold Do not use bold text --no-bold Do not use bold text
@@ -590,6 +590,7 @@ type Options struct {
Separator *string Separator *string
JumpLabels string JumpLabels string
Prompt string Prompt string
Gutter *string
Pointer *string Pointer *string
Marker *string Marker *string
MarkerMulti *[3]string MarkerMulti *[3]string
@@ -710,6 +711,7 @@ func defaultOptions() *Options {
Separator: nil, Separator: nil,
JumpLabels: defaultJumpLabels, JumpLabels: defaultJumpLabels,
Prompt: "> ", Prompt: "> ",
Gutter: nil,
Pointer: nil, Pointer: nil,
Marker: nil, Marker: nil,
MarkerMulti: nil, MarkerMulti: nil,
@@ -932,15 +934,12 @@ func parseBorder(str string, optional bool) (tui.BorderShape, error) {
return tui.BorderNone, errors.New("invalid border style (expected: rounded|sharp|bold|block|thinblock|double|horizontal|vertical|top|bottom|left|right|none)") return tui.BorderNone, errors.New("invalid border style (expected: rounded|sharp|bold|block|thinblock|double|horizontal|vertical|top|bottom|left|right|none)")
} }
func parseKeyChords(str string, message string) (map[tui.Event]string, error) { func parseKeyChords(str string, message string) (map[tui.Event]string, []tui.Event, error) {
return parseKeyChordsImpl(str, message)
}
func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error) {
if len(str) == 0 { if len(str) == 0 {
return nil, errors.New(message) return nil, nil, errors.New(message)
} }
list := []tui.Event{}
str = regexp.MustCompile("(?i)(alt-),").ReplaceAllString(str, "$1"+string([]rune{escapedComma})) str = regexp.MustCompile("(?i)(alt-),").ReplaceAllString(str, "$1"+string([]rune{escapedComma}))
tokens := strings.Split(str, ",") tokens := strings.Split(str, ",")
if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Contains(str, ",,,") { if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Contains(str, ",,,") {
@@ -956,6 +955,7 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
lkey := strings.ToLower(key) lkey := strings.ToLower(key)
add := func(e tui.EventType) { add := func(e tui.EventType) {
chords[e.AsEvent()] = key chords[e.AsEvent()] = key
list = append(list, e.AsEvent())
} }
switch lkey { switch lkey {
case "up": case "up":
@@ -969,13 +969,13 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
case "enter", "return": case "enter", "return":
add(tui.Enter) add(tui.Enter)
case "space": case "space":
chords[tui.Key(' ')] = key evt := tui.Key(' ')
chords[evt] = key
list = append(list, evt)
case "backspace", "bspace", "bs": case "backspace", "bspace", "bs":
add(tui.Backspace) add(tui.Backspace)
case "ctrl-space": case "ctrl-space":
add(tui.CtrlSpace) add(tui.CtrlSpace)
case "ctrl-delete":
add(tui.CtrlDelete)
case "ctrl-^", "ctrl-6": case "ctrl-^", "ctrl-6":
add(tui.CtrlCaret) add(tui.CtrlCaret)
case "ctrl-/", "ctrl-_": case "ctrl-/", "ctrl-_":
@@ -1008,14 +1008,24 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
add(tui.JumpCancel) add(tui.JumpCancel)
case "click-header": case "click-header":
add(tui.ClickHeader) add(tui.ClickHeader)
case "click-footer":
add(tui.ClickFooter)
case "multi": case "multi":
add(tui.Multi) add(tui.Multi)
case "alt-enter", "alt-return": case "alt-enter", "alt-return":
chords[tui.CtrlAltKey('m')] = key evt := tui.CtrlAltKey('m')
chords[evt] = key
list = append(list, evt)
case "alt-space": case "alt-space":
chords[tui.AltKey(' ')] = key evt := tui.AltKey(' ')
chords[evt] = key
list = append(list, evt)
case "alt-bs", "alt-bspace", "alt-backspace": case "alt-bs", "alt-bspace", "alt-backspace":
add(tui.AltBackspace) add(tui.AltBackspace)
case "ctrl-bs", "ctrl-bspace", "ctrl-backspace":
add(tui.CtrlBackspace)
case "ctrl-alt-bs", "ctrl-alt-bspace", "ctrl-alt-backspace":
add(tui.CtrlAltBackspace)
case "alt-up": case "alt-up":
add(tui.AltUp) add(tui.AltUp)
case "alt-down": case "alt-down":
@@ -1024,6 +1034,16 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
add(tui.AltLeft) add(tui.AltLeft)
case "alt-right": case "alt-right":
add(tui.AltRight) add(tui.AltRight)
case "alt-home":
add(tui.AltHome)
case "alt-end":
add(tui.AltEnd)
case "alt-delete":
add(tui.AltDelete)
case "alt-page-up":
add(tui.AltPageUp)
case "alt-page-down":
add(tui.AltPageDown)
case "tab": case "tab":
add(tui.Tab) add(tui.Tab)
case "btab", "shift-tab": case "btab", "shift-tab":
@@ -1050,6 +1070,88 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
add(tui.AltShiftLeft) add(tui.AltShiftLeft)
case "alt-shift-right", "shift-alt-right": case "alt-shift-right", "shift-alt-right":
add(tui.AltShiftRight) add(tui.AltShiftRight)
case "alt-shift-home", "shift-alt-home":
add(tui.AltShiftHome)
case "alt-shift-end", "shift-alt-end":
add(tui.AltShiftEnd)
case "alt-shift-delete", "shift-alt-delete":
add(tui.AltShiftDelete)
case "alt-shift-page-up", "shift-alt-page-up":
add(tui.AltShiftPageUp)
case "alt-shift-page-down", "shift-alt-page-down":
add(tui.AltShiftPageDown)
case "ctrl-up":
add(tui.CtrlUp)
case "ctrl-down":
add(tui.CtrlDown)
case "ctrl-right":
add(tui.CtrlRight)
case "ctrl-left":
add(tui.CtrlLeft)
case "ctrl-home":
add(tui.CtrlHome)
case "ctrl-end":
add(tui.CtrlEnd)
case "ctrl-delete":
add(tui.CtrlDelete)
case "ctrl-page-up":
add(tui.CtrlPageUp)
case "ctrl-page-down":
add(tui.CtrlPageDown)
case "ctrl-alt-up", "alt-ctrl-up":
add(tui.CtrlAltUp)
case "ctrl-alt-down", "alt-ctrl-down":
add(tui.CtrlAltDown)
case "ctrl-alt-right", "alt-ctrl-right":
add(tui.CtrlAltRight)
case "ctrl-alt-left", "alt-ctrl-left":
add(tui.CtrlAltLeft)
case "ctrl-alt-home", "alt-ctrl-home":
add(tui.CtrlAltHome)
case "ctrl-alt-end", "alt-ctrl-end":
add(tui.CtrlAltEnd)
case "ctrl-alt-delete", "alt-ctrl-delete":
add(tui.CtrlAltDelete)
case "ctrl-alt-page-up", "alt-ctrl-page-up":
add(tui.CtrlAltPageUp)
case "ctrl-alt-page-down", "alt-ctrl-page-down":
add(tui.CtrlAltPageDown)
case "ctrl-shift-up", "shift-ctrl-up":
add(tui.CtrlShiftUp)
case "ctrl-shift-down", "shift-ctrl-down":
add(tui.CtrlShiftDown)
case "ctrl-shift-right", "shift-ctrl-right":
add(tui.CtrlShiftRight)
case "ctrl-shift-left", "shift-ctrl-left":
add(tui.CtrlShiftLeft)
case "ctrl-shift-home", "shift-ctrl-home":
add(tui.CtrlShiftHome)
case "ctrl-shift-end", "shift-ctrl-end":
add(tui.CtrlShiftEnd)
case "ctrl-shift-delete", "shift-ctrl-delete":
add(tui.CtrlShiftDelete)
case "ctrl-shift-page-up", "shift-ctrl-page-up":
add(tui.CtrlShiftPageUp)
case "ctrl-shift-page-down", "shift-ctrl-page-down":
add(tui.CtrlShiftPageDown)
case "ctrl-alt-shift-up":
add(tui.CtrlAltShiftUp)
case "ctrl-alt-shift-down":
add(tui.CtrlAltShiftDown)
case "ctrl-alt-shift-right":
add(tui.CtrlAltShiftRight)
case "ctrl-alt-shift-left":
add(tui.CtrlAltShiftLeft)
case "ctrl-alt-shift-home":
add(tui.CtrlAltShiftHome)
case "ctrl-alt-shift-end":
add(tui.CtrlAltShiftEnd)
case "ctrl-alt-shift-delete":
add(tui.CtrlAltShiftDelete)
case "ctrl-alt-shift-page-up":
add(tui.CtrlAltShiftPageUp)
case "ctrl-alt-shift-page-down":
add(tui.CtrlAltShiftPageDown)
case "shift-up": case "shift-up":
add(tui.ShiftUp) add(tui.ShiftUp)
case "shift-down": case "shift-down":
@@ -1058,8 +1160,16 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
add(tui.ShiftLeft) add(tui.ShiftLeft)
case "shift-right": case "shift-right":
add(tui.ShiftRight) add(tui.ShiftRight)
case "shift-home":
add(tui.ShiftHome)
case "shift-end":
add(tui.ShiftEnd)
case "shift-delete": case "shift-delete":
add(tui.ShiftDelete) add(tui.ShiftDelete)
case "shift-page-up":
add(tui.ShiftPageUp)
case "shift-page-down":
add(tui.ShiftPageDown)
case "left-click": case "left-click":
add(tui.LeftClick) add(tui.LeftClick)
case "right-click": case "right-click":
@@ -1091,7 +1201,9 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
default: default:
runes := []rune(key) runes := []rune(key)
if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) { if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) {
chords[tui.CtrlAltKey(rune(key[9]))] = key evt := tui.CtrlAltKey(rune(key[9]))
chords[evt] = key
list = append(list, evt)
} else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) { } else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
add(tui.EventType(tui.CtrlA.Int() + int(lkey[5]) - 'a')) add(tui.EventType(tui.CtrlA.Int() + int(lkey[5]) - 'a'))
} else if len(runes) == 5 && strings.HasPrefix(lkey, "alt-") { } else if len(runes) == 5 && strings.HasPrefix(lkey, "alt-") {
@@ -1104,17 +1216,21 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error
case escapedPlus: case escapedPlus:
r = '+' r = '+'
} }
chords[tui.AltKey(r)] = key evt := tui.AltKey(r)
chords[evt] = key
list = append(list, evt)
} else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' { } else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
add(tui.EventType(tui.F1.Int() + int(key[1]) - '1')) add(tui.EventType(tui.F1.Int() + int(key[1]) - '1'))
} else if len(runes) == 1 { } else if len(runes) == 1 {
chords[tui.Key(runes[0])] = key evt := tui.Key(runes[0])
chords[evt] = key
list = append(list, evt)
} else { } else {
return nil, errors.New("unsupported key: " + key) return nil, list, errors.New("unsupported key: " + key)
} }
} }
} }
return chords, nil return chords, list, nil
} }
func parseScheme(str string) (string, []criterion, error) { func parseScheme(str string) (string, []criterion, error) {
@@ -1209,7 +1325,7 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
theme = dupeTheme(tui.Dark256) theme = dupeTheme(tui.Dark256)
case "light": case "light":
theme = dupeTheme(tui.Light256) theme = dupeTheme(tui.Light256)
case "16": case "base16", "16":
theme = dupeTheme(tui.Default16) theme = dupeTheme(tui.Default16)
case "bw", "no": case "bw", "no":
theme = tui.NoColorTheme() theme = tui.NoColorTheme()
@@ -1437,7 +1553,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|bg-transform|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|bg-transform|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search)`) `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|bg-transform|transform)-(?:query|prompt|(?:border|list|preview|input|header|footer)-label|header|footer|search|nth|pointer|ghost)|bg-transform|transform|change-(?:preview-window|preview|multi)|(?:re|un|toggle-)bind|pos|put|print|search|trigger)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@@ -1549,6 +1665,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actBackwardDeleteCharEof) appendAction(actBackwardDeleteCharEof)
case "backward-word": case "backward-word":
appendAction(actBackwardWord) appendAction(actBackwardWord)
case "backward-subword":
appendAction(actBackwardSubWord)
case "clear-screen": case "clear-screen":
appendAction(actClearScreen) appendAction(actClearScreen)
case "delete-char": case "delete-char":
@@ -1569,6 +1687,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actForwardChar) appendAction(actForwardChar)
case "forward-word": case "forward-word":
appendAction(actForwardWord) appendAction(actForwardWord)
case "forward-subword":
appendAction(actForwardSubWord)
case "jump": case "jump":
appendAction(actJump) appendAction(actJump)
case "jump-accept": case "jump-accept":
@@ -1577,6 +1697,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actKillLine) appendAction(actKillLine)
case "kill-word": case "kill-word":
appendAction(actKillWord) appendAction(actKillWord)
case "kill-subword":
appendAction(actKillSubWord)
case "unix-line-discard", "line-discard": case "unix-line-discard", "line-discard":
appendAction(actUnixLineDiscard) appendAction(actUnixLineDiscard)
case "unix-word-rubout", "word-rubout": case "unix-word-rubout", "word-rubout":
@@ -1585,6 +1707,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actYank) appendAction(actYank)
case "backward-kill-word": case "backward-kill-word":
appendAction(actBackwardKillWord) appendAction(actBackwardKillWord)
case "backward-kill-subword":
appendAction(actBackwardKillSubWord)
case "toggle-down": case "toggle-down":
appendAction(actToggle, actDown) appendAction(actToggle, actDown)
case "toggle-up": case "toggle-up":
@@ -1734,7 +1858,7 @@ func parseActionList(masked string, original string, prevActions []*action, putA
} }
switch t { switch t {
case actUnbind, actRebind, actToggleBind: case actUnbind, actRebind, actToggleBind:
if _, err := parseKeyChordsImpl(actionArg, spec[0:offset]+" target required"); err != nil { if _, _, err := parseKeyChords(actionArg, spec[0:offset]+" target required"); err != nil {
return nil, err return nil, err
} }
case actChangePreviewWindow: case actChangePreviewWindow:
@@ -1779,7 +1903,7 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) error {
} else if len(keyName) == 1 && keyName[0] == escapedPlus { } else if len(keyName) == 1 && keyName[0] == escapedPlus {
key = tui.Key('+') key = tui.Key('+')
} else { } else {
keys, err := parseKeyChordsImpl(keyName, "key name required") keys, _, err := parseKeyChords(keyName, "key name required")
if err != nil { if err != nil {
return err return err
} }
@@ -1926,6 +2050,8 @@ func isExecuteAction(str string) actionType {
return actBgTransformQuery return actBgTransformQuery
case "bg-transform-search": case "bg-transform-search":
return actBgTransformSearch return actBgTransformSearch
case "trigger":
return actTrigger
case "search": case "search":
return actSearch return actSearch
} }
@@ -1933,7 +2059,7 @@ func isExecuteAction(str string) actionType {
} }
func parseToggleSort(keymap map[tui.Event][]*action, str string) error { func parseToggleSort(keymap map[tui.Event][]*action, str string) error {
keys, err := parseKeyChords(str, "key name required") keys, _, err := parseKeyChords(str, "key name required")
if err != nil { if err != nil {
return err return err
} }
@@ -2472,7 +2598,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err != nil { if err != nil {
return err return err
} }
chords, err := parseKeyChords(str, "key names required") chords, _, err := parseKeyChords(str, "key names required")
if err != nil { if err != nil {
return err return err
} }
@@ -2733,6 +2859,13 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err != nil { if err != nil {
return err return err
} }
case "--gutter":
str, err := nextString("gutter character required")
if err != nil {
return err
}
str = firstLine(str)
opts.Gutter = &str
case "--pointer": case "--pointer":
str, err := nextString("pointer sign required") str, err := nextString("pointer sign required")
if err != nil { if err != nil {
@@ -2924,7 +3057,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
return err return err
} }
case "--no-header-lines-border": case "--no-header-lines-border":
opts.HeaderLinesShape = tui.BorderNone opts.HeaderLinesShape = tui.BorderUndefined
case "--header-lines-border": case "--header-lines-border":
hasArg, arg := optionalNextString() hasArg, arg := optionalNextString()
if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg); err != nil { if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg); err != nil {
@@ -3231,22 +3364,28 @@ func applyPreset(opts *Options, preset string) error {
return nil return nil
} }
func validateSign(sign string, signOptName string) error { func validateSign(sign string, signOptName string, maxWidth int) error {
if uniseg.StringWidth(sign) > 2 { if uniseg.StringWidth(sign) > maxWidth {
return fmt.Errorf("%v display width should be up to 2", signOptName) return fmt.Errorf("%v display width should be up to %d", signOptName, maxWidth)
} }
return nil return nil
} }
func validateOptions(opts *Options) error { func validateOptions(opts *Options) error {
if opts.Pointer != nil { if opts.Pointer != nil {
if err := validateSign(*opts.Pointer, "pointer"); err != nil { if err := validateSign(*opts.Pointer, "pointer", 2); err != nil {
return err return err
} }
} }
if opts.Marker != nil { if opts.Marker != nil {
if err := validateSign(*opts.Marker, "marker"); err != nil { if err := validateSign(*opts.Marker, "marker", 2); err != nil {
return err
}
}
if opts.Gutter != nil {
if err := validateSign(*opts.Gutter, "gutter", 1); err != nil {
return err return err
} }
} }

View File

@@ -142,7 +142,7 @@ func TestIrrelevantNth(t *testing.T) {
} }
func TestParseKeys(t *testing.T) { func TestParseKeys(t *testing.T) {
pairs, _ := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g,ctrl-alt-a,ALT-enter,alt-SPACE", "") pairs, _, _ := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g,ctrl-alt-a,ALT-enter,alt-SPACE", "")
checkEvent := func(e tui.Event, s string) { checkEvent := func(e tui.Event, s string) {
if pairs[e] != s { if pairs[e] != s {
t.Errorf("%s != %s", pairs[e], s) t.Errorf("%s != %s", pairs[e], s)
@@ -168,7 +168,7 @@ func TestParseKeys(t *testing.T) {
checkEvent(tui.AltKey(' '), "alt-SPACE") checkEvent(tui.AltKey(' '), "alt-SPACE")
// Synonyms // Synonyms
pairs, _ = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "") pairs, _, _ = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
if len(pairs) != 9 { if len(pairs) != 9 {
t.Error(9) t.Error(9)
} }
@@ -182,7 +182,7 @@ func TestParseKeys(t *testing.T) {
check(tui.Left, "left") check(tui.Left, "left")
check(tui.Right, "right") check(tui.Right, "right")
pairs, _ = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "") pairs, _, _ = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "")
if len(pairs) != 11 { if len(pairs) != 11 {
t.Error(11) t.Error(11)
} }
@@ -211,40 +211,40 @@ func TestParseKeysWithComma(t *testing.T) {
} }
} }
pairs, _ := parseKeyChords(",", "") pairs, _, _ := parseKeyChords(",", "")
checkN(len(pairs), 1) checkN(len(pairs), 1)
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords(",,a,b", "") pairs, _, _ = parseKeyChords(",,a,b", "")
checkN(len(pairs), 3) checkN(len(pairs), 3)
check(pairs, tui.Key('a'), "a") check(pairs, tui.Key('a'), "a")
check(pairs, tui.Key('b'), "b") check(pairs, tui.Key('b'), "b")
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords("a,b,,", "") pairs, _, _ = parseKeyChords("a,b,,", "")
checkN(len(pairs), 3) checkN(len(pairs), 3)
check(pairs, tui.Key('a'), "a") check(pairs, tui.Key('a'), "a")
check(pairs, tui.Key('b'), "b") check(pairs, tui.Key('b'), "b")
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords("a,,,b", "") pairs, _, _ = parseKeyChords("a,,,b", "")
checkN(len(pairs), 3) checkN(len(pairs), 3)
check(pairs, tui.Key('a'), "a") check(pairs, tui.Key('a'), "a")
check(pairs, tui.Key('b'), "b") check(pairs, tui.Key('b'), "b")
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords("a,,,b,c", "") pairs, _, _ = parseKeyChords("a,,,b,c", "")
checkN(len(pairs), 4) checkN(len(pairs), 4)
check(pairs, tui.Key('a'), "a") check(pairs, tui.Key('a'), "a")
check(pairs, tui.Key('b'), "b") check(pairs, tui.Key('b'), "b")
check(pairs, tui.Key('c'), "c") check(pairs, tui.Key('c'), "c")
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords(",,,", "") pairs, _, _ = parseKeyChords(",,,", "")
checkN(len(pairs), 1) checkN(len(pairs), 1)
check(pairs, tui.Key(','), ",") check(pairs, tui.Key(','), ",")
pairs, _ = parseKeyChords(",ALT-,,", "") pairs, _, _ = parseKeyChords(",ALT-,,", "")
checkN(len(pairs), 1) checkN(len(pairs), 1)
check(pairs, tui.AltKey(','), "ALT-,") check(pairs, tui.AltKey(','), "ALT-,")
} }
@@ -462,7 +462,7 @@ func TestValidateSign(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
err := validateSign(testCase.inputSign, "") err := validateSign(testCase.inputSign, "", 2)
if testCase.isValid && err != nil { if testCase.isValid && err != nil {
t.Errorf("Input sign `%s` caused error", testCase.inputSign) t.Errorf("Input sign `%s` caused error", testCase.inputSign)
} }

View File

@@ -90,11 +90,12 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool)
} }
} }
// * Write the command to a temporary file and run it with sh to ensure POSIX compliance. // Write the command to a temporary file and run it with sh to ensure POSIX compliance.
// * Nullify FZF_DEFAULT_* variables as tmux popup may inject them even when undefined. var exports []string
exports := []string{"FZF_DEFAULT_COMMAND=", "FZF_DEFAULT_OPTS=", "FZF_DEFAULT_OPTS_FILE="}
needBash := false needBash := false
if withExports { if withExports {
// Nullify FZF_DEFAULT_* variables as tmux popup may inject them even when undefined.
exports = []string{"FZF_DEFAULT_COMMAND=", "FZF_DEFAULT_OPTS=", "FZF_DEFAULT_OPTS_FILE="}
validIdentifier := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`) validIdentifier := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
for _, pairStr := range os.Environ() { for _, pairStr := range os.Environ() {
pair := strings.SplitN(pairStr, "=", 2) pair := strings.SplitN(pairStr, "=", 2)

View File

@@ -285,7 +285,7 @@ func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bo
if strings.HasPrefix(ignore, sep) { if strings.HasPrefix(ignore, sep) {
ignoresSuffix = append(ignoresSuffix, ignore) ignoresSuffix = append(ignoresSuffix, ignore)
} else { } else {
// 'foo/bar' should match match // 'foo/bar' should match
// * 'foo/bar' // * 'foo/bar'
// * 'baz/foo/bar' // * 'baz/foo/bar'
// * but NOT 'bazfoo/bar' // * but NOT 'bazfoo/bar'

View File

@@ -128,9 +128,9 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
// No ANSI codes // No ANSI codes
if len(itemColors) == 0 && len(nthOffsets) == 0 { if len(itemColors) == 0 && len(nthOffsets) == 0 {
var offsets []colorOffset offsets := make([]colorOffset, len(matchOffsets))
for _, off := range matchOffsets { for i, off := range matchOffsets {
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch, match: true}) offsets[i] = colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch, match: true}
} }
return offsets return offsets
} }
@@ -237,7 +237,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
if color.Fg().IsDefault() && origColor.HasBg() { if color.Fg().IsDefault() && origColor.HasBg() {
color = origColor color = origColor
if curr.nth { if curr.nth {
color = color.WithAttr(attrNth) color = color.WithAttr(attrNth &^ tui.AttrRegular)
} }
} else { } else {
color = origColor.MergeNonDefault(color) color = origColor.MergeNonDefault(color)

View File

@@ -67,7 +67,7 @@ const maxFocusEvents = 10000
const blockDuration = 1 * time.Second const blockDuration = 1 * time.Second
func init() { func init() {
placeholder = regexp.MustCompile(`\\?(?:{[+*sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`) placeholder = regexp.MustCompile(`\\?(?:{[+*sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{[+*]?f?nf?})`)
whiteSuffix = regexp.MustCompile(`\s*$`) whiteSuffix = regexp.MustCompile(`\s*$`)
offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`) offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`) offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
@@ -272,6 +272,7 @@ type Terminal struct {
footerLabel labelPrinter footerLabel labelPrinter
footerLabelLen int footerLabelLen int
footerLabelOpts labelOpts footerLabelOpts labelOpts
gutterReverse bool
pointer string pointer string
pointerLen int pointerLen int
pointerEmpty string pointerEmpty string
@@ -291,6 +292,8 @@ type Terminal struct {
gapLineLen int gapLineLen int
wordRubout string wordRubout string
wordNext string wordNext string
subWordRubout string
subWordNext string
cx int cx int
cy int cy int
offset int offset int
@@ -422,6 +425,8 @@ type Terminal struct {
forcePreview bool forcePreview bool
clickHeaderLine int clickHeaderLine int
clickHeaderColumn int clickHeaderColumn int
clickFooterLine int
clickFooterColumn int
proxyScript string proxyScript string
numLinesCache map[int32]numLinesCacheValue numLinesCache map[int32]numLinesCacheValue
} }
@@ -510,6 +515,7 @@ const (
actBackwardDeleteChar actBackwardDeleteChar
actBackwardDeleteCharEof actBackwardDeleteCharEof
actBackwardWord actBackwardWord
actBackwardSubWord
actCancel actCancel
actChangeBorderLabel actChangeBorderLabel
@@ -539,12 +545,15 @@ const (
actFatal actFatal
actForwardChar actForwardChar
actForwardWord actForwardWord
actForwardSubWord
actKillLine actKillLine
actKillWord actKillWord
actKillSubWord
actUnixLineDiscard actUnixLineDiscard
actUnixWordRubout actUnixWordRubout
actYank actYank
actBackwardKillWord actBackwardKillWord
actBackwardKillSubWord
actSelectAll actSelectAll
actDeselectAll actDeselectAll
actToggle actToggle
@@ -602,6 +611,8 @@ const (
actTransformQuery actTransformQuery
actTransformSearch actTransformSearch
actTrigger
actBgTransform actBgTransform
actBgTransformBorderLabel actBgTransformBorderLabel
actBgTransformGhost actBgTransformGhost
@@ -778,6 +789,7 @@ func defaultKeymap() map[tui.Event][]*action {
add(tui.CtrlF, actForwardChar) add(tui.CtrlF, actForwardChar)
add(tui.CtrlH, actBackwardDeleteChar) add(tui.CtrlH, actBackwardDeleteChar)
add(tui.Backspace, actBackwardDeleteChar) add(tui.Backspace, actBackwardDeleteChar)
add(tui.CtrlBackspace, actBackwardDeleteChar)
add(tui.Tab, actToggleDown) add(tui.Tab, actToggleDown)
add(tui.ShiftTab, actToggleUp) add(tui.ShiftTab, actToggleUp)
add(tui.CtrlJ, actDown) add(tui.CtrlJ, actDown)
@@ -932,6 +944,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
} }
wordRubout := "[^\\pL\\pN][\\pL\\pN]" wordRubout := "[^\\pL\\pN][\\pL\\pN]"
wordNext := "[\\pL\\pN][^\\pL\\pN]|(.$)" wordNext := "[\\pL\\pN][^\\pL\\pN]|(.$)"
subWordRubout := "[a-z][A-Z]|[^\\pL\\pN][\\pL\\pN]"
subWordNext := "[a-z][A-Z]|[\\pL\\pN][^\\pL\\pN]|(.$)"
if opts.FileWord { if opts.FileWord {
sep := regexp.QuoteMeta(string(os.PathSeparator)) sep := regexp.QuoteMeta(string(os.PathSeparator))
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep) wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
@@ -965,6 +979,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
markerMultiLine: *opts.MarkerMulti, markerMultiLine: *opts.MarkerMulti,
wordRubout: wordRubout, wordRubout: wordRubout,
wordNext: wordNext, wordNext: wordNext,
subWordRubout: subWordRubout,
subWordNext: subWordNext,
cx: len(input), cx: len(input),
cy: 0, cy: 0,
offset: 0, offset: 0,
@@ -1079,10 +1095,27 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
// This should be called before accessing tui.Color* // This should be called before accessing tui.Color*
tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible()) tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible())
// Gutter character
var gutterChar string
if opts.Gutter != nil {
gutterChar = *opts.Gutter
} else if t.unicode && !t.theme.Gutter.Color.IsDefault() {
gutterChar = "▌"
} else {
gutterChar = " "
t.gutterReverse = true
}
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt) t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
// Pre-calculated empty pointer and marker signs // Pre-calculated empty pointer and marker signs
t.pointerEmpty = strings.Repeat(" ", t.pointerLen) if t.pointerLen == 0 {
t.pointerEmpty = ""
} else {
t.pointerEmpty = gutterChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
}
t.markerEmpty = strings.Repeat(" ", t.markerLen) t.markerEmpty = strings.Repeat(" ", t.markerLen)
// Labels
t.listLabel, t.listLabelLen = t.ansiLabelPrinter(opts.ListLabel.label, &tui.ColListLabel, false) t.listLabel, t.listLabelLen = t.ansiLabelPrinter(opts.ListLabel.label, &tui.ColListLabel, false)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false) t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false) t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
@@ -1259,7 +1292,10 @@ func (t *Terminal) environImpl(forPreview bool) []string {
env = append(env, fmt.Sprintf("FZF_POS=%d", util.Min(t.merger.Length(), t.cy+1))) env = append(env, fmt.Sprintf("FZF_POS=%d", util.Min(t.merger.Length(), t.cy+1)))
env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_LINE=%d", t.clickHeaderLine)) env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_LINE=%d", t.clickHeaderLine))
env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_COLUMN=%d", t.clickHeaderColumn)) env = append(env, fmt.Sprintf("FZF_CLICK_HEADER_COLUMN=%d", t.clickHeaderColumn))
env = append(env, fmt.Sprintf("FZF_CLICK_FOOTER_LINE=%d", t.clickFooterLine))
env = append(env, fmt.Sprintf("FZF_CLICK_FOOTER_COLUMN=%d", t.clickFooterColumn))
env = t.addClickHeaderWord(env) env = t.addClickHeaderWord(env)
env = t.addClickFooterWord(env)
// Add preview environment variables if preview is enabled // Add preview environment variables if preview is enabled
pwindowSize := t.pwindowSize() pwindowSize := t.pwindowSize()
@@ -1387,7 +1423,7 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
if !fill { if !fill {
ellipsis, ellipsisWidth = util.Truncate(t.ellipsis, limit) ellipsis, ellipsisWidth = util.Truncate(t.ellipsis, limit)
} }
if length > limit-ellipsisWidth { if length > limit {
trimmedRunes, _ := t.trimRight(runes, limit-ellipsisWidth) trimmedRunes, _ := t.trimRight(runes, limit-ellipsisWidth)
window.CPrint(*color, string(trimmedRunes)+string(ellipsis)) window.CPrint(*color, string(trimmedRunes)+string(ellipsis))
} else if fill { } else if fill {
@@ -1634,6 +1670,8 @@ func (t *Terminal) changeFooter(footer string) {
lines = strings.Split(strings.TrimSuffix(footer, "\n"), "\n") lines = strings.Split(strings.TrimSuffix(footer, "\n"), "\n")
} }
t.footer = lines t.footer = lines
t.clickFooterLine = 0
t.clickFooterColumn = 0
} }
// UpdateHeader updates the header // UpdateHeader updates the header
@@ -1792,6 +1830,11 @@ func (t *Terminal) displayWidth(runes []rune) int {
return width return width
} }
func (t *Terminal) displayWidthWithPrefix(str string, prefixWidth int) int {
width, _ := util.RunesWidth([]rune(str), prefixWidth, t.tabstop, math.MaxInt32)
return width
}
const ( const (
minWidth = 4 minWidth = 4
minHeight = 3 minHeight = 3
@@ -2284,13 +2327,20 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
innerMarginInt[0]+shift, innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight-shrink, tui.WindowList, noBorder, true) innerMarginInt[0]+shift, innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight-shrink, tui.WindowList, noBorder, true)
// Clear characters on the margin // Clear characters on the margin
// fzf --bind 'space:preview(seq 100)' --preview-window left,1 // fzf --bind 'space:toggle-preview' --preview ':' --preview-window left,1
if !hasListBorder { if !hasListBorder {
for y := 0; y < innerHeight; y++ { for y := 0; y < innerHeight; y++ {
t.window.Move(y, -1) t.window.Move(y, -1)
t.window.Print(" ") t.window.Print(" ")
} }
} }
// fzf --bind 'space:toggle-preview' --preview ':' --preview-window left,1,border-none
if !previewOpts.Border().HasRight() {
for y := 0; y < innerHeight; y++ {
t.window.Move(y, -2)
t.window.Print(" ")
}
}
innerBorderFn(marginInt[0], marginInt[3]+pwidth, width-pwidth, height) innerBorderFn(marginInt[0], marginInt[3]+pwidth, width-pwidth, height)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height) createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
@@ -2596,11 +2646,11 @@ func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
} }
maxWidth := util.Max(1, w.Width()-t.promptLen-1) maxWidth := util.Max(1, w.Width()-t.promptLen-1)
_, overflow := t.trimLeft(t.input[:t.cx], maxWidth) _, overflow := t.trimLeft(t.input[:t.cx], maxWidth, 0)
minOffset := int(overflow) minOffset := int(overflow)
maxOffset := minOffset + (maxWidth-util.Max(0, maxWidth-t.cx))/2 maxOffset := minOffset + (maxWidth-util.Max(0, maxWidth-t.cx))/2
t.xoffset = util.Constrain(t.xoffset, minOffset, maxOffset) t.xoffset = util.Constrain(t.xoffset, minOffset, maxOffset)
before, _ := t.trimLeft(t.input[t.xoffset:t.cx], maxWidth) before, _ := t.trimLeft(t.input[t.xoffset:t.cx], maxWidth, 0)
beforeLen := t.displayWidth(before) beforeLen := t.displayWidth(before)
after, _ := t.trimRight(t.input[t.cx:], maxWidth-beforeLen) after, _ := t.trimRight(t.input[t.cx:], maxWidth-beforeLen)
afterLen := t.displayWidth(after) afterLen := t.displayWidth(after)
@@ -3064,9 +3114,21 @@ func (t *Terminal) renderEmptyLine(line int, barRange [2]int) {
t.renderBar(line, barRange) t.renderBar(line, barRange)
} }
func (t *Terminal) gutter(current bool) {
var color tui.ColorPair
if current {
color = tui.ColCurrentCursorEmpty
} else if t.gutterReverse {
color = tui.ColCursorEmpty
} else {
color = tui.ColCursorEmptyChar
}
t.window.CPrint(color, t.pointerEmpty)
}
func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) { func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) {
t.move(line, 0, false) t.move(line, 0, false)
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty) t.gutter(false)
t.window.Print(t.markerEmpty) t.window.Print(t.markerEmpty)
x := t.pointerLen + t.markerLen x := t.pointerLen + t.markerLen
@@ -3230,7 +3292,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
return indentSize return indentSize
} }
if len(label) == 0 { if len(label) == 0 {
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty) t.gutter(true)
} else { } else {
t.window.CPrint(tui.ColCurrentCursor, label) t.window.CPrint(tui.ColCurrentCursor, label)
} }
@@ -3252,7 +3314,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
return indentSize return indentSize
} }
if len(label) == 0 { if len(label) == 0 {
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty) t.gutter(false)
} else { } else {
t.window.CPrint(tui.ColCursor, label) t.window.CPrint(tui.ColCursor, label)
} }
@@ -3301,22 +3363,22 @@ func (t *Terminal) displayWidthWithLimit(runes []rune, prefixWidth int, limit in
return width return width
} }
func (t *Terminal) trimLeft(runes []rune, width int) ([]rune, int32) { func (t *Terminal) trimLeft(runes []rune, width int, ellipsisWidth int) ([]rune, int32) {
width = util.Max(0, width) width = util.Max(0, width)
var trimmed int32 var trimmed int32
// Assume that each rune takes at least one column on screen // Assume that each rune takes at least one column on screen
if len(runes) > width+2 { if len(runes) > width {
diff := len(runes) - width - 2 diff := len(runes) - width
trimmed = int32(diff) trimmed = int32(diff)
runes = runes[diff:] runes = runes[diff:]
} }
currentWidth := t.displayWidth(runes) currentWidth := t.displayWidth(runes)
for currentWidth > width && len(runes) > 0 { for currentWidth > width-ellipsisWidth && len(runes) > 0 {
runes = runes[1:] runes = runes[1:]
trimmed++ trimmed++
currentWidth = t.displayWidthWithLimit(runes, 2, width) currentWidth = t.displayWidthWithLimit(runes, ellipsisWidth, width)
} }
return runes, trimmed return runes, trimmed
} }
@@ -3541,7 +3603,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
} }
if t.hscroll { if t.hscroll {
if t.keepRight && pos == nil { if t.keepRight && pos == nil {
trimmed, diff := t.trimLeft(line, maxWidth-ellipsisWidth) trimmed, diff := t.trimLeft(line, maxWidth, ellipsisWidth)
transformOffsets(diff, false) transformOffsets(diff, false)
line = append(ellipsis, trimmed...) line = append(ellipsis, trimmed...)
} else if !t.overflow(line[:maxe], maxWidth-ellipsisWidth) { } else if !t.overflow(line[:maxe], maxWidth-ellipsisWidth) {
@@ -3557,7 +3619,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
} }
// ..ri.. // ..ri..
var diff int32 var diff int32
line, diff = t.trimLeft(line, maxWidth-ellipsisWidth) line, diff = t.trimLeft(line, maxWidth, ellipsisWidth)
// Transform offsets // Transform offsets
transformOffsets(diff, rightTrim) transformOffsets(diff, rightTrim)
@@ -3598,7 +3660,7 @@ func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []
for _, offset := range offsets { for _, offset := range offsets {
b := util.Constrain32(offset.offset[0], index, maxOffset) b := util.Constrain32(offset.offset[0], index, maxOffset)
e := util.Constrain32(offset.offset[1], index, maxOffset) e := util.Constrain32(offset.offset[1], index, maxOffset)
if url != nil && offset.url == nil { if url != nil && offset.url != url {
url = nil url = nil
window.LinkEnd() window.LinkEnd()
} }
@@ -4751,6 +4813,7 @@ func (t *Terminal) addClickHeaderWord(env []string) []string {
if t.layout == layoutReverse { if t.layout == layoutReverse {
headers[0], headers[1] = headers[1], headers[0] headers[0], headers[1] = headers[1], headers[0]
} }
var trimmedLine string
var words []Token var words []Token
var lineNum int var lineNum int
for lineNum = 0; lineNum <= clickHeaderLine; lineNum++ { for lineNum = 0; lineNum <= clickHeaderLine; lineNum++ {
@@ -4769,7 +4832,9 @@ func (t *Terminal) addClickHeaderWord(env []string) []string {
return env return env
} }
words = Tokenize(line, t.delimiter) // NOTE: We can't expand tabs here because the delimiter can contain tabs.
trimmedLine, _, _ = extractColor(line, nil, nil)
words = Tokenize(trimmedLine, t.delimiter)
if currentLine { if currentLine {
break break
} else { } else {
@@ -4780,11 +4845,14 @@ func (t *Terminal) addClickHeaderWord(env []string) []string {
} }
colNum := t.clickHeaderColumn - 1 colNum := t.clickHeaderColumn - 1
prefixWidth, prefixLength := 0, 0
for idx, token := range words { for idx, token := range words {
prefixWidth := int(token.prefixLength) prefixWidth += t.displayWidthWithPrefix(trimmedLine[prefixLength:token.prefixLength], prefixWidth)
word := token.text.ToString() prefixLength = int(token.prefixLength)
word, _ := t.processTabs(token.text.ToRunes(), prefixWidth)
trimmed := strings.TrimRightFunc(word, unicode.IsSpace) trimmed := strings.TrimRightFunc(word, unicode.IsSpace)
trimWidth, _ := util.RunesWidth([]rune(trimmed), prefixWidth, t.tabstop, math.MaxInt32) trimWidth := t.displayWidthWithPrefix(trimmed, prefixWidth)
// Find the position of the first non-space character in the word // Find the position of the first non-space character in the word
minPos := strings.IndexFunc(trimmed, func(r rune) bool { minPos := strings.IndexFunc(trimmed, func(r rune) bool {
@@ -4803,6 +4871,37 @@ func (t *Terminal) addClickHeaderWord(env []string) []string {
return env return env
} }
func (t *Terminal) addClickFooterWord(env []string) []string {
clickFooterLine := t.clickFooterLine - 1
if clickFooterLine < 0 || clickFooterLine >= len(t.footer) {
// Never clicked on the footer
return env
}
// NOTE: Unlike in click-header, we don't use --delimiter here, since we're
// only interested in the word, not nth. Does this make sense?
trimmed, _, _ := extractColor(t.footer[clickFooterLine], nil, nil)
trimmed, _ = t.processTabs([]rune(trimmed), 0)
words := Tokenize(trimmed, Delimiter{})
colNum := t.clickFooterColumn - 1
for _, token := range words {
prefixWidth := int(token.prefixLength)
word := token.text.ToString()
trimmed := strings.TrimRightFunc(word, unicode.IsSpace)
trimWidth := t.displayWidthWithPrefix(trimmed, prefixWidth)
// Find the position of the first non-space character in the word
minPos := strings.IndexFunc(trimmed, func(r rune) bool {
return !unicode.IsSpace(r)
})
if colNum >= minPos && colNum >= prefixWidth && colNum < prefixWidth+trimWidth {
env = append(env, fmt.Sprintf("FZF_CLICK_FOOTER_WORD=%s", trimmed))
return env
}
}
return env
}
// Loop is called to start Terminal I/O // Loop is called to start Terminal I/O
func (t *Terminal) Loop() error { func (t *Terminal) Loop() error {
// prof := profile.Start(profile.ProfilePath("/tmp/")) // prof := profile.Start(profile.ProfilePath("/tmp/"))
@@ -5400,6 +5499,7 @@ func (t *Terminal) Loop() error {
return nil return nil
} }
} }
triggering := map[tui.Event]struct{}{}
previousInput := t.input previousInput := t.input
previousCx := t.cx previousCx := t.cx
previousVersion := t.version previousVersion := t.version
@@ -5667,7 +5767,7 @@ func (t *Terminal) Loop() error {
capture(true, func(expr string) { capture(true, func(expr string) {
// Split nth expression // Split nth expression
tokens := strings.Split(expr, "|") tokens := strings.Split(expr, "|")
if nth, err := splitNth(tokens[0]); err == nil { if nth, err := splitNth(tokens[0]); err == nil || len(expr) == 0 {
// Changed // Changed
newNth = &nth newNth = &nth
} else { } else {
@@ -5961,6 +6061,11 @@ func (t *Terminal) Loop() error {
if t.cx > 0 { if t.cx > 0 {
t.rubout(t.wordRubout) t.rubout(t.wordRubout)
} }
case actBackwardKillSubWord:
beof = len(t.input) == 0
if t.cx > 0 {
t.rubout(t.subWordRubout)
}
case actYank: case actYank:
suffix := copySlice(t.input[t.cx:]) suffix := copySlice(t.input[t.cx:])
t.input = append(append(t.input[:t.cx], t.yanked...), suffix...) t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
@@ -6072,6 +6177,10 @@ func (t *Terminal) Loop() error {
t.cx = findLastMatch(t.wordRubout, string(t.input[:t.cx])) + 1 t.cx = findLastMatch(t.wordRubout, string(t.input[:t.cx])) + 1
case actForwardWord: case actForwardWord:
t.cx += findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1 t.cx += findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1
case actBackwardSubWord:
t.cx = findLastMatch(t.subWordRubout, string(t.input[:t.cx])) + 1
case actForwardSubWord:
t.cx += findFirstMatch(t.subWordNext, string(t.input[t.cx:])) + 1
case actKillWord: case actKillWord:
ncx := t.cx + ncx := t.cx +
findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1 findFirstMatch(t.wordNext, string(t.input[t.cx:])) + 1
@@ -6079,6 +6188,13 @@ func (t *Terminal) Loop() error {
t.yanked = copySlice(t.input[t.cx:ncx]) t.yanked = copySlice(t.input[t.cx:ncx])
t.input = append(t.input[:t.cx], t.input[ncx:]...) t.input = append(t.input[:t.cx], t.input[ncx:]...)
} }
case actKillSubWord:
ncx := t.cx +
findFirstMatch(t.subWordNext, string(t.input[t.cx:])) + 1
if ncx > t.cx {
t.yanked = copySlice(t.input[t.cx:ncx])
t.input = append(t.input[:t.cx], t.input[ncx:]...)
}
case actKillLine: case actKillLine:
if t.cx < len(t.input) { if t.cx < len(t.input) {
t.yanked = copySlice(t.input[t.cx:]) t.yanked = copySlice(t.input[t.cx:])
@@ -6191,6 +6307,20 @@ func (t *Terminal) Loop() error {
case actDisableSearch: case actDisableSearch:
t.paused = true t.paused = true
req(reqPrompt) req(reqPrompt)
case actTrigger:
if _, chords, err := parseKeyChords(a.a, ""); err == nil {
for _, chord := range chords {
if _, prs := triggering[chord]; prs {
// Avoid recursive triggering
continue
}
if acts, prs := t.keymap[chord]; prs {
triggering[chord] = struct{}{}
doActions(acts)
delete(triggering, chord)
}
}
}
case actSigStop: case actSigStop:
p, err := os.FindProcess(os.Getpid()) p, err := os.FindProcess(os.Getpid())
if err == nil { if err == nil {
@@ -6380,6 +6510,18 @@ func (t *Terminal) Loop() error {
return doActions(actionsFor(tui.ClickHeader)) return doActions(actionsFor(tui.ClickHeader))
} }
// Inside the footer window
if clicked && t.footerWindow != nil && t.footerWindow.Enclose(my, mx) {
mx -= t.footerWindow.Left() + t.headerIndent(t.footerBorderShape)
my -= t.footerWindow.Top()
if mx < 0 {
break
}
t.clickFooterLine = my + 1
t.clickFooterColumn = mx + 1
return doActions(actionsFor(tui.ClickFooter))
}
// Ignored // Ignored
if !t.window.Enclose(my, mx) && !barDragging { if !t.window.Enclose(my, mx) && !barDragging {
break break
@@ -6500,13 +6642,13 @@ func (t *Terminal) Loop() error {
t.reading = true t.reading = true
} }
case actUnbind: case actUnbind:
if keys, err := parseKeyChords(a.a, "PANIC"); err == nil { if keys, _, err := parseKeyChords(a.a, "PANIC"); err == nil {
for key := range keys { for key := range keys {
delete(t.keymap, key) delete(t.keymap, key)
} }
} }
case actRebind: case actRebind:
if keys, err := parseKeyChords(a.a, "PANIC"); err == nil { if keys, _, err := parseKeyChords(a.a, "PANIC"); err == nil {
for key := range keys { for key := range keys {
if originalAction, found := t.keymapOrg[key]; found { if originalAction, found := t.keymapOrg[key]; found {
t.keymap[key] = originalAction t.keymap[key] = originalAction
@@ -6514,7 +6656,7 @@ func (t *Terminal) Loop() error {
} }
} }
case actToggleBind: case actToggleBind:
if keys, err := parseKeyChords(a.a, "PANIC"); err == nil { if keys, _, err := parseKeyChords(a.a, "PANIC"); err == nil {
for key := range keys { for key := range keys {
if _, bound := t.keymap[key]; bound { if _, bound := t.keymap[key]; bound {
delete(t.keymap, key) delete(t.keymap, key)

View File

@@ -37,85 +37,137 @@ func _() {
_ = x[CtrlZ-26] _ = x[CtrlZ-26]
_ = x[Esc-27] _ = x[Esc-27]
_ = x[CtrlSpace-28] _ = x[CtrlSpace-28]
_ = x[CtrlDelete-29] _ = x[CtrlBackSlash-29]
_ = x[CtrlBackSlash-30] _ = x[CtrlRightBracket-30]
_ = x[CtrlRightBracket-31] _ = x[CtrlCaret-31]
_ = x[CtrlCaret-32] _ = x[CtrlSlash-32]
_ = x[CtrlSlash-33] _ = x[ShiftTab-33]
_ = x[ShiftTab-34] _ = x[Backspace-34]
_ = x[Backspace-35] _ = x[Delete-35]
_ = x[Delete-36] _ = x[PageUp-36]
_ = x[PageUp-37] _ = x[PageDown-37]
_ = x[PageDown-38] _ = x[Up-38]
_ = x[Up-39] _ = x[Down-39]
_ = x[Down-40] _ = x[Left-40]
_ = x[Left-41] _ = x[Right-41]
_ = x[Right-42] _ = x[Home-42]
_ = x[Home-43] _ = x[End-43]
_ = x[End-44] _ = x[Insert-44]
_ = x[Insert-45] _ = x[ShiftUp-45]
_ = x[ShiftUp-46] _ = x[ShiftDown-46]
_ = x[ShiftDown-47] _ = x[ShiftLeft-47]
_ = x[ShiftLeft-48] _ = x[ShiftRight-48]
_ = x[ShiftRight-49] _ = x[ShiftDelete-49]
_ = x[ShiftDelete-50] _ = x[ShiftHome-50]
_ = x[F1-51] _ = x[ShiftEnd-51]
_ = x[F2-52] _ = x[ShiftPageUp-52]
_ = x[F3-53] _ = x[ShiftPageDown-53]
_ = x[F4-54] _ = x[F1-54]
_ = x[F5-55] _ = x[F2-55]
_ = x[F6-56] _ = x[F3-56]
_ = x[F7-57] _ = x[F4-57]
_ = x[F8-58] _ = x[F5-58]
_ = x[F9-59] _ = x[F6-59]
_ = x[F10-60] _ = x[F7-60]
_ = x[F11-61] _ = x[F8-61]
_ = x[F12-62] _ = x[F9-62]
_ = x[AltBackspace-63] _ = x[F10-63]
_ = x[AltUp-64] _ = x[F11-64]
_ = x[AltDown-65] _ = x[F12-65]
_ = x[AltLeft-66] _ = x[AltBackspace-66]
_ = x[AltRight-67] _ = x[AltUp-67]
_ = x[AltShiftUp-68] _ = x[AltDown-68]
_ = x[AltShiftDown-69] _ = x[AltLeft-69]
_ = x[AltShiftLeft-70] _ = x[AltRight-70]
_ = x[AltShiftRight-71] _ = x[AltDelete-71]
_ = x[Alt-72] _ = x[AltHome-72]
_ = x[CtrlAlt-73] _ = x[AltEnd-73]
_ = x[Invalid-74] _ = x[AltPageUp-74]
_ = x[Fatal-75] _ = x[AltPageDown-75]
_ = x[BracketedPasteBegin-76] _ = x[AltShiftUp-76]
_ = x[BracketedPasteEnd-77] _ = x[AltShiftDown-77]
_ = x[Mouse-78] _ = x[AltShiftLeft-78]
_ = x[DoubleClick-79] _ = x[AltShiftRight-79]
_ = x[LeftClick-80] _ = x[AltShiftDelete-80]
_ = x[RightClick-81] _ = x[AltShiftHome-81]
_ = x[SLeftClick-82] _ = x[AltShiftEnd-82]
_ = x[SRightClick-83] _ = x[AltShiftPageUp-83]
_ = x[ScrollUp-84] _ = x[AltShiftPageDown-84]
_ = x[ScrollDown-85] _ = x[CtrlUp-85]
_ = x[SScrollUp-86] _ = x[CtrlDown-86]
_ = x[SScrollDown-87] _ = x[CtrlLeft-87]
_ = x[PreviewScrollUp-88] _ = x[CtrlRight-88]
_ = x[PreviewScrollDown-89] _ = x[CtrlHome-89]
_ = x[Resize-90] _ = x[CtrlEnd-90]
_ = x[Change-91] _ = x[CtrlBackspace-91]
_ = x[BackwardEOF-92] _ = x[CtrlDelete-92]
_ = x[Start-93] _ = x[CtrlPageUp-93]
_ = x[Load-94] _ = x[CtrlPageDown-94]
_ = x[Focus-95] _ = x[Alt-95]
_ = x[One-96] _ = x[CtrlAlt-96]
_ = x[Zero-97] _ = x[CtrlAltUp-97]
_ = x[Result-98] _ = x[CtrlAltDown-98]
_ = x[Jump-99] _ = x[CtrlAltLeft-99]
_ = x[JumpCancel-100] _ = x[CtrlAltRight-100]
_ = x[ClickHeader-101] _ = x[CtrlAltHome-101]
_ = x[Multi-102] _ = x[CtrlAltEnd-102]
_ = x[CtrlAltBackspace-103]
_ = x[CtrlAltDelete-104]
_ = x[CtrlAltPageUp-105]
_ = x[CtrlAltPageDown-106]
_ = x[CtrlShiftUp-107]
_ = x[CtrlShiftDown-108]
_ = x[CtrlShiftLeft-109]
_ = x[CtrlShiftRight-110]
_ = x[CtrlShiftHome-111]
_ = x[CtrlShiftEnd-112]
_ = x[CtrlShiftDelete-113]
_ = x[CtrlShiftPageUp-114]
_ = x[CtrlShiftPageDown-115]
_ = x[CtrlAltShiftUp-116]
_ = x[CtrlAltShiftDown-117]
_ = x[CtrlAltShiftLeft-118]
_ = x[CtrlAltShiftRight-119]
_ = x[CtrlAltShiftHome-120]
_ = x[CtrlAltShiftEnd-121]
_ = x[CtrlAltShiftDelete-122]
_ = x[CtrlAltShiftPageUp-123]
_ = x[CtrlAltShiftPageDown-124]
_ = x[Invalid-125]
_ = x[Fatal-126]
_ = x[BracketedPasteBegin-127]
_ = x[BracketedPasteEnd-128]
_ = x[Mouse-129]
_ = x[DoubleClick-130]
_ = x[LeftClick-131]
_ = x[RightClick-132]
_ = x[SLeftClick-133]
_ = x[SRightClick-134]
_ = x[ScrollUp-135]
_ = x[ScrollDown-136]
_ = x[SScrollUp-137]
_ = x[SScrollDown-138]
_ = x[PreviewScrollUp-139]
_ = x[PreviewScrollDown-140]
_ = x[Resize-141]
_ = x[Change-142]
_ = x[BackwardEOF-143]
_ = x[Start-144]
_ = x[Load-145]
_ = x[Focus-146]
_ = x[One-147]
_ = x[Zero-148]
_ = x[Result-149]
_ = x[Jump-150]
_ = x[JumpCancel-151]
_ = x[ClickHeader-152]
_ = x[ClickFooter-153]
_ = x[Multi-154]
} }
const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderMulti" const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteShiftHomeShiftEndShiftPageUpShiftPageDownF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltDeleteAltHomeAltEndAltPageUpAltPageDownAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltShiftDeleteAltShiftHomeAltShiftEndAltShiftPageUpAltShiftPageDownCtrlUpCtrlDownCtrlLeftCtrlRightCtrlHomeCtrlEndCtrlBackspaceCtrlDeleteCtrlPageUpCtrlPageDownAltCtrlAltCtrlAltUpCtrlAltDownCtrlAltLeftCtrlAltRightCtrlAltHomeCtrlAltEndCtrlAltBackspaceCtrlAltDeleteCtrlAltPageUpCtrlAltPageDownCtrlShiftUpCtrlShiftDownCtrlShiftLeftCtrlShiftRightCtrlShiftHomeCtrlShiftEndCtrlShiftDeleteCtrlShiftPageUpCtrlShiftPageDownCtrlAltShiftUpCtrlAltShiftDownCtrlAltShiftLeftCtrlAltShiftRightCtrlAltShiftHomeCtrlAltShiftEndCtrlAltShiftDeleteCtrlAltShiftPageUpCtrlAltShiftPageDownInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderClickFooterMulti"
var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 466, 483, 488, 499, 508, 518, 528, 539, 547, 557, 566, 577, 592, 609, 615, 621, 632, 637, 641, 646, 649, 653, 659, 663, 673, 684, 689} var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 157, 173, 182, 191, 199, 208, 214, 220, 228, 230, 234, 238, 243, 247, 250, 256, 263, 272, 281, 291, 302, 311, 319, 330, 343, 345, 347, 349, 351, 353, 355, 357, 359, 361, 364, 367, 370, 382, 387, 394, 401, 409, 418, 425, 431, 440, 451, 461, 473, 485, 498, 512, 524, 535, 549, 565, 571, 579, 587, 596, 604, 611, 624, 634, 644, 656, 659, 666, 675, 686, 697, 709, 720, 730, 746, 759, 772, 787, 798, 811, 824, 838, 851, 863, 878, 893, 910, 924, 940, 956, 973, 989, 1004, 1022, 1040, 1060, 1067, 1072, 1091, 1108, 1113, 1124, 1133, 1143, 1153, 1164, 1172, 1182, 1191, 1202, 1217, 1234, 1240, 1246, 1257, 1262, 1266, 1271, 1274, 1278, 1284, 1288, 1298, 1309, 1320, 1325}
func (i EventType) String() string { func (i EventType) String() string {
if i < 0 || i >= EventType(len(_EventType_index)-1) { if i < 0 || i >= EventType(len(_EventType_index)-1) {

View File

@@ -335,6 +335,8 @@ func (r *LightRenderer) GetChar() Event {
return Event{CtrlQ, 0, nil} return Event{CtrlQ, 0, nil}
case 127: case 127:
return Event{Backspace, 0, nil} return Event{Backspace, 0, nil}
case 8:
return Event{CtrlBackspace, 0, nil}
case 0: case 0:
return Event{CtrlSpace, 0, nil} return Event{CtrlSpace, 0, nil}
case 28: case 28:
@@ -381,6 +383,9 @@ func (r *LightRenderer) escSequence(sz *int) Event {
} }
*sz = 2 *sz = 2
if r.buffer[1] == 8 {
return Event{CtrlAltBackspace, 0, nil}
}
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 { if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
return CtrlAltKey(rune(r.buffer[1] + 'a' - 1)) return CtrlAltKey(rune(r.buffer[1] + 'a' - 1))
} }
@@ -473,22 +478,136 @@ func (r *LightRenderer) escSequence(sz *int) Event {
if r.buffer[3] == '~' { if r.buffer[3] == '~' {
return Event{Delete, 0, nil} return Event{Delete, 0, nil}
} }
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
switch r.buffer[5] {
case '0':
return Event{AltShiftDelete, 0, nil}
case '1':
return Event{AltDelete, 0, nil}
case '2':
return Event{AltShiftDelete, 0, nil}
case '3':
return Event{CtrlAltDelete, 0, nil}
case '4':
return Event{CtrlAltShiftDelete, 0, nil}
case '5':
return Event{CtrlAltDelete, 0, nil}
case '6':
return Event{CtrlAltShiftDelete, 0, nil}
}
}
if len(r.buffer) == 6 && r.buffer[5] == '~' { if len(r.buffer) == 6 && r.buffer[5] == '~' {
*sz = 6 *sz = 6
switch r.buffer[4] { switch r.buffer[4] {
case '5':
return Event{CtrlDelete, 0, nil}
case '2': case '2':
return Event{ShiftDelete, 0, nil} return Event{ShiftDelete, 0, nil}
case '3':
return Event{AltDelete, 0, nil}
case '4':
return Event{AltShiftDelete, 0, nil}
case '5':
return Event{CtrlDelete, 0, nil}
case '6':
return Event{CtrlShiftDelete, 0, nil}
case '7':
return Event{CtrlAltDelete, 0, nil}
case '8':
return Event{CtrlAltShiftDelete, 0, nil}
case '9':
return Event{AltDelete, 0, nil}
} }
} }
return Event{Invalid, 0, nil} return Event{Invalid, 0, nil}
case '4': case '4':
return Event{End, 0, nil} return Event{End, 0, nil}
case '5': case '5':
return Event{PageUp, 0, nil} if r.buffer[3] == '~' {
return Event{PageUp, 0, nil}
}
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
switch r.buffer[5] {
case '0':
return Event{AltShiftPageUp, 0, nil}
case '1':
return Event{AltPageUp, 0, nil}
case '2':
return Event{AltShiftPageUp, 0, nil}
case '3':
return Event{CtrlAltPageUp, 0, nil}
case '4':
return Event{CtrlAltShiftPageUp, 0, nil}
case '5':
return Event{CtrlAltPageUp, 0, nil}
case '6':
return Event{CtrlAltShiftPageUp, 0, nil}
}
}
if len(r.buffer) == 6 && r.buffer[5] == '~' {
*sz = 6
switch r.buffer[4] {
case '2':
return Event{ShiftPageUp, 0, nil}
case '3':
return Event{AltPageUp, 0, nil}
case '4':
return Event{AltShiftPageUp, 0, nil}
case '5':
return Event{CtrlPageUp, 0, nil}
case '6':
return Event{CtrlShiftPageUp, 0, nil}
case '7':
return Event{CtrlAltPageUp, 0, nil}
case '8':
return Event{CtrlAltShiftPageUp, 0, nil}
case '9':
return Event{AltPageUp, 0, nil}
}
}
return Event{Invalid, 0, nil}
case '6': case '6':
return Event{PageDown, 0, nil} if r.buffer[3] == '~' {
return Event{PageDown, 0, nil}
}
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
switch r.buffer[5] {
case '0':
return Event{AltShiftPageDown, 0, nil}
case '1':
return Event{AltPageDown, 0, nil}
case '2':
return Event{AltShiftPageDown, 0, nil}
case '3':
return Event{CtrlAltPageDown, 0, nil}
case '4':
return Event{CtrlAltShiftPageDown, 0, nil}
case '5':
return Event{CtrlAltPageDown, 0, nil}
case '6':
return Event{CtrlAltShiftPageDown, 0, nil}
}
}
if len(r.buffer) == 6 && r.buffer[5] == '~' {
*sz = 6
switch r.buffer[4] {
case '2':
return Event{ShiftPageDown, 0, nil}
case '3':
return Event{AltPageDown, 0, nil}
case '4':
return Event{AltShiftPageDown, 0, nil}
case '5':
return Event{CtrlPageDown, 0, nil}
case '6':
return Event{CtrlShiftPageDown, 0, nil}
case '7':
return Event{CtrlAltPageDown, 0, nil}
case '8':
return Event{CtrlAltShiftPageDown, 0, nil}
case '9':
return Event{AltPageDown, 0, nil}
}
}
return Event{Invalid, 0, nil}
case '7': case '7':
return Event{Home, 0, nil} return Event{Home, 0, nil}
case '8': case '8':
@@ -526,63 +645,172 @@ func (r *LightRenderer) escSequence(sz *int) Event {
} }
*sz = 6 *sz = 6
switch r.buffer[4] { switch r.buffer[4] {
case '1', '2', '3', '4', '5': case '1', '2', '3', '4', '5', '6', '7', '8', '9':
// Kitty iTerm2 WezTerm // Kitty iTerm2 WezTerm
// SHIFT-ARROW "\e[1;2D" // SHIFT-ARROW "\e[1;2D"
// ALT-SHIFT-ARROW "\e[1;4D" "\e[1;10D" "\e[1;4D" // ALT-SHIFT-ARROW "\e[1;4D" "\e[1;10D" "\e[1;4D"
// CTRL-SHIFT-ARROW "\e[1;6D" N/A // CTRL-SHIFT-ARROW "\e[1;6D" N/A
// CMD-SHIFT-ARROW "\e[1;10D" N/A N/A ("\e[1;2D") // CMD-SHIFT-ARROW "\e[1;10D" N/A N/A ("\e[1;2D")
alt := r.buffer[4] == '3' ctrl := bytes.IndexByte([]byte{'5', '6', '7', '8'}, r.buffer[4]) >= 0
alt := bytes.IndexByte([]byte{'3', '4', '7', '8'}, r.buffer[4]) >= 0
shift := bytes.IndexByte([]byte{'2', '4', '6', '8'}, r.buffer[4]) >= 0
char := r.buffer[5] char := r.buffer[5]
altShift := false if r.buffer[4] == '9' {
if r.buffer[4] == '1' && r.buffer[5] == '0' { ctrl = false
altShift = true alt = true
if len(r.buffer) < 7 { shift = false
return Event{Invalid, 0, nil}
}
*sz = 7
char = r.buffer[6]
} else if r.buffer[4] == '4' {
altShift = true
if len(r.buffer) < 6 { if len(r.buffer) < 6 {
return Event{Invalid, 0, nil} return Event{Invalid, 0, nil}
} }
*sz = 6 *sz = 6
char = r.buffer[5] char = r.buffer[5]
} else if r.buffer[4] == '1' && bytes.IndexByte([]byte{'0', '1', '2', '3', '4', '5', '6'}, r.buffer[5]) >= 0 {
ctrl = bytes.IndexByte([]byte{'3', '4', '5', '6'}, r.buffer[5]) >= 0
alt = true
shift = bytes.IndexByte([]byte{'0', '2', '4', '6'}, r.buffer[5]) >= 0
if len(r.buffer) < 7 {
return Event{Invalid, 0, nil}
}
*sz = 7
char = r.buffer[6]
} }
ctrlShift := ctrl && shift
ctrlAlt := ctrl && alt
altShift := alt && shift
ctrlAltShift := ctrl && alt && shift
switch char { switch char {
case 'A': case 'A':
if alt { if ctrlAltShift {
return Event{AltUp, 0, nil} return Event{CtrlAltShiftUp, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltUp, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftUp, 0, nil}
} }
if altShift { if altShift {
return Event{AltShiftUp, 0, nil} return Event{AltShiftUp, 0, nil}
} }
return Event{ShiftUp, 0, nil} if ctrl {
case 'B': return Event{CtrlUp, 0, nil}
}
if alt { if alt {
return Event{AltDown, 0, nil} return Event{AltUp, 0, nil}
}
if shift {
return Event{ShiftUp, 0, nil}
}
case 'B':
if ctrlAltShift {
return Event{CtrlAltShiftDown, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltDown, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftDown, 0, nil}
} }
if altShift { if altShift {
return Event{AltShiftDown, 0, nil} return Event{AltShiftDown, 0, nil}
} }
return Event{ShiftDown, 0, nil} if ctrl {
case 'C': return Event{CtrlDown, 0, nil}
}
if alt { if alt {
return Event{AltRight, 0, nil} return Event{AltDown, 0, nil}
}
if shift {
return Event{ShiftDown, 0, nil}
}
case 'C':
if ctrlAltShift {
return Event{CtrlAltShiftRight, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltRight, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftRight, 0, nil}
} }
if altShift { if altShift {
return Event{AltShiftRight, 0, nil} return Event{AltShiftRight, 0, nil}
} }
return Event{ShiftRight, 0, nil} if ctrl {
case 'D': return Event{CtrlRight, 0, nil}
}
if shift {
return Event{ShiftRight, 0, nil}
}
if alt { if alt {
return Event{AltLeft, 0, nil} return Event{AltRight, 0, nil}
}
case 'D':
if ctrlAltShift {
return Event{CtrlAltShiftLeft, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltLeft, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftLeft, 0, nil}
} }
if altShift { if altShift {
return Event{AltShiftLeft, 0, nil} return Event{AltShiftLeft, 0, nil}
} }
return Event{ShiftLeft, 0, nil} if ctrl {
return Event{CtrlLeft, 0, nil}
}
if alt {
return Event{AltLeft, 0, nil}
}
if shift {
return Event{ShiftLeft, 0, nil}
}
case 'H':
if ctrlAltShift {
return Event{CtrlAltShiftHome, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltHome, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftHome, 0, nil}
}
if altShift {
return Event{AltShiftHome, 0, nil}
}
if ctrl {
return Event{CtrlHome, 0, nil}
}
if alt {
return Event{AltHome, 0, nil}
}
if shift {
return Event{ShiftHome, 0, nil}
}
case 'F':
if ctrlAltShift {
return Event{CtrlAltShiftEnd, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltEnd, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftEnd, 0, nil}
}
if altShift {
return Event{AltShiftEnd, 0, nil}
}
if ctrl {
return Event{CtrlEnd, 0, nil}
}
if alt {
return Event{AltEnd, 0, nil}
}
if shift {
return Event{ShiftEnd, 0, nil}
}
} }
} // r.buffer[4] } // r.buffer[4]
} // r.buffer[3] } // r.buffer[3]

335
src/tui/light_test.go Normal file
View File

@@ -0,0 +1,335 @@
package tui
import (
"fmt"
"os"
"testing"
"unicode"
)
func TestLightRenderer(t *testing.T) {
tty_file, _ := os.Open("")
renderer, _ := NewLightRenderer(
"", tty_file, &ColorTheme{}, true, false, 0, false, true,
func(h int) int { return h })
light_renderer := renderer.(*LightRenderer)
assertCharSequence := func(sequence string, name string) {
bytes := []byte(sequence)
light_renderer.buffer = bytes
event := light_renderer.GetChar()
if event.KeyName() != name {
t.Errorf(
"sequence: %q | %v | '%s' (%s) != %s",
string(bytes), bytes,
event.KeyName(), event.Type.String(), name)
}
}
assertEscSequence := func(sequence string, name string) {
bytes := []byte(sequence)
light_renderer.buffer = bytes
sz := 1
event := light_renderer.escSequence(&sz)
if fmt.Sprintf("!%s", event.Type.String()) == name {
// this is fine
} else if event.KeyName() != name {
t.Errorf(
"sequence: %q | %v | '%s' (%s) != %s",
string(bytes), bytes,
event.KeyName(), event.Type.String(), name)
}
}
// invalid
assertEscSequence("\x1b[<", "!Invalid")
assertEscSequence("\x1b[1;1R", "!Invalid")
assertEscSequence("\x1b[", "!Invalid")
assertEscSequence("\x1b[1", "!Invalid")
assertEscSequence("\x1b[3;3~1", "!Invalid")
assertEscSequence("\x1b[13", "!Invalid")
assertEscSequence("\x1b[1;3", "!Invalid")
assertEscSequence("\x1b[1;10", "!Invalid")
assertEscSequence("\x1b[220~", "!Invalid")
assertEscSequence("\x1b[5;30~", "!Invalid")
assertEscSequence("\x1b[6;30~", "!Invalid")
// general
for r := 'a'; r < 'z'; r++ {
lower_r := fmt.Sprintf("%c", r)
upper_r := fmt.Sprintf("%c", unicode.ToUpper(r))
assertCharSequence(lower_r, lower_r)
assertCharSequence(upper_r, upper_r)
}
assertCharSequence("\x01", "ctrl-a")
assertCharSequence("\x02", "ctrl-b")
assertCharSequence("\x03", "ctrl-c")
assertCharSequence("\x04", "ctrl-d")
assertCharSequence("\x05", "ctrl-e")
assertCharSequence("\x06", "ctrl-f")
assertCharSequence("\x07", "ctrl-g")
// ctrl-h is the same as ctrl-backspace
// ctrl-i is the same as tab
assertCharSequence("\n", "ctrl-j")
assertCharSequence("\x0b", "ctrl-k")
assertCharSequence("\x0c", "ctrl-l")
assertCharSequence("\r", "enter") // enter
assertCharSequence("\x0e", "ctrl-n")
assertCharSequence("\x0f", "ctrl-o")
assertCharSequence("\x10", "ctrl-p")
assertCharSequence("\x11", "ctrl-q")
assertCharSequence("\x12", "ctrl-r")
assertCharSequence("\x13", "ctrl-s")
assertCharSequence("\x14", "ctrl-t")
assertCharSequence("\x15", "ctrl-u")
assertCharSequence("\x16", "ctrl-v")
assertCharSequence("\x17", "ctrl-w")
assertCharSequence("\x18", "ctrl-x")
assertCharSequence("\x19", "ctrl-y")
assertCharSequence("\x1a", "ctrl-z")
assertCharSequence("\x00", "ctrl-space")
assertCharSequence("\x1c", "ctrl-\\")
assertCharSequence("\x1d", "ctrl-]")
assertCharSequence("\x1e", "ctrl-^")
assertCharSequence("\x1f", "ctrl-/")
assertEscSequence("\x1ba", "alt-a")
assertEscSequence("\x1bb", "alt-b")
assertEscSequence("\x1bc", "alt-c")
assertEscSequence("\x1bd", "alt-d")
assertEscSequence("\x1be", "alt-e")
assertEscSequence("\x1bf", "alt-f")
assertEscSequence("\x1bg", "alt-g")
assertEscSequence("\x1bh", "alt-h")
assertEscSequence("\x1bi", "alt-i")
assertEscSequence("\x1bj", "alt-j")
assertEscSequence("\x1bk", "alt-k")
assertEscSequence("\x1bl", "alt-l")
assertEscSequence("\x1bm", "alt-m")
assertEscSequence("\x1bn", "alt-n")
assertEscSequence("\x1bo", "alt-o")
assertEscSequence("\x1bp", "alt-p")
assertEscSequence("\x1bq", "alt-q")
assertEscSequence("\x1br", "alt-r")
assertEscSequence("\x1bs", "alt-s")
assertEscSequence("\x1bt", "alt-t")
assertEscSequence("\x1bu", "alt-u")
assertEscSequence("\x1bv", "alt-v")
assertEscSequence("\x1bw", "alt-w")
assertEscSequence("\x1bx", "alt-x")
assertEscSequence("\x1by", "alt-y")
assertEscSequence("\x1bz", "alt-z")
assertEscSequence("\x1bOP", "f1")
assertEscSequence("\x1bOQ", "f2")
assertEscSequence("\x1bOR", "f3")
assertEscSequence("\x1bOS", "f4")
assertEscSequence("\x1b[15~", "f5")
assertEscSequence("\x1b[17~", "f6")
assertEscSequence("\x1b[18~", "f7")
assertEscSequence("\x1b[19~", "f8")
assertEscSequence("\x1b[20~", "f9")
assertEscSequence("\x1b[21~", "f10")
assertEscSequence("\x1b[23~", "f11")
assertEscSequence("\x1b[24~", "f12")
assertEscSequence("\x1b", "esc")
assertCharSequence("\t", "tab")
assertEscSequence("\x1b[Z", "shift-tab")
assertCharSequence("\x7f", "backspace")
assertEscSequence("\x1b\x7f", "alt-backspace")
assertCharSequence("\b", "ctrl-backspace")
assertEscSequence("\x1b\b", "ctrl-alt-backspace")
assertEscSequence("\x1b[A", "up")
assertEscSequence("\x1b[B", "down")
assertEscSequence("\x1b[C", "right")
assertEscSequence("\x1b[D", "left")
assertEscSequence("\x1b[H", "home")
assertEscSequence("\x1b[F", "end")
assertEscSequence("\x1b[2~", "insert")
assertEscSequence("\x1b[3~", "delete")
assertEscSequence("\x1b[5~", "page-up")
assertEscSequence("\x1b[6~", "page-down")
assertEscSequence("\x1b[7~", "home")
assertEscSequence("\x1b[8~", "end")
assertEscSequence("\x1b[1;2A", "shift-up")
assertEscSequence("\x1b[1;2B", "shift-down")
assertEscSequence("\x1b[1;2C", "shift-right")
assertEscSequence("\x1b[1;2D", "shift-left")
assertEscSequence("\x1b[1;2H", "shift-home")
assertEscSequence("\x1b[1;2F", "shift-end")
assertEscSequence("\x1b[3;2~", "shift-delete")
assertEscSequence("\x1b[5;2~", "shift-page-up")
assertEscSequence("\x1b[6;2~", "shift-page-down")
assertEscSequence("\x1b\x1b", "esc")
assertEscSequence("\x1b\x1b[A", "alt-up")
assertEscSequence("\x1b\x1b[B", "alt-down")
assertEscSequence("\x1b\x1b[C", "alt-right")
assertEscSequence("\x1b\x1b[D", "alt-left")
assertEscSequence("\x1b[1;3A", "alt-up")
assertEscSequence("\x1b[1;3B", "alt-down")
assertEscSequence("\x1b[1;3C", "alt-right")
assertEscSequence("\x1b[1;3D", "alt-left")
assertEscSequence("\x1b[1;3H", "alt-home")
assertEscSequence("\x1b[1;3F", "alt-end")
assertEscSequence("\x1b[3;3~", "alt-delete")
assertEscSequence("\x1b[5;3~", "alt-page-up")
assertEscSequence("\x1b[6;3~", "alt-page-down")
assertEscSequence("\x1b[1;4A", "alt-shift-up")
assertEscSequence("\x1b[1;4B", "alt-shift-down")
assertEscSequence("\x1b[1;4C", "alt-shift-right")
assertEscSequence("\x1b[1;4D", "alt-shift-left")
assertEscSequence("\x1b[1;4H", "alt-shift-home")
assertEscSequence("\x1b[1;4F", "alt-shift-end")
assertEscSequence("\x1b[3;4~", "alt-shift-delete")
assertEscSequence("\x1b[5;4~", "alt-shift-page-up")
assertEscSequence("\x1b[6;4~", "alt-shift-page-down")
assertEscSequence("\x1b[1;5A", "ctrl-up")
assertEscSequence("\x1b[1;5B", "ctrl-down")
assertEscSequence("\x1b[1;5C", "ctrl-right")
assertEscSequence("\x1b[1;5D", "ctrl-left")
assertEscSequence("\x1b[1;5H", "ctrl-home")
assertEscSequence("\x1b[1;5F", "ctrl-end")
assertEscSequence("\x1b[3;5~", "ctrl-delete")
assertEscSequence("\x1b[5;5~", "ctrl-page-up")
assertEscSequence("\x1b[6;5~", "ctrl-page-down")
assertEscSequence("\x1b[1;7A", "ctrl-alt-up")
assertEscSequence("\x1b[1;7B", "ctrl-alt-down")
assertEscSequence("\x1b[1;7C", "ctrl-alt-right")
assertEscSequence("\x1b[1;7D", "ctrl-alt-left")
assertEscSequence("\x1b[1;7H", "ctrl-alt-home")
assertEscSequence("\x1b[1;7F", "ctrl-alt-end")
assertEscSequence("\x1b[3;7~", "ctrl-alt-delete")
assertEscSequence("\x1b[5;7~", "ctrl-alt-page-up")
assertEscSequence("\x1b[6;7~", "ctrl-alt-page-down")
assertEscSequence("\x1b[1;6A", "ctrl-shift-up")
assertEscSequence("\x1b[1;6B", "ctrl-shift-down")
assertEscSequence("\x1b[1;6C", "ctrl-shift-right")
assertEscSequence("\x1b[1;6D", "ctrl-shift-left")
assertEscSequence("\x1b[1;6H", "ctrl-shift-home")
assertEscSequence("\x1b[1;6F", "ctrl-shift-end")
assertEscSequence("\x1b[3;6~", "ctrl-shift-delete")
assertEscSequence("\x1b[5;6~", "ctrl-shift-page-up")
assertEscSequence("\x1b[6;6~", "ctrl-shift-page-down")
assertEscSequence("\x1b[1;8A", "ctrl-alt-shift-up")
assertEscSequence("\x1b[1;8B", "ctrl-alt-shift-down")
assertEscSequence("\x1b[1;8C", "ctrl-alt-shift-right")
assertEscSequence("\x1b[1;8D", "ctrl-alt-shift-left")
assertEscSequence("\x1b[1;8H", "ctrl-alt-shift-home")
assertEscSequence("\x1b[1;8F", "ctrl-alt-shift-end")
assertEscSequence("\x1b[3;8~", "ctrl-alt-shift-delete")
assertEscSequence("\x1b[5;8~", "ctrl-alt-shift-page-up")
assertEscSequence("\x1b[6;8~", "ctrl-alt-shift-page-down")
// xterm meta & mac
assertEscSequence("\x1b[1;9A", "alt-up")
assertEscSequence("\x1b[1;9B", "alt-down")
assertEscSequence("\x1b[1;9C", "alt-right")
assertEscSequence("\x1b[1;9D", "alt-left")
assertEscSequence("\x1b[1;9H", "alt-home")
assertEscSequence("\x1b[1;9F", "alt-end")
assertEscSequence("\x1b[3;9~", "alt-delete")
assertEscSequence("\x1b[5;9~", "alt-page-up")
assertEscSequence("\x1b[6;9~", "alt-page-down")
assertEscSequence("\x1b[1;10A", "alt-shift-up")
assertEscSequence("\x1b[1;10B", "alt-shift-down")
assertEscSequence("\x1b[1;10C", "alt-shift-right")
assertEscSequence("\x1b[1;10D", "alt-shift-left")
assertEscSequence("\x1b[1;10H", "alt-shift-home")
assertEscSequence("\x1b[1;10F", "alt-shift-end")
assertEscSequence("\x1b[3;10~", "alt-shift-delete")
assertEscSequence("\x1b[5;10~", "alt-shift-page-up")
assertEscSequence("\x1b[6;10~", "alt-shift-page-down")
assertEscSequence("\x1b[1;11A", "alt-up")
assertEscSequence("\x1b[1;11B", "alt-down")
assertEscSequence("\x1b[1;11C", "alt-right")
assertEscSequence("\x1b[1;11D", "alt-left")
assertEscSequence("\x1b[1;11H", "alt-home")
assertEscSequence("\x1b[1;11F", "alt-end")
assertEscSequence("\x1b[3;11~", "alt-delete")
assertEscSequence("\x1b[5;11~", "alt-page-up")
assertEscSequence("\x1b[6;11~", "alt-page-down")
assertEscSequence("\x1b[1;12A", "alt-shift-up")
assertEscSequence("\x1b[1;12B", "alt-shift-down")
assertEscSequence("\x1b[1;12C", "alt-shift-right")
assertEscSequence("\x1b[1;12D", "alt-shift-left")
assertEscSequence("\x1b[1;12H", "alt-shift-home")
assertEscSequence("\x1b[1;12F", "alt-shift-end")
assertEscSequence("\x1b[3;12~", "alt-shift-delete")
assertEscSequence("\x1b[5;12~", "alt-shift-page-up")
assertEscSequence("\x1b[6;12~", "alt-shift-page-down")
assertEscSequence("\x1b[1;13A", "ctrl-alt-up")
assertEscSequence("\x1b[1;13B", "ctrl-alt-down")
assertEscSequence("\x1b[1;13C", "ctrl-alt-right")
assertEscSequence("\x1b[1;13D", "ctrl-alt-left")
assertEscSequence("\x1b[1;13H", "ctrl-alt-home")
assertEscSequence("\x1b[1;13F", "ctrl-alt-end")
assertEscSequence("\x1b[3;13~", "ctrl-alt-delete")
assertEscSequence("\x1b[5;13~", "ctrl-alt-page-up")
assertEscSequence("\x1b[6;13~", "ctrl-alt-page-down")
assertEscSequence("\x1b[1;14A", "ctrl-alt-shift-up")
assertEscSequence("\x1b[1;14B", "ctrl-alt-shift-down")
assertEscSequence("\x1b[1;14C", "ctrl-alt-shift-right")
assertEscSequence("\x1b[1;14D", "ctrl-alt-shift-left")
assertEscSequence("\x1b[1;14H", "ctrl-alt-shift-home")
assertEscSequence("\x1b[1;14F", "ctrl-alt-shift-end")
assertEscSequence("\x1b[3;14~", "ctrl-alt-shift-delete")
assertEscSequence("\x1b[5;14~", "ctrl-alt-shift-page-up")
assertEscSequence("\x1b[6;14~", "ctrl-alt-shift-page-down")
assertEscSequence("\x1b[1;15A", "ctrl-alt-up")
assertEscSequence("\x1b[1;15B", "ctrl-alt-down")
assertEscSequence("\x1b[1;15C", "ctrl-alt-right")
assertEscSequence("\x1b[1;15D", "ctrl-alt-left")
assertEscSequence("\x1b[1;15H", "ctrl-alt-home")
assertEscSequence("\x1b[1;15F", "ctrl-alt-end")
assertEscSequence("\x1b[3;15~", "ctrl-alt-delete")
assertEscSequence("\x1b[5;15~", "ctrl-alt-page-up")
assertEscSequence("\x1b[6;15~", "ctrl-alt-page-down")
assertEscSequence("\x1b[1;16A", "ctrl-alt-shift-up")
assertEscSequence("\x1b[1;16B", "ctrl-alt-shift-down")
assertEscSequence("\x1b[1;16C", "ctrl-alt-shift-right")
assertEscSequence("\x1b[1;16D", "ctrl-alt-shift-left")
assertEscSequence("\x1b[1;16H", "ctrl-alt-shift-home")
assertEscSequence("\x1b[1;16F", "ctrl-alt-shift-end")
assertEscSequence("\x1b[3;16~", "ctrl-alt-shift-delete")
assertEscSequence("\x1b[5;16~", "ctrl-alt-shift-page-up")
assertEscSequence("\x1b[6;16~", "ctrl-alt-shift-page-down")
// tmux & emacs
assertEscSequence("\x1bOA", "up")
assertEscSequence("\x1bOB", "down")
assertEscSequence("\x1bOC", "right")
assertEscSequence("\x1bOD", "left")
assertEscSequence("\x1bOH", "home")
assertEscSequence("\x1bOF", "end")
// rrvt
assertEscSequence("\x1b[1~", "home")
assertEscSequence("\x1b[4~", "end")
assertEscSequence("\x1b[11~", "f1")
assertEscSequence("\x1b[12~", "f2")
assertEscSequence("\x1b[13~", "f3")
assertEscSequence("\x1b[14~", "f4")
}

View File

@@ -354,6 +354,8 @@ func (r *FullscreenRenderer) GetChar() Event {
shift := (mods & tcell.ModShift) > 0 shift := (mods & tcell.ModShift) > 0
ctrlAlt := ctrl && alt ctrlAlt := ctrl && alt
altShift := alt && shift altShift := alt && shift
ctrlShift := ctrl && shift
ctrlAltShift := ctrl && alt && shift
keyfn := func(r rune) Event { keyfn := func(r rune) Event {
if alt { if alt {
@@ -380,8 +382,11 @@ func (r *FullscreenRenderer) GetChar() Event {
case tcell.KeyCtrlH: case tcell.KeyCtrlH:
switch ev.Rune() { switch ev.Rune() {
case 0: case 0:
if ctrlAlt {
return Event{CtrlAltBackspace, 0, nil}
}
if ctrl { if ctrl {
return Event{Backspace, 0, nil} return Event{CtrlBackspace, 0, nil}
} }
case rune(tcell.KeyCtrlH): case rune(tcell.KeyCtrlH):
switch { switch {
@@ -442,6 +447,9 @@ func (r *FullscreenRenderer) GetChar() Event {
return Event{CtrlSlash, 0, nil} return Event{CtrlSlash, 0, nil}
// section 3: (Alt)+Backspace2 // section 3: (Alt)+Backspace2
case tcell.KeyBackspace2: case tcell.KeyBackspace2:
if ctrl {
return Event{CtrlBackspace, 0, nil}
}
if alt { if alt {
return Event{AltBackspace, 0, nil} return Event{AltBackspace, 0, nil}
} }
@@ -449,9 +457,21 @@ func (r *FullscreenRenderer) GetChar() Event {
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right) // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
case tcell.KeyUp: case tcell.KeyUp:
if ctrlAltShift {
return Event{CtrlAltShiftUp, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltUp, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftUp, 0, nil}
}
if altShift { if altShift {
return Event{AltShiftUp, 0, nil} return Event{AltShiftUp, 0, nil}
} }
if ctrl {
return Event{CtrlUp, 0, nil}
}
if shift { if shift {
return Event{ShiftUp, 0, nil} return Event{ShiftUp, 0, nil}
} }
@@ -460,9 +480,21 @@ func (r *FullscreenRenderer) GetChar() Event {
} }
return Event{Up, 0, nil} return Event{Up, 0, nil}
case tcell.KeyDown: case tcell.KeyDown:
if ctrlAltShift {
return Event{CtrlAltShiftDown, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltDown, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftDown, 0, nil}
}
if altShift { if altShift {
return Event{AltShiftDown, 0, nil} return Event{AltShiftDown, 0, nil}
} }
if ctrl {
return Event{CtrlDown, 0, nil}
}
if shift { if shift {
return Event{ShiftDown, 0, nil} return Event{ShiftDown, 0, nil}
} }
@@ -471,9 +503,21 @@ func (r *FullscreenRenderer) GetChar() Event {
} }
return Event{Down, 0, nil} return Event{Down, 0, nil}
case tcell.KeyLeft: case tcell.KeyLeft:
if ctrlAltShift {
return Event{CtrlAltShiftLeft, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltLeft, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftLeft, 0, nil}
}
if altShift { if altShift {
return Event{AltShiftLeft, 0, nil} return Event{AltShiftLeft, 0, nil}
} }
if ctrl {
return Event{CtrlLeft, 0, nil}
}
if shift { if shift {
return Event{ShiftLeft, 0, nil} return Event{ShiftLeft, 0, nil}
} }
@@ -482,9 +526,21 @@ func (r *FullscreenRenderer) GetChar() Event {
} }
return Event{Left, 0, nil} return Event{Left, 0, nil}
case tcell.KeyRight: case tcell.KeyRight:
if ctrlAltShift {
return Event{CtrlAltShiftRight, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltRight, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftRight, 0, nil}
}
if altShift { if altShift {
return Event{AltShiftRight, 0, nil} return Event{AltShiftRight, 0, nil}
} }
if ctrl {
return Event{CtrlRight, 0, nil}
}
if shift { if shift {
return Event{ShiftRight, 0, nil} return Event{ShiftRight, 0, nil}
} }
@@ -497,20 +553,119 @@ func (r *FullscreenRenderer) GetChar() Event {
case tcell.KeyInsert: case tcell.KeyInsert:
return Event{Insert, 0, nil} return Event{Insert, 0, nil}
case tcell.KeyHome: case tcell.KeyHome:
if ctrlAltShift {
return Event{CtrlAltShiftHome, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltHome, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftHome, 0, nil}
}
if altShift {
return Event{AltShiftHome, 0, nil}
}
if ctrl {
return Event{CtrlHome, 0, nil}
}
if shift {
return Event{ShiftHome, 0, nil}
}
if alt {
return Event{AltHome, 0, nil}
}
return Event{Home, 0, nil} return Event{Home, 0, nil}
case tcell.KeyDelete: case tcell.KeyDelete:
if ctrlAltShift {
return Event{CtrlAltShiftDelete, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltDelete, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftDelete, 0, nil}
}
if altShift {
return Event{AltShiftDelete, 0, nil}
}
if ctrl { if ctrl {
return Event{CtrlDelete, 0, nil} return Event{CtrlDelete, 0, nil}
} }
if alt {
return Event{AltDelete, 0, nil}
}
if shift { if shift {
return Event{ShiftDelete, 0, nil} return Event{ShiftDelete, 0, nil}
} }
return Event{Delete, 0, nil} return Event{Delete, 0, nil}
case tcell.KeyEnd: case tcell.KeyEnd:
if ctrlAltShift {
return Event{CtrlAltShiftEnd, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltEnd, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftEnd, 0, nil}
}
if altShift {
return Event{AltShiftEnd, 0, nil}
}
if ctrl {
return Event{CtrlEnd, 0, nil}
}
if shift {
return Event{ShiftEnd, 0, nil}
}
if alt {
return Event{AltEnd, 0, nil}
}
return Event{End, 0, nil} return Event{End, 0, nil}
case tcell.KeyPgUp: case tcell.KeyPgUp:
if ctrlAltShift {
return Event{CtrlAltShiftPageUp, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltPageUp, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftPageUp, 0, nil}
}
if altShift {
return Event{AltShiftPageUp, 0, nil}
}
if ctrl {
return Event{CtrlPageUp, 0, nil}
}
if shift {
return Event{ShiftPageUp, 0, nil}
}
if alt {
return Event{AltPageUp, 0, nil}
}
return Event{PageUp, 0, nil} return Event{PageUp, 0, nil}
case tcell.KeyPgDn: case tcell.KeyPgDn:
if ctrlAltShift {
return Event{CtrlAltShiftPageDown, 0, nil}
}
if ctrlAlt {
return Event{CtrlAltPageDown, 0, nil}
}
if ctrlShift {
return Event{CtrlShiftPageDown, 0, nil}
}
if altShift {
return Event{AltShiftPageDown, 0, nil}
}
if ctrl {
return Event{CtrlPageDown, 0, nil}
}
if shift {
return Event{ShiftPageDown, 0, nil}
}
if alt {
return Event{AltPageDown, 0, nil}
}
return Event{PageDown, 0, nil} return Event{PageDown, 0, nil}
case tcell.KeyBacktab: case tcell.KeyBacktab:
return Event{ShiftTab, 0, nil} return Event{ShiftTab, 0, nil}
@@ -569,13 +724,13 @@ func (r *FullscreenRenderer) GetChar() Event {
func (r *FullscreenRenderer) Pause(clear bool) { func (r *FullscreenRenderer) Pause(clear bool) {
if clear { if clear {
r.Close() _screen.Suspend()
} }
} }
func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) { func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) {
if clear { if clear {
r.initScreen() _screen.Resume()
} }
} }

View File

@@ -107,18 +107,20 @@ func TestGetCharEventKey(t *testing.T) {
{giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // fabricated {giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // fabricated
{giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{Backspace, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{Backspace, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Delete, 0, nil}}, {giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Delete, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{Delete, 0, nil}}, {giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{AltDelete, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{Backspace, 0, nil}}, // actual "Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{Backspace, 0, nil}}, // actual "Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke
{giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke {giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
@@ -126,9 +128,41 @@ func TestGetCharEventKey(t *testing.T) {
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right) // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}}, {giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}}, {giveKey{tcell.KeyDown, 0, tcell.ModNone}, wantKey{Down, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModNone}, wantKey{Left, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModNone}, wantKey{Right, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModNone}, wantKey{Down, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModNone}, wantKey{Right, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModNone}, wantKey{Left, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModCtrl}, wantKey{CtrlUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModCtrl}, wantKey{CtrlDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModCtrl}, wantKey{CtrlRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModCtrl}, wantKey{CtrlLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModShift}, wantKey{ShiftUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModShift}, wantKey{ShiftDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModShift}, wantKey{ShiftRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{ShiftLeft, 0, nil}}, {giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{ShiftLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModAlt}, wantKey{AltUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModAlt}, wantKey{AltRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModAlt}, wantKey{AltLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftRight, 0, nil}}, {giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftLeft, 0, nil}},
{giveKey{tcell.KeyUp, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftUp, 0, nil}},
{giveKey{tcell.KeyDown, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftDown, 0, nil}},
{giveKey{tcell.KeyRight, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftRight, 0, nil}},
{giveKey{tcell.KeyLeft, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftLeft, 0, nil}},
{giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
@@ -137,6 +171,46 @@ func TestGetCharEventKey(t *testing.T) {
// section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12) // section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
{giveKey{tcell.KeyInsert, 0, tcell.ModNone}, wantKey{Insert, 0, nil}}, {giveKey{tcell.KeyInsert, 0, tcell.ModNone}, wantKey{Insert, 0, nil}},
{giveKey{tcell.KeyF1, 0, tcell.ModNone}, wantKey{F1, 0, nil}}, {giveKey{tcell.KeyF1, 0, tcell.ModNone}, wantKey{F1, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModNone}, wantKey{Home, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModNone}, wantKey{End, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Delete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModNone}, wantKey{PageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModNone}, wantKey{PageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModCtrl}, wantKey{CtrlHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModCtrl}, wantKey{CtrlEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModCtrl}, wantKey{CtrlDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModCtrl}, wantKey{CtrlPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModCtrl}, wantKey{CtrlPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModShift}, wantKey{ShiftHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModShift}, wantKey{ShiftEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModShift}, wantKey{ShiftDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModShift}, wantKey{ShiftPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModShift}, wantKey{ShiftPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModAlt}, wantKey{AltHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModAlt}, wantKey{AltEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{AltDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModAlt}, wantKey{AltPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModAlt}, wantKey{AltPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlShiftPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltShiftPageDown, 0, nil}},
{giveKey{tcell.KeyHome, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftHome, 0, nil}},
{giveKey{tcell.KeyEnd, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftEnd, 0, nil}},
{giveKey{tcell.KeyDelete, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftDelete, 0, nil}},
{giveKey{tcell.KeyPgUp, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftPageUp, 0, nil}},
{giveKey{tcell.KeyPgDn, 0, tcell.ModCtrl | tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAltShiftPageDown, 0, nil}},
// section 6: (Ctrl+Alt)+'rune' // section 6: (Ctrl+Alt)+'rune'
{giveKey{tcell.KeyRune, 'a', tcell.ModNone}, wantKey{Rune, 'a', nil}}, {giveKey{tcell.KeyRune, 'a', tcell.ModNone}, wantKey{Rune, 'a', nil}},
{giveKey{tcell.KeyRune, 'a', tcell.ModCtrl}, wantKey{Rune, 'a', nil}}, // fabricated {giveKey{tcell.KeyRune, 'a', tcell.ModCtrl}, wantKey{Rune, 'a', nil}}, // fabricated
@@ -198,6 +272,10 @@ func TestGetCharEventKey(t *testing.T) {
initialResizeAsInvalid = false initialResizeAsInvalid = false
gotEvent = r.GetChar() gotEvent = r.GetChar()
} }
if gotEvent.Type == Resize {
t.Logf("Resize swallowed")
gotEvent = r.GetChar()
}
t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char) t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char)
t.Logf("gotEvent = %T{Type: %v, Char: %q (%[3]v)}\n", gotEvent, gotEvent.Type, gotEvent.Char) t.Logf("gotEvent = %T{Type: %v, Char: %q (%[3]v)}\n", gotEvent, gotEvent.Type, gotEvent.Char)

View File

@@ -44,7 +44,6 @@ const (
CtrlZ CtrlZ
Esc Esc
CtrlSpace CtrlSpace
CtrlDelete
// https://apple.stackexchange.com/questions/24261/how-do-i-send-c-that-is-control-slash-to-the-terminal // https://apple.stackexchange.com/questions/24261/how-do-i-send-c-that-is-control-slash-to-the-terminal
CtrlBackSlash CtrlBackSlash
@@ -72,6 +71,10 @@ const (
ShiftLeft ShiftLeft
ShiftRight ShiftRight
ShiftDelete ShiftDelete
ShiftHome
ShiftEnd
ShiftPageUp
ShiftPageDown
F1 F1
F2 F2
@@ -92,15 +95,67 @@ const (
AltDown AltDown
AltLeft AltLeft
AltRight AltRight
AltDelete
AltHome
AltEnd
AltPageUp
AltPageDown
AltShiftUp AltShiftUp
AltShiftDown AltShiftDown
AltShiftLeft AltShiftLeft
AltShiftRight AltShiftRight
AltShiftDelete
AltShiftHome
AltShiftEnd
AltShiftPageUp
AltShiftPageDown
CtrlUp
CtrlDown
CtrlLeft
CtrlRight
CtrlHome
CtrlEnd
CtrlBackspace
CtrlDelete
CtrlPageUp
CtrlPageDown
Alt Alt
CtrlAlt CtrlAlt
CtrlAltUp
CtrlAltDown
CtrlAltLeft
CtrlAltRight
CtrlAltHome
CtrlAltEnd
CtrlAltBackspace
CtrlAltDelete
CtrlAltPageUp
CtrlAltPageDown
CtrlShiftUp
CtrlShiftDown
CtrlShiftLeft
CtrlShiftRight
CtrlShiftHome
CtrlShiftEnd
CtrlShiftDelete
CtrlShiftPageUp
CtrlShiftPageDown
CtrlAltShiftUp
CtrlAltShiftDown
CtrlAltShiftLeft
CtrlAltShiftRight
CtrlAltShiftHome
CtrlAltShiftEnd
CtrlAltShiftDelete
CtrlAltShiftPageUp
CtrlAltShiftPageDown
Invalid Invalid
Fatal Fatal
BracketedPasteBegin BracketedPasteBegin
@@ -132,6 +187,7 @@ const (
Jump Jump
JumpCancel JumpCancel
ClickHeader ClickHeader
ClickFooter
Multi Multi
) )
@@ -247,6 +303,14 @@ const (
colMagenta colMagenta
colCyan colCyan
colWhite colWhite
colGrey
colBrightRed
colBrightGreen
colBrightYellow
colBrightBlue
colBrightMagenta
colBrightCyan
colBrightWhite
) )
type FillReturn int type FillReturn int
@@ -504,7 +568,7 @@ type BorderCharacter int
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle { func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
if shape == BorderNone || shape == BorderPhantom { if shape == BorderNone || shape == BorderPhantom {
return BorderStyle{ return BorderStyle{
shape: shape, shape: BorderNone,
top: ' ', top: ' ',
bottom: ' ', bottom: ' ',
left: ' ', left: ' ',
@@ -720,6 +784,7 @@ var (
ColMatch ColorPair ColMatch ColorPair
ColCursor ColorPair ColCursor ColorPair
ColCursorEmpty ColorPair ColCursorEmpty ColorPair
ColCursorEmptyChar ColorPair
ColMarker ColorPair ColMarker ColorPair
ColSelected ColorPair ColSelected ColorPair
ColSelectedMatch ColorPair ColSelectedMatch ColorPair
@@ -866,19 +931,19 @@ func init() {
SelectedFg: ColorAttr{colUndefined, AttrUndefined}, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined}, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{colBlack, AttrUndefined}, DarkBg: ColorAttr{colGrey, AttrUndefined},
Prompt: ColorAttr{colBlue, AttrUndefined}, Prompt: ColorAttr{colBlue, AttrUndefined},
Match: ColorAttr{colGreen, AttrUndefined}, Match: ColorAttr{colGreen, AttrUndefined},
Current: ColorAttr{colYellow, AttrUndefined}, Current: ColorAttr{colBrightWhite, AttrUndefined},
CurrentMatch: ColorAttr{colGreen, AttrUndefined}, CurrentMatch: ColorAttr{colBrightGreen, AttrUndefined},
Spinner: ColorAttr{colGreen, AttrUndefined}, Spinner: ColorAttr{colGreen, AttrUndefined},
Info: ColorAttr{colWhite, AttrUndefined}, Info: ColorAttr{colYellow, AttrUndefined},
Cursor: ColorAttr{colRed, AttrUndefined}, Cursor: ColorAttr{colRed, AttrUndefined},
Marker: ColorAttr{colMagenta, AttrUndefined}, Marker: ColorAttr{colMagenta, AttrUndefined},
Header: ColorAttr{colCyan, AttrUndefined}, Header: ColorAttr{colCyan, AttrUndefined},
Footer: ColorAttr{colCyan, AttrUndefined}, Footer: ColorAttr{colCyan, AttrUndefined},
Border: ColorAttr{colBlack, AttrUndefined}, Border: ColorAttr{colDefault, Dim},
BorderLabel: ColorAttr{colWhite, AttrUndefined}, BorderLabel: ColorAttr{colDefault, AttrUndefined},
Ghost: ColorAttr{colUndefined, Dim}, Ghost: ColorAttr{colUndefined, Dim},
Disabled: ColorAttr{colUndefined, AttrUndefined}, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined}, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
@@ -1112,10 +1177,11 @@ func initPalette(theme *ColorTheme) {
ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg) ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg)
ColCursor = pair(theme.Cursor, theme.Gutter) ColCursor = pair(theme.Cursor, theme.Gutter)
ColCursorEmpty = pair(blank, theme.Gutter) ColCursorEmpty = pair(blank, theme.Gutter)
ColCursorEmptyChar = pair(theme.Gutter, theme.ListBg)
if theme.SelectedBg.Color != theme.ListBg.Color { if theme.SelectedBg.Color != theme.ListBg.Color {
ColMarker = pair(theme.Marker, theme.SelectedBg) ColMarker = pair(theme.Marker, theme.SelectedBg)
} else { } else {
ColMarker = pair(theme.Marker, theme.Gutter) ColMarker = pair(theme.Marker, theme.ListBg)
} }
ColCurrent = pair(theme.Current, theme.DarkBg) ColCurrent = pair(theme.Current, theme.DarkBg)
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg) ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)

View File

@@ -24,7 +24,7 @@ DEFAULT_TIMEOUT = 10
FILE = File.expand_path(__FILE__) FILE = File.expand_path(__FILE__)
BASE = File.expand_path('../..', __dir__) BASE = File.expand_path('../..', __dir__)
Dir.chdir(BASE) Dir.chdir(BASE)
FZF = "FZF_DEFAULT_OPTS=\"--no-scrollbar --pointer \\> --marker \\>\" FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf".freeze FZF = %[FZF_DEFAULT_OPTS="--no-scrollbar --gutter ' ' --pointer '>' --marker '>'" FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf].freeze
def wait(timeout = DEFAULT_TIMEOUT) def wait(timeout = DEFAULT_TIMEOUT)
since = Time.now since = Time.now

View File

@@ -108,6 +108,40 @@ class TestCore < TestInteractive
assert_equal %w[3 2 5 6 8 7], fzf_output_lines assert_equal %w[3 2 5 6 8 7], fzf_output_lines
end end
def test_subword_forward
tmux.send_keys "#{FZF} --bind K:kill-subword,F:forward-subword -q 'foo bar foo-bar fooFooBar'", :Enter, :Home
tmux.until { |lines| assert_equal '> foo bar foo-bar fooFooBar', lines.last }
tmux.send_keys 'F', :Delete
tmux.until { |lines| assert_equal '> foobar foo-bar fooFooBar', lines.last }
tmux.send_keys 'K'
tmux.until { |lines| assert_equal '> foo foo-bar fooFooBar', lines.last }
tmux.send_keys 'F', 'K'
tmux.until { |lines| assert_equal '> foo foo fooFooBar', lines.last }
tmux.send_keys 'F', 'F', 'K'
tmux.until { |lines| assert_equal '> foo foo fooFoo', lines.last }
end
def test_subword_backward
tmux.send_keys "#{FZF} --bind K:backward-kill-subword,B:backward-subword -q 'foo bar foo-bar fooBar'", :Enter
tmux.until { |lines| assert_equal '> foo bar foo-bar fooBar', lines.last }
tmux.send_keys 'B', :BSpace
tmux.until { |lines| assert_equal '> foo bar foo-bar foBar', lines.last }
tmux.send_keys 'K'
tmux.until { |lines| assert_equal '> foo bar foo-bar Bar', lines.last }
tmux.send_keys 'B', :BSpace
tmux.until { |lines| assert_equal '> foo bar foobar Bar', lines.last }
tmux.send_keys 'B', 'B', :BSpace
tmux.until { |lines| assert_equal '> foobar foobar Bar', lines.last }
end
def test_multi_max def test_multi_max
tmux.send_keys "seq 1 10 | #{FZF} -m 3 --bind A:select-all,T:toggle-all --preview 'echo [{+}]/{}'", :Enter tmux.send_keys "seq 1 10 | #{FZF} -m 3 --bind A:select-all,T:toggle-all --preview 'echo [{+}]/{}'", :Enter
@@ -1415,6 +1449,11 @@ class TestCore < TestInteractive
tmux.until { assert_match(%r{ --1/10000/10000-- *$}, it[-1]) } tmux.until { assert_match(%r{ --1/10000/10000-- *$}, it[-1]) }
end end
def test_info_command_inline_right_no_ansi
tmux.send_keys(%(seq 10000 | #{FZF} --info-command 'echo -e "--$FZF_POS/$FZF_INFO--"' --info inline-right), :Enter)
tmux.until { assert_match(%r{ --1/10000/10000-- *$}, it[-1]) }
end
def test_info_command_and_focus def test_info_command_and_focus
tmux.send_keys(%(seq 100 | #{FZF} --separator x --info-command 'echo $FZF_POS' --bind focus:clear-query), :Enter) tmux.send_keys(%(seq 100 | #{FZF} --separator x --info-command 'echo $FZF_POS' --bind focus:clear-query), :Enter)
tmux.until { assert_match(/^ 1 xx/, it[-2]) } tmux.until { assert_match(/^ 1 xx/, it[-2]) }
@@ -2035,4 +2074,29 @@ class TestCore < TestInteractive
assert_equal 19, it.select_count assert_equal 19, it.select_count
end end
end end
def test_trigger
tmux.send_keys %(seq 100 | #{FZF} --bind 'a:up+trigger(a),b:trigger(a,a,b,a)'), :Enter
tmux.until { assert_equal 100, it.match_count }
tmux.until { |lines| assert_includes lines, '> 1' }
tmux.send_keys :a
tmux.until { |lines| assert_includes lines, '> 3' }
tmux.send_keys :b
tmux.until { |lines| assert_includes lines, '> 9' }
end
def test_change_nth_unset_default
tmux.send_keys %(echo foo bar | #{FZF} --nth 2 --query fb --bind space:change-nth:), :Enter
tmux.until do
assert_equal 1, it.item_count
assert_equal 0, it.match_count
end
tmux.send_keys :Space
tmux.until do
assert_equal 1, it.item_count
assert_equal 1, it.match_count
end
end
end end

View File

@@ -403,7 +403,7 @@ class TestExec < TestInteractive
end end
def test_become def test_become
tmux.send_keys "seq 100 | #{FZF} --bind 'enter:become:seq {} | #{FZF}'", :Enter tmux.send_keys "seq 100 | fzf --bind 'enter:become:seq {} | fzf'", :Enter
tmux.until { |lines| assert_equal 100, lines.match_count } tmux.until { |lines| assert_equal 100, lines.match_count }
tmux.send_keys 999 tmux.send_keys 999
tmux.until { |lines| assert_equal 0, lines.match_count } tmux.until { |lines| assert_equal 0, lines.match_count }

View File

@@ -178,8 +178,8 @@ class TestLayout < TestInteractive
tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border rounded', :Enter tmux.send_keys 'seq 3 | fzf --height ~100% --info=inline --border rounded', :Enter
expected = <<~OUTPUT expected = <<~OUTPUT
3 3
2 2
> 1 > 1
> < 3/3 > < 3/3
@@ -197,8 +197,8 @@ class TestLayout < TestInteractive
3 3
2 2
> 1 > 1
> < 3/3 > < 3/3
@@ -247,7 +247,7 @@ class TestLayout < TestInteractive
tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border rounded', :Enter tmux.send_keys 'seq 100 | fzf --height ~5 --info=inline --border rounded', :Enter
expected = <<~OUTPUT expected = <<~OUTPUT
2 2
> 1 > 1
> < 100/100 > < 100/100
@@ -275,12 +275,12 @@ class TestLayout < TestInteractive
def test_fzf_multi_line def test_fzf_multi_line
tmux.send_keys %[(echo -en '0\\0'; echo -en '1\\n2\\0'; seq 1000) | fzf --read0 --multi --bind load:select-all --border rounded], :Enter tmux.send_keys %[(echo -en '0\\0'; echo -en '1\\n2\\0'; seq 1000) | fzf --read0 --multi --bind load:select-all --border rounded], :Enter
block = <<~BLOCK block = <<~BLOCK
998 998
999 999
1000 1000
1 1
2 2
>>0 >>0
3/3 (3) 3/3 (3)
> >
@@ -312,11 +312,11 @@ class TestLayout < TestInteractive
> >
3/3 (3) 3/3 (3)
>>0 >>0
1 1
2 2
1 1
2 2
3 3
BLOCK BLOCK
tmux.until { assert_block(block, it) } tmux.until { assert_block(block, it) }
end end
@@ -995,6 +995,8 @@ class TestLayout < TestInteractive
%[--header "$(seq 101 102)" --header-border sharp], %[--header "$(seq 101 102)" --header-border sharp],
%[--header "$(seq 101 102)" --header-border sharp --header-first], %[--header "$(seq 101 102)" --header-border sharp --header-first],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2], %[--header "$(seq 101 102)" --header-border sharp --header-lines 2],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --no-header-lines-border],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border none],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp], %[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --input-border sharp], %[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --input-border sharp],
%[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --no-input], %[--header "$(seq 101 102)" --header-border sharp --header-lines 2 --header-lines-border sharp --header-first --no-input],
@@ -1002,24 +1004,24 @@ class TestLayout < TestInteractive
%[--header "$(seq 101 102)" --style full:sharp --header-first] %[--header "$(seq 101 102)" --style full:sharp --header-first]
] ]
output = <<~BLOCK output = <<~BLOCK
201 201 201 201 201 201 201 201 201 201 201 FOOT FOOT 201 201 201 201 201 201 201 201 201 201 201 201 201 FOOT FOOT
202 202 202 202 202 202 202 202 202 202 202 201 201 202 202 202 202 202 202 202 202 202 202 202 202 202 201 201
FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT 202 202 FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT FOOT 202 202
3 3 3 > 3 > 3 3 3 > 3 > 3 > 3 > 3 3 3 3 > 3 > 3 3 3 > 3 > 3 > 3 > 3 > 3 > 3
2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 3
> 1 > 1 > 1 1 1 > 1 > 1 2 2 2 2 2 3 > 1 > 1 > 1 1 1 > 1 > 1 2 2 1 2 2 2 2 3
3/3 101 3/3 101 1/1 3/3 1 1 1 1 > 1 2 3/3 101 3/3 101 1/1 3/3 1 1 1 1 1 > 1 2
> 102 > 102 > 101 > 101 101 > 1 > 102 > 102 > 101 > 101 101 101 101 > 1
3/3 101 1/1 101 102 102 102 3/3 101 1/1 101 102 102 102 102 102
> 102 > 102 HEAD 101 HEAD 101 1/1 101 > 102 > 102 HEAD 101 HEAD HEAD HEAD 101 1/1 101
3/3 102 1/1 102 > 102 3/3 > 3/3 102 1/1 1/1 1/1 102 > 102 3/3 >
> HEAD > HEAD HEAD > > HEAD > > > HEAD HEAD >
1/1 1/1
> 101 101 > 101 101
102 102 102 102
HEAD HEAD HEAD HEAD
BLOCK BLOCK
expects = [] expects = []
@@ -1154,6 +1156,42 @@ class TestLayout < TestInteractive
tmux.until { assert_block(block, it) } tmux.until { assert_block(block, it) }
end end
def test_gutter_default
tmux.send_keys %(seq 10 | fzf), :Enter
block = <<~BLOCK
3
2
> 1
10/10
>
BLOCK
tmux.until { assert_block(block, it) }
end
def test_gutter_default_no_unicode
tmux.send_keys %(seq 10 | fzf --no-unicode), :Enter
block = <<~BLOCK
3
2
> 1
10/10
>
BLOCK
tmux.until { assert_block(block, it) }
end
def test_gutter_custom
tmux.send_keys %(seq 10 | fzf --gutter x), :Enter
block = <<~BLOCK
x 3
x 2
> 1
10/10
>
BLOCK
tmux.until { assert_block(block, it) }
end
def test_combinations def test_combinations
skip unless ENV['LONGTEST'] skip unless ENV['LONGTEST']

View File

@@ -190,17 +190,17 @@ class TestPreview < TestInteractive
end end
def test_preview_asterisk def test_preview_asterisk
tmux.send_keys %(seq 5 | #{FZF} --multi --preview 'echo [{} / {+} / {*}]' --preview-window '+{1}'), :Enter tmux.send_keys %(seq 5 | #{FZF} --multi --preview 'echo [{}/{+}/{*}/{*n}]' --preview-window '+{1}'), :Enter
tmux.until { |lines| assert_equal 5, lines.match_count } tmux.until { |lines| assert_equal 5, lines.match_count }
tmux.until { |lines| assert_includes lines[1], ' [1 / 1 / 1 2 3 4 5] ' } tmux.until { |lines| assert_includes lines[1], ' [1/1/1 2 3 4 5/0 1 2 3 4] ' }
tmux.send_keys :BTab tmux.send_keys :BTab
tmux.until { |lines| assert_includes lines[1], ' [2 / 1 / 1 2 3 4 5] ' } tmux.until { |lines| assert_includes lines[1], ' [2/1/1 2 3 4 5/0 1 2 3 4] ' }
tmux.send_keys :BTab tmux.send_keys :BTab
tmux.until { |lines| assert_includes lines[1], ' [3 / 1 2 / 1 2 3 4 5] ' } tmux.until { |lines| assert_includes lines[1], ' [3/1 2/1 2 3 4 5/0 1 2 3 4] ' }
tmux.send_keys '5' tmux.send_keys '5'
tmux.until { |lines| assert_includes lines[1], ' [5 / 1 2 / 5] ' } tmux.until { |lines| assert_includes lines[1], ' [5/1 2/5/4] ' }
tmux.send_keys '5' tmux.send_keys '5'
tmux.until { |lines| assert_includes lines[1], ' [ / 1 2 / ] ' } tmux.until { |lines| assert_includes lines[1], ' [/1 2//] ' }
end end
def test_preview_file def test_preview_file