mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-10 20:33:48 -05:00
Compare commits
125 Commits
v0.62.0
...
adjust-def
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afc955771c | ||
|
|
c134c6f898 | ||
|
|
95697b96f8 | ||
|
|
3809ebb0c8 | ||
|
|
ca9b4d5a49 | ||
|
|
04888f5b94 | ||
|
|
3072b46218 | ||
|
|
a67aa85820 | ||
|
|
c5cabe1691 | ||
|
|
cbed41cd82 | ||
|
|
6684771cbf | ||
|
|
f5f894ea47 | ||
|
|
a0a334fc8d | ||
|
|
ae12e94b1f | ||
|
|
9ed971cc90 | ||
|
|
129cb23078 | ||
|
|
d22812e917 | ||
|
|
10d712824a | ||
|
|
de4059c8fa | ||
|
|
416aff86e9 | ||
|
|
59dc7f178f | ||
|
|
a3c9f8bfee | ||
|
|
5546c65491 | ||
|
|
f2179f015c | ||
|
|
9a53d84b9c | ||
|
|
0a8ff7899c | ||
|
|
f9d7877d8b | ||
|
|
9fe9976591 | ||
|
|
de1824f71d | ||
|
|
19a9296c47 | ||
|
|
49967f3d45 | ||
|
|
978b6254c7 | ||
|
|
1afd143810 | ||
|
|
e5cd7f0a3a | ||
|
|
51d3940c63 | ||
|
|
179aec1578 | ||
|
|
af0014aba8 | ||
|
|
da3d995709 | ||
|
|
04c4269db3 | ||
|
|
78f238294f | ||
|
|
354d0468c1 | ||
|
|
4efcc344c3 | ||
|
|
5818b58350 | ||
|
|
7941129cc4 | ||
|
|
069d71a840 | ||
|
|
08027e7a79 | ||
|
|
ead302981c | ||
|
|
fe0ffa14ff | ||
|
|
821b8e70a8 | ||
|
|
8ceda54c7d | ||
|
|
84e515bd6e | ||
|
|
dea1df6878 | ||
|
|
0076ec2e8d | ||
|
|
82c9671f79 | ||
|
|
d364a1122e | ||
|
|
fb570e94e7 | ||
|
|
6e3c830cd2 | ||
|
|
d7db7fc132 | ||
|
|
ff1550bb38 | ||
|
|
976001e474 | ||
|
|
531dd6fb4f | ||
|
|
ba035f2a76 | ||
|
|
d34675d3c9 | ||
|
|
ce95adc66c | ||
|
|
397fe8e395 | ||
|
|
111266d832 | ||
|
|
19d858f9b6 | ||
|
|
79690724d8 | ||
|
|
5ed87ffcb9 | ||
|
|
b99cb6323f | ||
|
|
debf3d8a8a | ||
|
|
4811e52af3 | ||
|
|
8d81730ec2 | ||
|
|
330a85c25c | ||
|
|
3a21116307 | ||
|
|
247d168af6 | ||
|
|
b2a8a283c7 | ||
|
|
c36ddce36f | ||
|
|
c35d9cff7d | ||
|
|
549ce3cf6c | ||
|
|
575bc0768c | ||
|
|
89334e881e | ||
|
|
dcec6354f5 | ||
|
|
16d338da84 | ||
|
|
27258f7207 | ||
|
|
4d2d6a5ced | ||
|
|
0c00b203e6 | ||
|
|
3b68dcdd81 | ||
|
|
39db026161 | ||
|
|
f6c589c606 | ||
|
|
2bd29c3172 | ||
|
|
4a61f53b85 | ||
|
|
adc9ad28da | ||
|
|
585cfaef8b | ||
|
|
b5cd8880b1 | ||
|
|
44ddab881e | ||
|
|
bfa287b66d | ||
|
|
243e52fa11 | ||
|
|
c166eaba6d | ||
|
|
09194c24f2 | ||
|
|
ec521e47aa | ||
|
|
e3f4a51c18 | ||
|
|
0a06fd6f63 | ||
|
|
70eace5290 | ||
|
|
40f9f254a9 | ||
|
|
15d6c17390 | ||
|
|
a9d1d42436 | ||
|
|
1ecfa38eee | ||
|
|
54fd92b7dd | ||
|
|
835906d392 | ||
|
|
1721e6a1ed | ||
|
|
c7ee3b833f | ||
|
|
ffb6e28ca7 | ||
|
|
a4c6846851 | ||
|
|
d18c0bf694 | ||
|
|
4e3f9854e6 | ||
|
|
b27943423e | ||
|
|
894a1016bc | ||
|
|
efe6cddd34 | ||
|
|
f1c6bdf3e8 | ||
|
|
710659bcf5 | ||
|
|
be67775da4 | ||
|
|
2c6381499c | ||
|
|
4df842e78c | ||
|
|
b81696fb64 |
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -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
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/depsreview.yaml
vendored
2
.github/workflows/depsreview.yaml
vendored
@@ -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
|
||||||
|
|||||||
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: Test fzf on Linux
|
name: build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -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
|
||||||
|
|||||||
4
.github/workflows/macos.yml
vendored
4
.github/workflows/macos.yml
vendored
@@ -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
|
||||||
|
|||||||
2
.github/workflows/sponsors.yml
vendored
2
.github/workflows/sponsors.yml
vendored
@@ -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
|
||||||
|
|||||||
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ builds:
|
|||||||
- windows
|
- windows
|
||||||
- freebsd
|
- freebsd
|
||||||
- openbsd
|
- openbsd
|
||||||
|
- android
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm
|
- arm
|
||||||
@@ -22,9 +23,9 @@ builds:
|
|||||||
- ppc64le
|
- ppc64le
|
||||||
- s390x
|
- s390x
|
||||||
goarm:
|
goarm:
|
||||||
- 5
|
- "5"
|
||||||
- 6
|
- "6"
|
||||||
- 7
|
- "7"
|
||||||
flags:
|
flags:
|
||||||
- -trimpath
|
- -trimpath
|
||||||
ldflags:
|
ldflags:
|
||||||
@@ -38,6 +39,10 @@ builds:
|
|||||||
goarch: arm64
|
goarch: arm64
|
||||||
- goos: openbsd
|
- goos: openbsd
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
|
- goos: android
|
||||||
|
goarch: amd64
|
||||||
|
- goos: android
|
||||||
|
goarch: arm
|
||||||
|
|
||||||
# .goreleaser.yaml
|
# .goreleaser.yaml
|
||||||
notarize:
|
notarize:
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
golang 1.20.13
|
golang 1.23.12
|
||||||
ruby 3.4.1
|
ruby 3.4.1
|
||||||
|
|||||||
16
BUILD.md
16
BUILD.md
@@ -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
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|||||||
174
CHANGELOG.md
174
CHANGELOG.md
@@ -1,6 +1,180 @@
|
|||||||
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
|
||||||
|
------
|
||||||
|
- Added `multi` event that is triggered when the multi-selection has changed.
|
||||||
|
```sh
|
||||||
|
fzf --multi \
|
||||||
|
--bind 'ctrl-a:select-all,ctrl-d:deselect-all' \
|
||||||
|
--bind 'multi:transform-footer:(( FZF_SELECT_COUNT )) && echo "Selected $FZF_SELECT_COUNT item(s)"'
|
||||||
|
```
|
||||||
|
- [Halfwidth and fullwidth alphanumeric and punctuation characters](https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)) are now internally normalized to their ASCII equivalents to allow matching with ASCII queries.
|
||||||
|
```sh
|
||||||
|
echo ABC| fzf -q abc
|
||||||
|
```
|
||||||
|
- Renamed `clear-selection` action to `clear-multi` for consistency.
|
||||||
|
- `clear-selection` remains supported as an alias for backward compatibility.
|
||||||
|
- Bug fixes
|
||||||
|
- Fixed a bug that could cause fzf to abort due to incorrect update ordering.
|
||||||
|
- Fixed a bug where some multi-selections were lost when using `exclude` or `change-nth`.
|
||||||
|
|
||||||
|
0.63.0
|
||||||
|
------
|
||||||
|
_Release highlights: https://junegunn.github.io/fzf/releases/0.63.0/_
|
||||||
|
|
||||||
|
- Added footer. The default border style for footer is `line`, which draws a single separator line.
|
||||||
|
```sh
|
||||||
|
fzf --reverse --footer "fzf: friend zone forever"
|
||||||
|
```
|
||||||
|
- Options
|
||||||
|
- `--footer[=STRING]`
|
||||||
|
- `--footer-border[=STYLE]`
|
||||||
|
- `--footer-label=LABEL`
|
||||||
|
- `--footer-label-pos=COL[:bottom]`
|
||||||
|
- Colors
|
||||||
|
- `footer`
|
||||||
|
- `footer-bg`
|
||||||
|
- `footer-border`
|
||||||
|
- `footer-label`
|
||||||
|
- Actions
|
||||||
|
- `change-footer`
|
||||||
|
- `transform-footer`
|
||||||
|
- `bg-transform-footer`
|
||||||
|
- `change-footer-label`
|
||||||
|
- `transform-footer-label`
|
||||||
|
- `bg-transform-footer-label`
|
||||||
|
- `line` border style is now allowed for all types of border except for `--list-border`.
|
||||||
|
```sh
|
||||||
|
fzf --height 50% --style full:line --preview 'cat {}' \
|
||||||
|
--bind 'focus:bg-transform-header(file {})+bg-transform-footer(wc {})'
|
||||||
|
```
|
||||||
|
- Added `{*}` placeholder flag that evaluates to all matched items.
|
||||||
|
```bash
|
||||||
|
seq 10000 | fzf --preview "awk '{sum += \$1} END {print sum}' {*f}"
|
||||||
|
```
|
||||||
|
- Use this with caution, as it can make fzf sluggish for large lists.
|
||||||
|
- Added asynchronous transform actions with `bg-` prefix that run asynchronously in the background, along with `bg-cancel` action to cancel currently running `bg-transform` actions.
|
||||||
|
```sh
|
||||||
|
# Implement popup that disappears after 1 second
|
||||||
|
# * Use footer as the popup
|
||||||
|
# * Use `bell` to ring the terminal bell
|
||||||
|
# * Use `bg-transform-footer` to clear the footer after 1 second
|
||||||
|
# * Use `bg-cancel` to cancel currently running background transform actions
|
||||||
|
fzf --multi --list-border \
|
||||||
|
--bind 'enter:execute-silent(echo -n {+} | pbcopy)+bell' \
|
||||||
|
--bind 'enter:+transform-footer(echo Copied {} to clipboard)' \
|
||||||
|
--bind 'enter:+bg-cancel+bg-transform-footer(sleep 1)'
|
||||||
|
|
||||||
|
# It's okay for the commands to take a little while because they run in the background
|
||||||
|
GETTER='curl -s http://metaphorpsum.com/sentences/1'
|
||||||
|
fzf --style full --border --preview : \
|
||||||
|
--bind "focus:bg-transform-header:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-footer:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-border-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-preview-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-input-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-list-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-header-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-footer-label:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-ghost:$GETTER" \
|
||||||
|
--bind "focus:+bg-transform-prompt:$GETTER"
|
||||||
|
```
|
||||||
|
- Added support for full-line background color in the list section
|
||||||
|
```sh
|
||||||
|
for i in $(seq 16 255); do
|
||||||
|
echo -e "\x1b[48;5;${i}m\x1b[0Khello"
|
||||||
|
done | fzf --ansi
|
||||||
|
```
|
||||||
|
- SSH completion enhancements by @akinomyoga
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
0.62.0
|
0.62.0
|
||||||
------
|
------
|
||||||
- Relaxed the `--color` option syntax to allow whitespace-separated entries (in addition to commas), making multi-line definitions easier to write and read
|
- Relaxed the `--color` option syntax to allow whitespace-separated entries (in addition to commas), making multi-line definitions easier to write and read
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -493,4 +493,4 @@ autocmd FileType fzf set laststatus=0 noshowmode noruler
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|||||||
33
SECURITY.md
Normal file
33
SECURITY.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Security Reporting
|
||||||
|
|
||||||
|
If you wish to report a security vulnerability privately, we appreciate your diligence. Please follow the guidelines below to submit your report.
|
||||||
|
|
||||||
|
## Reporting
|
||||||
|
|
||||||
|
To report a security vulnerability, please provide the following information:
|
||||||
|
|
||||||
|
1. **PROJECT**
|
||||||
|
- https://github.com/junegunn/fzf
|
||||||
|
|
||||||
|
2. **PUBLIC**
|
||||||
|
- Indicate whether this vulnerability has already been publicly discussed or disclosed.
|
||||||
|
- If so, provide relevant links.
|
||||||
|
|
||||||
|
3. **DESCRIPTION**
|
||||||
|
- Provide a detailed description of the security vulnerability.
|
||||||
|
- Include as much information as possible to help us understand and address the issue.
|
||||||
|
|
||||||
|
Send this information, along with any additional relevant details, to <junegunn.c AT gmail DOT com>.
|
||||||
|
|
||||||
|
## Confidentiality
|
||||||
|
|
||||||
|
We kindly ask you to keep the report confidential until a public announcement is made.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Vulnerabilities will be handled on a best-effort basis.
|
||||||
|
- You may request an advance copy of the patched release, but we cannot guarantee early access before the public release.
|
||||||
|
- You will be notified via email simultaneously with the public announcement.
|
||||||
|
- We will respond within a few weeks to confirm whether your report has been accepted or rejected.
|
||||||
|
|
||||||
|
Thank you for helping to improve the security of our project!
|
||||||
@@ -503,7 +503,7 @@ LICENSE *fzf-license*
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
||||||
|
|||||||
12
go.mod
12
go.mod
@@ -1,20 +1,20 @@
|
|||||||
module github.com/junegunn/fzf
|
module github.com/junegunn/fzf
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charlievieth/fastwalk v1.0.10
|
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
52
go.sum
@@ -1,10 +1,9 @@
|
|||||||
github.com/charlievieth/fastwalk v1.0.10 h1:0qUbvA2O+K+X+IrTfZTC0UH2DK5MOA+KjVfStAHUnGg=
|
github.com/charlievieth/fastwalk v1.0.14 h1:3Eh5uaFGwHZd8EGwTjJnSpBkfwfsak9h6ICgnWlhAyg=
|
||||||
github.com/charlievieth/fastwalk v1.0.10/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=
|
||||||
|
|||||||
41
install
41
install
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.62.0
|
version=0.65.2
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
@@ -164,28 +164,29 @@ download() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Try to download binary executable
|
# Try to download binary executable
|
||||||
archi=$(uname -sm)
|
archi=$(uname -smo 2>/dev/null || uname -sm)
|
||||||
binary_available=1
|
binary_available=1
|
||||||
binary_error=""
|
binary_error=""
|
||||||
case "$archi" in
|
case "$archi" in
|
||||||
Darwin\ arm64) download fzf-$version-darwin_arm64.tar.gz ;;
|
Darwin\ arm64*) download fzf-$version-darwin_arm64.tar.gz ;;
|
||||||
Darwin\ x86_64) download fzf-$version-darwin_amd64.tar.gz ;;
|
Darwin\ x86_64*) download fzf-$version-darwin_amd64.tar.gz ;;
|
||||||
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
|
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
|
||||||
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
|
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
|
||||||
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
|
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
|
||||||
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
|
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||||
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
|
Linux\ aarch64\ Android) download fzf-$version-android_arm64.tar.gz ;;
|
||||||
Linux\ loongarch64) download fzf-$version-linux_loong64.tar.gz ;;
|
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
|
||||||
Linux\ ppc64le) download fzf-$version-linux_ppc64le.tar.gz ;;
|
Linux\ loongarch64*) download fzf-$version-linux_loong64.tar.gz ;;
|
||||||
Linux\ *64) download fzf-$version-linux_amd64.tar.gz ;;
|
Linux\ ppc64le*) download fzf-$version-linux_ppc64le.tar.gz ;;
|
||||||
Linux\ s390x) download fzf-$version-linux_s390x.tar.gz ;;
|
Linux\ *64*) download fzf-$version-linux_amd64.tar.gz ;;
|
||||||
FreeBSD\ *64) download fzf-$version-freebsd_amd64.tar.gz ;;
|
Linux\ s390x*) download fzf-$version-linux_s390x.tar.gz ;;
|
||||||
OpenBSD\ *64) download fzf-$version-openbsd_amd64.tar.gz ;;
|
FreeBSD\ *64*) download fzf-$version-freebsd_amd64.tar.gz ;;
|
||||||
CYGWIN*\ *64) download fzf-$version-windows_amd64.zip ;;
|
OpenBSD\ *64*) download fzf-$version-openbsd_amd64.tar.gz ;;
|
||||||
MINGW*\ *64) download fzf-$version-windows_amd64.zip ;;
|
CYGWIN*\ *64*) download fzf-$version-windows_amd64.zip ;;
|
||||||
MSYS*\ *64) download fzf-$version-windows_amd64.zip ;;
|
MINGW*\ *64*) download fzf-$version-windows_amd64.zip ;;
|
||||||
Windows*\ *64) download fzf-$version-windows_amd64.zip ;;
|
MSYS*\ *64*) download fzf-$version-windows_amd64.zip ;;
|
||||||
*) binary_available=0 binary_error=1 ;;
|
Windows*\ *64*) download fzf-$version-windows_amd64.zip ;;
|
||||||
|
*) binary_available=0 binary_error=1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
cd "$fzf_base"
|
cd "$fzf_base"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
$version="0.62.0"
|
$version="0.65.2"
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "0.62"
|
var version = "0.65"
|
||||||
var revision = "devel"
|
var revision = "devel"
|
||||||
|
|
||||||
//go:embed shell/key-bindings.bash
|
//go:embed shell/key-bindings.bash
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.ig
|
.ig
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -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 "May 2025" "fzf 0.62.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
|
||||||
|
|||||||
258
man/man1/fzf.1
258
man/man1/fzf.1
@@ -1,7 +1,7 @@
|
|||||||
.ig
|
.ig
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -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 "May 2025" "fzf 0.62.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:
|
||||||
@@ -262,8 +262,9 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
|
|||||||
\fBlist\-bg \fRList section background
|
\fBlist\-bg \fRList section background
|
||||||
\fBselected\-bg \fRSelected line background
|
\fBselected\-bg \fRSelected line background
|
||||||
\fBpreview\-bg \fRPreview window background
|
\fBpreview\-bg \fRPreview window background
|
||||||
\fBinput\-bg \fRInput window background (\fB\-\-input\-border\fR)
|
\fBinput\-bg \fRInput window background
|
||||||
\fBheader\-bg \fRHeader window background (\fB\-\-header\-border\fR)
|
\fBheader\-bg \fRHeader window background
|
||||||
|
\fBfooter\-bg \fRFooter window background
|
||||||
\fBhl \fRHighlighted substrings
|
\fBhl \fRHighlighted substrings
|
||||||
\fBselected\-hl \fRHighlighted substrings in the selected line
|
\fBselected\-hl \fRHighlighted substrings in the selected line
|
||||||
\fBcurrent\-fg (fg+) \fRText (current line)
|
\fBcurrent\-fg (fg+) \fRText (current line)
|
||||||
@@ -272,6 +273,7 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
|
|||||||
\fBcurrent\-hl (hl+) \fRHighlighted substrings (current line)
|
\fBcurrent\-hl (hl+) \fRHighlighted substrings (current line)
|
||||||
\fBalt\-bg \fRAlternate background color to create striped lines
|
\fBalt\-bg \fRAlternate background color to create striped lines
|
||||||
\fBquery (input\-fg) \fRQuery string
|
\fBquery (input\-fg) \fRQuery string
|
||||||
|
\fBghost \fRGhost text (\fB\-\-ghost\fR, \fBdim\fR applied by default)
|
||||||
\fBdisabled \fRQuery string when search is disabled (\fB\-\-disabled\fR)
|
\fBdisabled \fRQuery string when search is disabled (\fB\-\-disabled\fR)
|
||||||
\fBinfo \fRInfo line (match counters)
|
\fBinfo \fRInfo line (match counters)
|
||||||
\fBborder \fRBorder around the window (\fB\-\-border\fR and \fB\-\-preview\fR)
|
\fBborder \fRBorder around the window (\fB\-\-border\fR and \fB\-\-preview\fR)
|
||||||
@@ -283,16 +285,19 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
|
|||||||
\fBpreview\-scrollbar \fRScrollbar
|
\fBpreview\-scrollbar \fRScrollbar
|
||||||
\fBinput\-border \fRBorder around the input window (\fB\-\-input\-border\fR)
|
\fBinput\-border \fRBorder around the input window (\fB\-\-input\-border\fR)
|
||||||
\fBheader\-border \fRBorder around the header window (\fB\-\-header\-border\fR)
|
\fBheader\-border \fRBorder around the header window (\fB\-\-header\-border\fR)
|
||||||
|
\fBfooter\-border \fRBorder around the footer window (\fB\-\-footer\-border\fR)
|
||||||
\fBlabel \fRBorder label (\fB\-\-border\-label\fR, \fB\-\-list\-label\fR, \fB\-\-input\-label\fR, and \fB\-\-preview\-label\fR)
|
\fBlabel \fRBorder label (\fB\-\-border\-label\fR, \fB\-\-list\-label\fR, \fB\-\-input\-label\fR, and \fB\-\-preview\-label\fR)
|
||||||
\fBlist\-label \fRBorder label of the list section (\fB\-\-list\-label\fR)
|
\fBlist\-label \fRBorder label of the list section (\fB\-\-list\-label\fR)
|
||||||
\fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
|
\fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
|
||||||
\fBinput\-label \fRBorder label of the input window (\fB\-\-input\-label\fR)
|
\fBinput\-label \fRBorder label of the input window (\fB\-\-input\-label\fR)
|
||||||
\fBheader\-label \fRBorder label of the header window (\fB\-\-header\-label\fR)
|
\fBheader\-label \fRBorder label of the header window (\fB\-\-header\-label\fR)
|
||||||
|
\fBfooter\-label \fRBorder label of the footer window (\fB\-\-footer\-label\fR)
|
||||||
\fBprompt \fRPrompt
|
\fBprompt \fRPrompt
|
||||||
\fBpointer \fRPointer to the current line
|
\fBpointer \fRPointer to the current line
|
||||||
\fBmarker \fRMulti\-select marker
|
\fBmarker \fRMulti\-select marker
|
||||||
\fBspinner \fRStreaming input indicator
|
\fBspinner \fRStreaming input indicator
|
||||||
\fBheader (header\-fg) \fRHeader
|
\fBheader (header\-fg) \fRHeader
|
||||||
|
\fBfooter (footer\-fg) \fRFooter
|
||||||
\fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes)
|
\fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes)
|
||||||
|
|
||||||
.B ANSI COLORS:
|
.B ANSI COLORS:
|
||||||
@@ -502,6 +507,8 @@ Draw border around the finder
|
|||||||
.br
|
.br
|
||||||
.BR vertical " Vertical lines on each side of the finder"
|
.BR vertical " Vertical lines on each side of the finder"
|
||||||
.br
|
.br
|
||||||
|
.BR line " Single line border (position automatically determined)"
|
||||||
|
.br
|
||||||
.BR top " (up)"
|
.BR top " (up)"
|
||||||
.br
|
.br
|
||||||
.BR bottom " (down)"
|
.BR bottom " (down)"
|
||||||
@@ -517,6 +524,9 @@ If you use a terminal emulator where each box-drawing character takes
|
|||||||
2 columns, try setting \fB\-\-ambidouble\fR. If the border is still not properly
|
2 columns, try setting \fB\-\-ambidouble\fR. If the border is still not properly
|
||||||
rendered, set \fB\-\-no\-unicode\fR.
|
rendered, set \fB\-\-no\-unicode\fR.
|
||||||
|
|
||||||
|
\fBline\fR style draws a single separator line at the top when \fB\-\-height\fR
|
||||||
|
is used.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-border\-label" [=LABEL]
|
.BI "\-\-border\-label" [=LABEL]
|
||||||
Label to print on the horizontal border line. Should be used with one of the
|
Label to print on the horizontal border line. Should be used with one of the
|
||||||
@@ -660,7 +670,8 @@ Do not display scrollbar. A synonym for \fB\-\-scrollbar=''\fB
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-list\-border" [=STYLE]
|
.BI "\-\-list\-border" [=STYLE]
|
||||||
Draw border around the list section
|
Draw border around the list section. \fBline\fR style is not supported for
|
||||||
|
this border.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-list\-label" [=LABEL]
|
.BI "\-\-list\-label" [=LABEL]
|
||||||
@@ -747,7 +758,8 @@ actions are affected:
|
|||||||
\fBkill\-word\fR
|
\fBkill\-word\fR
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-input\-border" [=STYLE]
|
.BI "\-\-input\-border" [=STYLE]
|
||||||
Draw border around the input section
|
Draw border around the input section. \fBline\fR style draws a single separator
|
||||||
|
line between the input section and the list section.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-input\-label" [=LABEL]
|
.BI "\-\-input\-label" [=LABEL]
|
||||||
@@ -781,13 +793,16 @@ fzf also exports \fB$FZF_PREVIEW_TOP\fR and \fB$FZF_PREVIEW_LEFT\fR so that
|
|||||||
the preview command can determine the position of the preview window.
|
the preview command can determine the position of the preview window.
|
||||||
|
|
||||||
A placeholder expression starting with \fB+\fR flag will be replaced to the
|
A placeholder expression starting with \fB+\fR flag will be replaced to the
|
||||||
space-separated list of the selected lines (or the current line if no selection
|
space-separated list of the selected items (or the current item if no selection
|
||||||
was made) individually quoted.
|
was made) individually quoted.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
\fBfzf \-\-multi \-\-preview='head \-10 {+}'
|
\fBfzf \-\-multi \-\-preview='head \-10 {+}'
|
||||||
git log \-\-oneline | fzf \-\-multi \-\-preview 'git show {+1}'\fR
|
git log \-\-oneline | fzf \-\-multi \-\-preview 'git show {+1}'\fR
|
||||||
|
|
||||||
|
Similarly, a placeholder expression starting with \fB*\fR flag will be replaced
|
||||||
|
to the space-separated list of all matched items individually quoted.
|
||||||
|
|
||||||
Each expression expands to a quoted string, so that it's safe to pass it as an
|
Each expression expands to a quoted string, so that it's safe to pass it as an
|
||||||
argument to an external command. So you should not manually add quotes around
|
argument to an external command. So you should not manually add quotes around
|
||||||
the curly braces. But if you don't want this behavior, you can put
|
the curly braces. But if you don't want this behavior, you can put
|
||||||
@@ -799,14 +814,13 @@ from the replacement string. To preserve the whitespace, use the \fBs\fR flag.
|
|||||||
|
|
||||||
A placeholder expression with \fBf\fR flag is replaced to the path of
|
A placeholder expression with \fBf\fR flag is replaced to the path of
|
||||||
a temporary file that holds the evaluated list. This is useful when you
|
a temporary file that holds the evaluated list. This is useful when you
|
||||||
multi-select a large number of items and the length of the evaluated string may
|
pass a large number of items and the length of the evaluated string may
|
||||||
exceed \fBARG_MAX\fR.
|
exceed \fBARG_MAX\fR.
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
\fB# Press CTRL\-A to select 100K items and see the sum of all the numbers.
|
\fB# See the sum of all the matched numbers
|
||||||
# This won't work properly without 'f' flag due to ARG_MAX limit.
|
# This won't work properly without 'f' flag due to ARG_MAX limit.
|
||||||
seq 100000 | fzf \-\-multi \-\-bind ctrl\-a:select\-all \\
|
seq 100000 | fzf \-\-preview "awk '{sum+=\\$1} END {print sum}' {*f}"\fR
|
||||||
\-\-preview "awk '{sum+=\\$1} END {print sum}' {+f}"\fR
|
|
||||||
|
|
||||||
Also,
|
Also,
|
||||||
|
|
||||||
@@ -847,8 +861,7 @@ e.g.
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-preview\-border" [=STYLE]
|
.BI "\-\-preview\-border" [=STYLE]
|
||||||
Short for \fB\-\-preview\-window=border\-STYLE\fR. In addition to the other
|
Short for \fB\-\-preview\-window=border\-STYLE\fR. \fBline\fR style draws
|
||||||
styles, \fBline\fR style is also supported for preview border, which draws
|
|
||||||
a single separator line between the preview window and the rest of the
|
a single separator line between the preview window and the rest of the
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
@@ -1000,10 +1013,12 @@ The first N lines of the input are treated as the sticky header. When
|
|||||||
lines that follow.
|
lines that follow.
|
||||||
.TP
|
.TP
|
||||||
.B "\-\-header\-first"
|
.B "\-\-header\-first"
|
||||||
Print header before the prompt line
|
Print header before the prompt line. When both normal header and header lines
|
||||||
|
(\fB\-\-header\-lines\fR) are present, this applies only to the normal header.
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-border" [=STYLE]
|
.BI "\-\-header\-border" [=STYLE]
|
||||||
Draw border around the header section
|
Draw border around the header section. \fBline\fR style draws a single
|
||||||
|
separator line between the header window and the list section.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-label" [=LABEL]
|
.BI "\-\-header\-label" [=LABEL]
|
||||||
@@ -1017,7 +1032,30 @@ Position of the header label
|
|||||||
.BI "\-\-header\-lines\-border" [=STYLE]
|
.BI "\-\-header\-lines\-border" [=STYLE]
|
||||||
Display header from \fB--header\-lines\fR with a separate border. Pass
|
Display header from \fB--header\-lines\fR with a separate border. Pass
|
||||||
\fBnone\fR to still separate the header lines but without a border. To combine
|
\fBnone\fR to still separate the header lines but without a border. To combine
|
||||||
two headers, use \fB\-\-no\-header\-lines\-border\fR.
|
two headers, use \fB\-\-no\-header\-lines\-border\fR. \fBline\fR style draws
|
||||||
|
a single separator line between the header lines and the list section.
|
||||||
|
|
||||||
|
.SS FOOTER
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer=" "STR"
|
||||||
|
The given string will be printed as the sticky footer. The lines are displayed
|
||||||
|
in the given order from top to bottom regardless of \fB\-\-layout\fR option, and
|
||||||
|
are not affected by \fB\-\-with\-nth\fR. ANSI color codes are processed even when
|
||||||
|
\fB\-\-ansi\fR is not set.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-border" [=STYLE]
|
||||||
|
Draw border around the footer section. \fBline\fR style draws a single
|
||||||
|
separator line between the footer and the list section.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-label" [=LABEL]
|
||||||
|
Label to print on the footer border
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-footer\-label\-pos" [=N[:top|bottom]]
|
||||||
|
Position of the footer label
|
||||||
|
|
||||||
.SS SCRIPTING
|
.SS SCRIPTING
|
||||||
.TP
|
.TP
|
||||||
@@ -1108,11 +1146,6 @@ e.g.
|
|||||||
# Send action to the server
|
# Send action to the server
|
||||||
curl \-XPOST localhost:6266 \-d 'reload(seq 100)+change\-prompt(hundred> )'
|
curl \-XPOST localhost:6266 \-d 'reload(seq 100)+change\-prompt(hundred> )'
|
||||||
|
|
||||||
# Get program state in JSON format (experimental)
|
|
||||||
# * Make sure NOT to access this endpoint from execute/transform actions
|
|
||||||
# as it will result in a timeout
|
|
||||||
curl localhost:6266
|
|
||||||
|
|
||||||
# Start HTTP server on port 6266 with remote connections allowed
|
# Start HTTP server on port 6266 with remote connections allowed
|
||||||
# * Listening on non-localhost address requires using an API key
|
# * Listening on non-localhost address requires using an API key
|
||||||
export FZF_API_KEY="$(head \-c 32 /dev/urandom | base64)"
|
export FZF_API_KEY="$(head \-c 32 /dev/urandom | base64)"
|
||||||
@@ -1123,6 +1156,24 @@ e.g.
|
|||||||
|
|
||||||
# Choose port automatically and export it as $FZF_PORT to the child process
|
# Choose port automatically and export it as $FZF_PORT to the child process
|
||||||
fzf \-\-listen \-\-bind 'start:execute\-silent:echo $FZF_PORT > /tmp/fzf\-port'
|
fzf \-\-listen \-\-bind 'start:execute\-silent:echo $FZF_PORT > /tmp/fzf\-port'
|
||||||
|
|
||||||
|
# Get program state in JSON format (experimental)
|
||||||
|
# - GET Parameters:
|
||||||
|
# - limit: number of items to return (default: 100)
|
||||||
|
# - offset: number of items to skip (default: 0)
|
||||||
|
curl localhost:6266
|
||||||
|
|
||||||
|
# Automatically select items with .txt extension
|
||||||
|
fzf \-\-multi \-\-sync \-\-listen \-\-bind 'load:transform:
|
||||||
|
pos=1
|
||||||
|
curl \-s localhost:$FZF_PORT?limit=1000 | jq \-r .matches[].text | while read \-r text; do
|
||||||
|
if [[ $text =~ \\.txt$ ]]; then
|
||||||
|
echo \-n "+pos($pos)+select"
|
||||||
|
fi
|
||||||
|
pos=$((pos + 1))
|
||||||
|
done
|
||||||
|
echo +first
|
||||||
|
'
|
||||||
\fR
|
\fR
|
||||||
|
|
||||||
.SS DIRECTORY TRAVERSAL
|
.SS DIRECTORY TRAVERSAL
|
||||||
@@ -1422,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)
|
||||||
@@ -1454,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
|
||||||
@@ -1462,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
|
||||||
@@ -1472,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
|
||||||
@@ -1496,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
|
||||||
@@ -1558,6 +1715,10 @@ e.g.
|
|||||||
# Beware not to introduce an infinite loop
|
# Beware not to introduce an infinite loop
|
||||||
seq 10 | fzf \-\-bind 'focus:up' \-\-cycle\fR
|
seq 10 | fzf \-\-bind 'focus:up' \-\-cycle\fR
|
||||||
.RE
|
.RE
|
||||||
|
\fImulti\fR
|
||||||
|
.RS
|
||||||
|
Triggered when the multi\-selection has changed.
|
||||||
|
.RE
|
||||||
|
|
||||||
\fIone\fR
|
\fIone\fR
|
||||||
.RS
|
.RS
|
||||||
@@ -1627,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.
|
||||||
|
|
||||||
@@ -1636,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)
|
||||||
@@ -1660,13 +1832,13 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBchange\-prompt(...)\fR (change prompt to the given string)
|
\fBchange\-prompt(...)\fR (change prompt to the given string)
|
||||||
\fBchange\-query(...)\fR (change query string to the given string)
|
\fBchange\-query(...)\fR (change query string to the given string)
|
||||||
\fBclear\-screen\fR \fIctrl\-l\fR
|
\fBclear\-screen\fR \fIctrl\-l\fR
|
||||||
\fBclear\-selection\fR (clear multi\-selection)
|
\fBclear\-multi\fR (clear multi\-selection)
|
||||||
\fBclose\fR (close preview window if open, abort fzf otherwise)
|
\fBclose\fR (close preview window if open, abort fzf otherwise)
|
||||||
\fBclear\-query\fR (clear query string)
|
\fBclear\-query\fR (clear query string)
|
||||||
\fBdelete\-char\fR \fIdel\fR
|
\fBdelete\-char\fR \fIdel\fR
|
||||||
\fBdelete\-char/eof\fR \fIctrl\-d\fR (same as \fBdelete\-char\fR except aborts fzf if query is empty)
|
\fBdelete\-char/eof\fR \fIctrl\-d\fR (same as \fBdelete\-char\fR except aborts fzf if query is empty)
|
||||||
\fBdeselect\fR
|
\fBdeselect\fR
|
||||||
\fBdeselect\-all\fR (deselect all matches)
|
\fBdeselect\-all\fR (deselect all matches; to also clear non-matched selections, use \fBclear\-multi\fR)
|
||||||
\fBdisable\-search\fR (disable search functionality)
|
\fBdisable\-search\fR (disable search functionality)
|
||||||
\fBdown\fR \fIctrl\-j ctrl\-n down\fR
|
\fBdown\fR \fIctrl\-j ctrl\-n down\fR
|
||||||
\fBenable\-search\fR (enable search functionality)
|
\fBenable\-search\fR (enable search functionality)
|
||||||
@@ -1677,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)
|
||||||
@@ -1753,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
|
||||||
@@ -1760,6 +1935,9 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBup\fR \fIctrl\-k ctrl\-p up\fR
|
\fBup\fR \fIctrl\-k ctrl\-p up\fR
|
||||||
\fByank\fR \fIctrl\-y\fR
|
\fByank\fR \fIctrl\-y\fR
|
||||||
|
|
||||||
|
Each \fBtransform*\fR action has a corresponding \fBbg\-transform*\fR
|
||||||
|
variant that runs the command in the background.
|
||||||
|
|
||||||
.SS ACTION COMPOSITION
|
.SS ACTION COMPOSITION
|
||||||
|
|
||||||
Multiple actions can be chained using \fB+\fR separator.
|
Multiple actions can be chained using \fB+\fR separator.
|
||||||
@@ -1884,6 +2062,26 @@ e.g.
|
|||||||
echo "change\-header:Invalid selection"'
|
echo "change\-header:Invalid selection"'
|
||||||
\fR
|
\fR
|
||||||
|
|
||||||
|
A common mistake when writing a \fBtransform\fR action is not escaping
|
||||||
|
placeholder expressions when passing them back to fzf. In the following
|
||||||
|
example, if you don't escape \fB{}\fR, fzf will immediately replace it with the
|
||||||
|
single-quoted string of the current item. This causes single quotes to appear
|
||||||
|
in the header and footer, and the script will break if any item contains
|
||||||
|
double-quote characters.
|
||||||
|
|
||||||
|
\fBfzf \-\-bind 'focus:transform:[[ $FZF_ACTION =~ up ]] &&
|
||||||
|
echo "change\-header()+transform\-footer:echo \\{}" ||
|
||||||
|
echo "change\-footer()+transform\-header:echo \\{}"'\fR
|
||||||
|
|
||||||
|
.SS TRANSFORM IN THE BACKGROUND
|
||||||
|
|
||||||
|
Transform actions are synchronous, meaning fzf becomes unresponsive while the
|
||||||
|
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
|
||||||
|
chain multiple transform actions where later ones depend on earlier results,
|
||||||
|
prefer using the \fBbg\fR variant. To cancel currently running background
|
||||||
|
transform processes, use \fBbg\-cancel\fR action.
|
||||||
|
|
||||||
.SS PREVIEW BINDING
|
.SS PREVIEW BINDING
|
||||||
|
|
||||||
With \fBpreview(...)\fR action, you can specify multiple different preview
|
With \fBpreview(...)\fR action, you can specify multiple different preview
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
" Copyright (c) 2013-2024 Junegunn Choi
|
" Copyright (c) 2013-2025 Junegunn Choi
|
||||||
"
|
"
|
||||||
" MIT License
|
" MIT License
|
||||||
"
|
"
|
||||||
@@ -553,8 +553,15 @@ try
|
|||||||
let height = s:calc_size(&lines, dict.down, dict)
|
let height = s:calc_size(&lines, dict.down, dict)
|
||||||
let optstr .= ' --no-tmux --height='.height
|
let optstr .= ' --no-tmux --height='.height
|
||||||
endif
|
endif
|
||||||
" Respect --border option given in $FZF_DEFAULT_OPTS and 'options'
|
|
||||||
let optstr = join([s:border_opt(get(dict, 'window', 0)), s:extract_option($FZF_DEFAULT_OPTS, 'border'), optstr])
|
if exists('&winborder') && &winborder !=# '' && &winborder !=# 'none'
|
||||||
|
" Add 1-column horizontal margin
|
||||||
|
let optstr = join(['--margin 0,1', optstr])
|
||||||
|
else
|
||||||
|
" Respect --border option given in $FZF_DEFAULT_OPTS and 'options'
|
||||||
|
let optstr = join([s:border_opt(get(dict, 'window', 0)), s:extract_option($FZF_DEFAULT_OPTS, 'border'), optstr])
|
||||||
|
endif
|
||||||
|
|
||||||
let command = prefix.(use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
|
let command = prefix.(use_tmux ? s:fzf_tmux(dict) : fzf_exec).' '.optstr.' > '.temps.result
|
||||||
|
|
||||||
if use_term
|
if use_term
|
||||||
@@ -1020,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
|
||||||
|
|||||||
38
shell/common.sh
Normal file
38
shell/common.sh
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
__fzf_defaults() {
|
||||||
|
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||||
|
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||||
|
printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
||||||
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
||||||
|
printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_exec_awk() {
|
||||||
|
# This function performs `exec awk "$@"` safely by working around awk
|
||||||
|
# compatibility issues.
|
||||||
|
#
|
||||||
|
# To reduce an extra fork, this function performs "exec" so is expected to be
|
||||||
|
# run as the last command in a subshell.
|
||||||
|
if [[ -z ${__fzf_awk-} ]]; then
|
||||||
|
__fzf_awk=awk
|
||||||
|
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
|
||||||
|
# Note: Solaris awk at /usr/bin/awk is meant for backward compatibility
|
||||||
|
# with an ancient implementation of 1977 awk in the original UNIX. It
|
||||||
|
# lacks many features of POSIX awk, so it is essentially useless in the
|
||||||
|
# modern point of view. To use a standard-conforming version in Solaris,
|
||||||
|
# one needs to explicitly use /usr/xpg4/bin/awk.
|
||||||
|
__fzf_awk=/usr/xpg4/bin/awk
|
||||||
|
elif command -v mawk >/dev/null 2>&1; then
|
||||||
|
# choose the faster mawk if: it's installed && build date >= 20230322 &&
|
||||||
|
# version >= 1.3.4
|
||||||
|
local n x y z d
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Note: macOS awk has a quirk that it stops processing at all when it sees
|
||||||
|
# any data not following UTF-8 in the input stream when the current LC_CTYPE
|
||||||
|
# specifies the UTF-8 encoding. To work around this quirk, one needs to
|
||||||
|
# specify LC_ALL=C to change the current encoding to the plain one.
|
||||||
|
LC_ALL=C exec "$__fzf_awk" "$@"
|
||||||
|
}
|
||||||
@@ -31,17 +31,32 @@ if [[ $- =~ i ]]; then
|
|||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
# To redraw line after fzf closes (printf '\e[5n')
|
#----BEGIN INCLUDE common.sh
|
||||||
bind '"\e[0n": redraw-current-line' 2> /dev/null
|
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||||
|
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
|
||||||
|
# the changes. See code comments in "common.sh" for the implementation details.
|
||||||
|
|
||||||
__fzf_defaults() {
|
__fzf_defaults() {
|
||||||
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
||||||
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
||||||
echo "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
|
||||||
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
||||||
echo "${FZF_DEFAULT_OPTS-} $2"
|
printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__fzf_exec_awk() {
|
||||||
|
if [[ -z ${__fzf_awk-} ]]; then
|
||||||
|
__fzf_awk=awk
|
||||||
|
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
|
||||||
|
__fzf_awk=/usr/xpg4/bin/awk
|
||||||
|
elif command -v mawk >/dev/null 2>&1; then
|
||||||
|
local n x y z d
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LC_ALL=C exec "$__fzf_awk" "$@"
|
||||||
|
}
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
__fzf_comprun() {
|
__fzf_comprun() {
|
||||||
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
|
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
|
||||||
_fzf_comprun "$@"
|
_fzf_comprun "$@"
|
||||||
@@ -328,6 +343,8 @@ __fzf_generic_path_completion() {
|
|||||||
else
|
else
|
||||||
COMPREPLY=( "$cur" )
|
COMPREPLY=( "$cur" )
|
||||||
fi
|
fi
|
||||||
|
# To redraw line after fzf closes (printf '\e[5n')
|
||||||
|
bind '"\e[0n": redraw-current-line' 2> /dev/null
|
||||||
printf '\e[5n'
|
printf '\e[5n'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -365,7 +382,7 @@ _fzf_complete() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local cur selected trigger cmd post
|
local cur selected trigger cmd post
|
||||||
post="$(caller 0 | command awk '{print $2}')_post"
|
post="$(caller 0 | __fzf_exec_awk '{print $2}')_post"
|
||||||
type -t "$post" > /dev/null 2>&1 || post='command cat'
|
type -t "$post" > /dev/null 2>&1 || post='command cat'
|
||||||
|
|
||||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
@@ -384,6 +401,7 @@ _fzf_complete() {
|
|||||||
else
|
else
|
||||||
COMPREPLY=("$cur")
|
COMPREPLY=("$cur")
|
||||||
fi
|
fi
|
||||||
|
bind '"\e[0n": redraw-current-line' 2> /dev/null
|
||||||
printf '\e[5n'
|
printf '\e[5n'
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@@ -443,7 +461,7 @@ _fzf_proc_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_proc_completion_post() {
|
_fzf_proc_completion_post() {
|
||||||
command awk '{print $2}'
|
__fzf_exec_awk '{print $2}'
|
||||||
}
|
}
|
||||||
|
|
||||||
# To use custom hostname lists, override __fzf_list_hosts.
|
# To use custom hostname lists, override __fzf_list_hosts.
|
||||||
@@ -460,10 +478,54 @@ _fzf_proc_completion_post() {
|
|||||||
# }
|
# }
|
||||||
if ! declare -F __fzf_list_hosts > /dev/null; then
|
if ! declare -F __fzf_list_hosts > /dev/null; then
|
||||||
__fzf_list_hosts() {
|
__fzf_list_hosts() {
|
||||||
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | command awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \
|
command sort -u \
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts 2> /dev/null | command tr ',' '\n' | command tr -d '[' | command awk '{ print $1 " " $1 }') \
|
<(
|
||||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts 2> /dev/null | command grep -Fv '0.0.0.0' | command sed 's/#.*//') |
|
# Note: To make the pathname expansion of "~/.ssh/config.d/*" work
|
||||||
command awk '{for (i = 2; i <= NF; i++) print $i}' | command sort -u
|
# properly, we need to adjust the related shell options. We need to
|
||||||
|
# unset "set -f" and "GLOBIGNORE", which disable the pathname expansion
|
||||||
|
# totally or partially. We need to unset "dotglob" and "nocaseglob" to
|
||||||
|
# avoid matching unwanted files. We need to unset "failglob" to avoid
|
||||||
|
# outputting the error messages to the terminal when no matching is
|
||||||
|
# found. We need to set "nullglob" to avoid attempting to read the
|
||||||
|
# literal filename '~/.ssh/config.d/*' when no matching is found.
|
||||||
|
set +f
|
||||||
|
GLOBIGNORE=
|
||||||
|
shopt -u dotglob nocaseglob failglob
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
__fzf_exec_awk '
|
||||||
|
# Note: mawk <= 1.3.3-20090705 does not support the POSIX brackets of
|
||||||
|
# the form [[:blank:]], and Ubuntu 18.04 LTS still uses this
|
||||||
|
# 16-year-old mawk unfortunately. We need to use [ \t] instead.
|
||||||
|
match(tolower($0), /^[ \t]*host(name)?[ \t]*[ \t=]/) {
|
||||||
|
$0 = substr($0, RLENGTH + 1) # Remove "Host(name)?=?"
|
||||||
|
sub(/#.*/, "")
|
||||||
|
for (i = 1; i <= NF; i++)
|
||||||
|
if ($i !~ /[*?%]/)
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null
|
||||||
|
) \
|
||||||
|
<(
|
||||||
|
__fzf_exec_awk -F ',' '
|
||||||
|
match($0, /^[][a-zA-Z0-9.,:-]+/) {
|
||||||
|
$0 = substr($0, 1, RLENGTH)
|
||||||
|
gsub(/[][]|:[^,]*/, "")
|
||||||
|
for (i = 1; i <= NF; i++)
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' ~/.ssh/known_hosts 2> /dev/null
|
||||||
|
) \
|
||||||
|
<(
|
||||||
|
__fzf_exec_awk '
|
||||||
|
{
|
||||||
|
sub(/#.*/, "")
|
||||||
|
for (i = 2; i <= NF; i++)
|
||||||
|
if ($i != "0.0.0.0")
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' /etc/hosts 2> /dev/null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -484,7 +546,7 @@ _fzf_complete_ssh() {
|
|||||||
*)
|
*)
|
||||||
local user=
|
local user=
|
||||||
[[ "$2" =~ '@' ]] && user="${2%%@*}@"
|
[[ "$2" =~ '@' ]] && user="${2%%@*}@"
|
||||||
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | command awk -v user="$user" '{print user $0}')
|
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}')
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -572,7 +634,7 @@ __fzf_defc() {
|
|||||||
if __fzf_orig_completion_instantiate "$cmd" "$func"; then
|
if __fzf_orig_completion_instantiate "$cmd" "$func"; then
|
||||||
eval "$REPLY"
|
eval "$REPLY"
|
||||||
else
|
else
|
||||||
complete -F "$func" $opts "$cmd"
|
eval "complete -F \"$func\" $opts \"$cmd\""
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,14 +96,32 @@ if [[ -o interactive ]]; then
|
|||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
|
#----BEGIN INCLUDE common.sh
|
||||||
|
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||||
|
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
|
||||||
|
# the changes. See code comments in "common.sh" for the implementation details.
|
||||||
|
|
||||||
__fzf_defaults() {
|
__fzf_defaults() {
|
||||||
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
||||||
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
||||||
echo -E "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
|
||||||
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
||||||
echo -E "${FZF_DEFAULT_OPTS-} $2"
|
printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__fzf_exec_awk() {
|
||||||
|
if [[ -z ${__fzf_awk-} ]]; then
|
||||||
|
__fzf_awk=awk
|
||||||
|
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
|
||||||
|
__fzf_awk=/usr/xpg4/bin/awk
|
||||||
|
elif command -v mawk >/dev/null 2>&1; then
|
||||||
|
local n x y z d
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LC_ALL=C exec "$__fzf_awk" "$@"
|
||||||
|
}
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
__fzf_comprun() {
|
__fzf_comprun() {
|
||||||
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
||||||
_fzf_comprun "$@"
|
_fzf_comprun "$@"
|
||||||
@@ -242,11 +260,50 @@ _fzf_complete() {
|
|||||||
# desired sorting and with any duplicates removed, to standard output.
|
# desired sorting and with any duplicates removed, to standard output.
|
||||||
if ! declare -f __fzf_list_hosts > /dev/null; then
|
if ! declare -f __fzf_list_hosts > /dev/null; then
|
||||||
__fzf_list_hosts() {
|
__fzf_list_hosts() {
|
||||||
setopt localoptions nonomatch
|
command sort -u \
|
||||||
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \
|
<(
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts 2> /dev/null | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
# Note: To make the pathname expansion of "~/.ssh/config.d/*" work
|
||||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts 2> /dev/null | command grep -Fv '0.0.0.0' | command sed 's/#.*//') |
|
# properly, we need to adjust the related shell options. We need to
|
||||||
awk '{for (i = 2; i <= NF; i++) print $i}' | sort -u
|
# unset "NO_GLOB" (or reset "GLOB"), which disable the pathname
|
||||||
|
# expansion totally. We need to unset "DOT_GLOB" and set "CASE_GLOB"
|
||||||
|
# to avoid matching unwanted files. We need to set "NULL_GLOB" to
|
||||||
|
# avoid attempting to read the literal filename '~/.ssh/config.d/*'
|
||||||
|
# when no matching is found.
|
||||||
|
setopt GLOB NO_DOT_GLOB CASE_GLOB NO_NOMATCH NULL_GLOB
|
||||||
|
|
||||||
|
__fzf_exec_awk '
|
||||||
|
# Note: mawk <= 1.3.3-20090705 does not support the POSIX brackets of
|
||||||
|
# the form [[:blank:]], and Ubuntu 18.04 LTS still uses this
|
||||||
|
# 16-year-old mawk unfortunately. We need to use [ \t] instead.
|
||||||
|
match(tolower($0), /^[ \t]*host(name)?[ \t]*[ \t=]/) {
|
||||||
|
$0 = substr($0, RLENGTH + 1) # Remove "Host(name)?=?"
|
||||||
|
sub(/#.*/, "")
|
||||||
|
for (i = 1; i <= NF; i++)
|
||||||
|
if ($i !~ /[*?%]/)
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null
|
||||||
|
) \
|
||||||
|
<(
|
||||||
|
__fzf_exec_awk -F ',' '
|
||||||
|
match($0, /^[][a-zA-Z0-9.,:-]+/) {
|
||||||
|
$0 = substr($0, 1, RLENGTH)
|
||||||
|
gsub(/[][]|:[^,]*/, "")
|
||||||
|
for (i = 1; i <= NF; i++)
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' ~/.ssh/known_hosts 2> /dev/null
|
||||||
|
) \
|
||||||
|
<(
|
||||||
|
__fzf_exec_awk '
|
||||||
|
{
|
||||||
|
sub(/#.*/, "")
|
||||||
|
for (i = 2; i <= NF; i++)
|
||||||
|
if ($i != "0.0.0.0")
|
||||||
|
print $i
|
||||||
|
}
|
||||||
|
' /etc/hosts 2> /dev/null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -266,7 +323,7 @@ _fzf_complete_ssh() {
|
|||||||
*)
|
*)
|
||||||
local user
|
local user
|
||||||
[[ $prefix =~ @ ]] && user="${prefix%%@*}@"
|
[[ $prefix =~ @ ]] && user="${prefix%%@*}@"
|
||||||
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | awk -v user="$user" '{print user $0}')
|
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}')
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -324,7 +381,7 @@ _fzf_complete_kill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_kill_post() {
|
_fzf_complete_kill_post() {
|
||||||
awk '{print $2}'
|
__fzf_exec_awk '{print $2}'
|
||||||
}
|
}
|
||||||
|
|
||||||
fzf-completion() {
|
fzf-completion() {
|
||||||
|
|||||||
@@ -17,14 +17,32 @@ if [[ $- =~ i ]]; then
|
|||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
|
#----BEGIN INCLUDE common.sh
|
||||||
|
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||||
|
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
|
||||||
|
# the changes. See code comments in "common.sh" for the implementation details.
|
||||||
|
|
||||||
__fzf_defaults() {
|
__fzf_defaults() {
|
||||||
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
||||||
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
||||||
echo "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
|
||||||
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
||||||
echo "${FZF_DEFAULT_OPTS-} $2"
|
printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__fzf_exec_awk() {
|
||||||
|
if [[ -z ${__fzf_awk-} ]]; then
|
||||||
|
__fzf_awk=awk
|
||||||
|
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
|
||||||
|
__fzf_awk=/usr/xpg4/bin/awk
|
||||||
|
elif command -v mawk >/dev/null 2>&1; then
|
||||||
|
local n x y z d
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LC_ALL=C exec "$__fzf_awk" "$@"
|
||||||
|
}
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
__fzf_select__() {
|
__fzf_select__() {
|
||||||
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \
|
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \
|
||||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \
|
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \
|
||||||
@@ -74,13 +92,7 @@ if command -v perl > /dev/null; then
|
|||||||
}
|
}
|
||||||
else # awk - fallback for POSIX systems
|
else # awk - fallback for POSIX systems
|
||||||
__fzf_history__() {
|
__fzf_history__() {
|
||||||
local output script n x y z d
|
local output script
|
||||||
if [[ -z $__fzf_awk ]]; then
|
|
||||||
__fzf_awk=awk
|
|
||||||
# choose the faster mawk if: it's installed && build date >= 20230322 && version >= 1.3.4
|
|
||||||
IFS=' .' read 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
|
|
||||||
fi
|
|
||||||
[[ $(HISTTIMEFORMAT='' builtin history 1) =~ [[:digit:]]+ ]] # how many history entries
|
[[ $(HISTTIMEFORMAT='' builtin history 1) =~ [[:digit:]]+ ]] # how many history entries
|
||||||
script='function P(b) { ++n; sub(/^[ *]/, "", b); if (!seen[b]++) { printf "%d\t%s%c", '$((BASH_REMATCH + 1))' - n, b, 0 } }
|
script='function P(b) { ++n; sub(/^[ *]/, "", b); if (!seen[b]++) { printf "%d\t%s%c", '$((BASH_REMATCH + 1))' - n, b, 0 } }
|
||||||
NR==1 { b = substr($0, 2); next }
|
NR==1 { b = substr($0, 2); next }
|
||||||
@@ -90,7 +102,7 @@ else # awk - fallback for POSIX systems
|
|||||||
output=$(
|
output=$(
|
||||||
set +o pipefail
|
set +o pipefail
|
||||||
builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '<lines>$'\n' )* ; <lines> ::= [^\n]* ( $'\n'<lines> )*
|
builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '<lines>$'\n' )* ; <lines> ::= [^\n]* ( $'\n'<lines> )*
|
||||||
command $__fzf_awk "$script" | # ( <counter>$'\t'<lines>$'\000' )*
|
__fzf_exec_awk "$script" | # ( <counter>$'\t'<lines>$'\000' )*
|
||||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \
|
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \
|
||||||
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
|
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
|
||||||
) || return
|
) || return
|
||||||
@@ -113,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
|
||||||
@@ -138,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
|
||||||
|
|||||||
@@ -38,14 +38,32 @@ fi
|
|||||||
{
|
{
|
||||||
if [[ -o interactive ]]; then
|
if [[ -o interactive ]]; then
|
||||||
|
|
||||||
|
#----BEGIN INCLUDE common.sh
|
||||||
|
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||||
|
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
|
||||||
|
# the changes. See code comments in "common.sh" for the implementation details.
|
||||||
|
|
||||||
__fzf_defaults() {
|
__fzf_defaults() {
|
||||||
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
printf '%s\n' "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
||||||
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
||||||
echo -E "--height ${FZF_TMUX_HEIGHT:-40%} --min-height 20+ --bind=ctrl-z:ignore $1"
|
|
||||||
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
||||||
echo -E "${FZF_DEFAULT_OPTS-} $2"
|
printf '%s\n' "${FZF_DEFAULT_OPTS-} $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__fzf_exec_awk() {
|
||||||
|
if [[ -z ${__fzf_awk-} ]]; then
|
||||||
|
__fzf_awk=awk
|
||||||
|
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
|
||||||
|
__fzf_awk=/usr/xpg4/bin/awk
|
||||||
|
elif command -v mawk >/dev/null 2>&1; then
|
||||||
|
local n x y z d
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LC_ALL=C exec "$__fzf_awk" "$@"
|
||||||
|
}
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
# CTRL-T - Paste the selected file path(s) into the command line
|
# CTRL-T - Paste the selected file path(s) into the command line
|
||||||
__fzf_select() {
|
__fzf_select() {
|
||||||
setopt localoptions pipefail no_aliases 2> /dev/null
|
setopt localoptions pipefail no_aliases 2> /dev/null
|
||||||
@@ -117,13 +135,13 @@ fzf-history-widget() {
|
|||||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \
|
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \
|
||||||
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
|
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
|
||||||
else
|
else
|
||||||
selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
selected="$(fc -rl 1 | __fzf_exec_awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
||||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \
|
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \
|
||||||
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
|
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
|
||||||
fi
|
fi
|
||||||
local ret=$?
|
local ret=$?
|
||||||
if [ -n "$selected" ]; then
|
if [ -n "$selected" ]; then
|
||||||
if [[ $(awk '{print $1; exit}' <<< "$selected") =~ ^[1-9][0-9]* ]]; then
|
if [[ $(__fzf_exec_awk '{print $1; exit}' <<< "$selected") =~ ^[1-9][0-9]* ]]; then
|
||||||
zle vi-fetch-history -n $MATCH
|
zle vi-fetch-history -n $MATCH
|
||||||
else # selected is a custom query, not from history
|
else # selected is a custom query, not from history
|
||||||
LBUFFER="$selected"
|
LBUFFER="$selected"
|
||||||
|
|||||||
31
shell/update-common.sh
Executable file
31
shell/update-common.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This script applies the contents of "common.sh" to the other files.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Go to the directory that contains this script
|
||||||
|
dir=${0%"${0##*/}"}
|
||||||
|
if [ -n "$dir" ]; then
|
||||||
|
cd "$dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
update() {
|
||||||
|
{
|
||||||
|
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
|
||||||
|
cat <<EOF
|
||||||
|
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||||
|
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
|
||||||
|
# the changes. See code comments in "common.sh" for the implementation details.
|
||||||
|
EOF
|
||||||
|
grep -v '^[[:blank:]]*#' common.sh # remove code comments in common.sh
|
||||||
|
sed -n '/^#----END INCLUDE/,$p' "$1"
|
||||||
|
} > "$1.part"
|
||||||
|
|
||||||
|
mv -f "$1.part" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
update completion.bash
|
||||||
|
update completion.zsh
|
||||||
|
update key-bindings.bash
|
||||||
|
update key-bindings.zsh
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013-2024 Junegunn Choi
|
Copyright (c) 2013-2025 Junegunn Choi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -25,132 +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[actChangeHeaderLabel-21]
|
_ = x[actChangeHeader-21]
|
||||||
_ = x[actChangeInputLabel-22]
|
_ = x[actChangeFooter-22]
|
||||||
_ = x[actChangeListLabel-23]
|
_ = x[actChangeHeaderLabel-23]
|
||||||
_ = x[actChangeMulti-24]
|
_ = x[actChangeFooterLabel-24]
|
||||||
_ = x[actChangeNth-25]
|
_ = x[actChangeInputLabel-25]
|
||||||
_ = x[actChangePointer-26]
|
_ = x[actChangeListLabel-26]
|
||||||
_ = x[actChangePreview-27]
|
_ = x[actChangeMulti-27]
|
||||||
_ = x[actChangePreviewLabel-28]
|
_ = x[actChangeNth-28]
|
||||||
_ = x[actChangePreviewWindow-29]
|
_ = x[actChangePointer-29]
|
||||||
_ = x[actChangePrompt-30]
|
_ = x[actChangePreview-30]
|
||||||
_ = x[actChangeQuery-31]
|
_ = x[actChangePreviewLabel-31]
|
||||||
_ = x[actClearScreen-32]
|
_ = x[actChangePreviewWindow-32]
|
||||||
_ = x[actClearQuery-33]
|
_ = x[actChangePrompt-33]
|
||||||
_ = x[actClearSelection-34]
|
_ = x[actChangeQuery-34]
|
||||||
_ = x[actClose-35]
|
_ = x[actClearScreen-35]
|
||||||
_ = x[actDeleteChar-36]
|
_ = x[actClearQuery-36]
|
||||||
_ = x[actDeleteCharEof-37]
|
_ = x[actClearSelection-37]
|
||||||
_ = x[actEndOfLine-38]
|
_ = x[actClose-38]
|
||||||
_ = x[actFatal-39]
|
_ = x[actDeleteChar-39]
|
||||||
_ = x[actForwardChar-40]
|
_ = x[actDeleteCharEof-40]
|
||||||
_ = x[actForwardWord-41]
|
_ = x[actEndOfLine-41]
|
||||||
_ = x[actKillLine-42]
|
_ = x[actFatal-42]
|
||||||
_ = x[actKillWord-43]
|
_ = x[actForwardChar-43]
|
||||||
_ = x[actUnixLineDiscard-44]
|
_ = x[actForwardWord-44]
|
||||||
_ = x[actUnixWordRubout-45]
|
_ = x[actForwardSubWord-45]
|
||||||
_ = x[actYank-46]
|
_ = x[actKillLine-46]
|
||||||
_ = x[actBackwardKillWord-47]
|
_ = x[actKillWord-47]
|
||||||
_ = x[actSelectAll-48]
|
_ = x[actKillSubWord-48]
|
||||||
_ = x[actDeselectAll-49]
|
_ = x[actUnixLineDiscard-49]
|
||||||
_ = x[actToggle-50]
|
_ = x[actUnixWordRubout-50]
|
||||||
_ = x[actToggleSearch-51]
|
_ = x[actYank-51]
|
||||||
_ = x[actToggleAll-52]
|
_ = x[actBackwardKillWord-52]
|
||||||
_ = x[actToggleDown-53]
|
_ = x[actBackwardKillSubWord-53]
|
||||||
_ = x[actToggleUp-54]
|
_ = x[actSelectAll-54]
|
||||||
_ = x[actToggleIn-55]
|
_ = x[actDeselectAll-55]
|
||||||
_ = x[actToggleOut-56]
|
_ = x[actToggle-56]
|
||||||
_ = x[actToggleTrack-57]
|
_ = x[actToggleSearch-57]
|
||||||
_ = x[actToggleTrackCurrent-58]
|
_ = x[actToggleAll-58]
|
||||||
_ = x[actToggleHeader-59]
|
_ = x[actToggleDown-59]
|
||||||
_ = x[actToggleWrap-60]
|
_ = x[actToggleUp-60]
|
||||||
_ = x[actToggleMultiLine-61]
|
_ = x[actToggleIn-61]
|
||||||
_ = x[actToggleHscroll-62]
|
_ = x[actToggleOut-62]
|
||||||
_ = x[actTrackCurrent-63]
|
_ = x[actToggleTrack-63]
|
||||||
_ = x[actToggleInput-64]
|
_ = x[actToggleTrackCurrent-64]
|
||||||
_ = x[actHideInput-65]
|
_ = x[actToggleHeader-65]
|
||||||
_ = x[actShowInput-66]
|
_ = x[actToggleWrap-66]
|
||||||
_ = x[actUntrackCurrent-67]
|
_ = x[actToggleMultiLine-67]
|
||||||
_ = x[actDown-68]
|
_ = x[actToggleHscroll-68]
|
||||||
_ = x[actUp-69]
|
_ = x[actTrackCurrent-69]
|
||||||
_ = x[actPageUp-70]
|
_ = x[actToggleInput-70]
|
||||||
_ = x[actPageDown-71]
|
_ = x[actHideInput-71]
|
||||||
_ = x[actPosition-72]
|
_ = x[actShowInput-72]
|
||||||
_ = x[actHalfPageUp-73]
|
_ = x[actUntrackCurrent-73]
|
||||||
_ = x[actHalfPageDown-74]
|
_ = x[actDown-74]
|
||||||
_ = x[actOffsetUp-75]
|
_ = x[actUp-75]
|
||||||
_ = x[actOffsetDown-76]
|
_ = x[actPageUp-76]
|
||||||
_ = x[actOffsetMiddle-77]
|
_ = x[actPageDown-77]
|
||||||
_ = x[actJump-78]
|
_ = x[actPosition-78]
|
||||||
_ = x[actJumpAccept-79]
|
_ = x[actHalfPageUp-79]
|
||||||
_ = x[actPrintQuery-80]
|
_ = x[actHalfPageDown-80]
|
||||||
_ = x[actRefreshPreview-81]
|
_ = x[actOffsetUp-81]
|
||||||
_ = x[actReplaceQuery-82]
|
_ = x[actOffsetDown-82]
|
||||||
_ = x[actToggleSort-83]
|
_ = x[actOffsetMiddle-83]
|
||||||
_ = x[actShowPreview-84]
|
_ = x[actJump-84]
|
||||||
_ = x[actHidePreview-85]
|
_ = x[actJumpAccept-85]
|
||||||
_ = x[actTogglePreview-86]
|
_ = x[actPrintQuery-86]
|
||||||
_ = x[actTogglePreviewWrap-87]
|
_ = x[actRefreshPreview-87]
|
||||||
_ = x[actTransform-88]
|
_ = x[actReplaceQuery-88]
|
||||||
_ = x[actTransformBorderLabel-89]
|
_ = x[actToggleSort-89]
|
||||||
_ = x[actTransformGhost-90]
|
_ = x[actShowPreview-90]
|
||||||
_ = x[actTransformHeader-91]
|
_ = x[actHidePreview-91]
|
||||||
_ = x[actTransformHeaderLabel-92]
|
_ = x[actTogglePreview-92]
|
||||||
_ = x[actTransformInputLabel-93]
|
_ = x[actTogglePreviewWrap-93]
|
||||||
_ = x[actTransformListLabel-94]
|
_ = x[actTransform-94]
|
||||||
_ = x[actTransformNth-95]
|
_ = x[actTransformBorderLabel-95]
|
||||||
_ = x[actTransformPointer-96]
|
_ = x[actTransformGhost-96]
|
||||||
_ = x[actTransformPreviewLabel-97]
|
_ = x[actTransformHeader-97]
|
||||||
_ = x[actTransformPrompt-98]
|
_ = x[actTransformFooter-98]
|
||||||
_ = x[actTransformQuery-99]
|
_ = x[actTransformHeaderLabel-99]
|
||||||
_ = x[actTransformSearch-100]
|
_ = x[actTransformFooterLabel-100]
|
||||||
_ = x[actSearch-101]
|
_ = x[actTransformInputLabel-101]
|
||||||
_ = x[actPreview-102]
|
_ = x[actTransformListLabel-102]
|
||||||
_ = x[actPreviewTop-103]
|
_ = x[actTransformNth-103]
|
||||||
_ = x[actPreviewBottom-104]
|
_ = x[actTransformPointer-104]
|
||||||
_ = x[actPreviewUp-105]
|
_ = x[actTransformPreviewLabel-105]
|
||||||
_ = x[actPreviewDown-106]
|
_ = x[actTransformPrompt-106]
|
||||||
_ = x[actPreviewPageUp-107]
|
_ = x[actTransformQuery-107]
|
||||||
_ = x[actPreviewPageDown-108]
|
_ = x[actTransformSearch-108]
|
||||||
_ = x[actPreviewHalfPageUp-109]
|
_ = x[actTrigger-109]
|
||||||
_ = x[actPreviewHalfPageDown-110]
|
_ = x[actBgTransform-110]
|
||||||
_ = x[actPrevHistory-111]
|
_ = x[actBgTransformBorderLabel-111]
|
||||||
_ = x[actPrevSelected-112]
|
_ = x[actBgTransformGhost-112]
|
||||||
_ = x[actPrint-113]
|
_ = x[actBgTransformHeader-113]
|
||||||
_ = x[actPut-114]
|
_ = x[actBgTransformFooter-114]
|
||||||
_ = x[actNextHistory-115]
|
_ = x[actBgTransformHeaderLabel-115]
|
||||||
_ = x[actNextSelected-116]
|
_ = x[actBgTransformFooterLabel-116]
|
||||||
_ = x[actExecute-117]
|
_ = x[actBgTransformInputLabel-117]
|
||||||
_ = x[actExecuteSilent-118]
|
_ = x[actBgTransformListLabel-118]
|
||||||
_ = x[actExecuteMulti-119]
|
_ = x[actBgTransformNth-119]
|
||||||
_ = x[actSigStop-120]
|
_ = x[actBgTransformPointer-120]
|
||||||
_ = x[actFirst-121]
|
_ = x[actBgTransformPreviewLabel-121]
|
||||||
_ = x[actLast-122]
|
_ = x[actBgTransformPrompt-122]
|
||||||
_ = x[actReload-123]
|
_ = x[actBgTransformQuery-123]
|
||||||
_ = x[actReloadSync-124]
|
_ = x[actBgTransformSearch-124]
|
||||||
_ = x[actDisableSearch-125]
|
_ = x[actBgCancel-125]
|
||||||
_ = x[actEnableSearch-126]
|
_ = x[actSearch-126]
|
||||||
_ = x[actSelect-127]
|
_ = x[actPreview-127]
|
||||||
_ = x[actDeselect-128]
|
_ = x[actPreviewTop-128]
|
||||||
_ = x[actUnbind-129]
|
_ = x[actPreviewBottom-129]
|
||||||
_ = x[actRebind-130]
|
_ = x[actPreviewUp-130]
|
||||||
_ = x[actToggleBind-131]
|
_ = x[actPreviewDown-131]
|
||||||
_ = x[actBecome-132]
|
_ = x[actPreviewPageUp-132]
|
||||||
_ = x[actShowHeader-133]
|
_ = x[actPreviewPageDown-133]
|
||||||
_ = x[actHideHeader-134]
|
_ = x[actPreviewHalfPageUp-134]
|
||||||
_ = x[actBell-135]
|
_ = x[actPreviewHalfPageDown-135]
|
||||||
_ = x[actExclude-136]
|
_ = x[actPrevHistory-136]
|
||||||
_ = x[actExcludeMulti-137]
|
_ = x[actPrevSelected-137]
|
||||||
|
_ = x[actPrint-138]
|
||||||
|
_ = x[actPut-139]
|
||||||
|
_ = x[actNextHistory-140]
|
||||||
|
_ = x[actNextSelected-141]
|
||||||
|
_ = x[actExecute-142]
|
||||||
|
_ = x[actExecuteSilent-143]
|
||||||
|
_ = x[actExecuteMulti-144]
|
||||||
|
_ = x[actSigStop-145]
|
||||||
|
_ = x[actFirst-146]
|
||||||
|
_ = x[actLast-147]
|
||||||
|
_ = x[actReload-148]
|
||||||
|
_ = x[actReloadSync-149]
|
||||||
|
_ = x[actDisableSearch-150]
|
||||||
|
_ = x[actEnableSearch-151]
|
||||||
|
_ = x[actSelect-152]
|
||||||
|
_ = x[actDeselect-153]
|
||||||
|
_ = x[actUnbind-154]
|
||||||
|
_ = x[actRebind-155]
|
||||||
|
_ = x[actToggleBind-156]
|
||||||
|
_ = x[actBecome-157]
|
||||||
|
_ = x[actShowHeader-158]
|
||||||
|
_ = x[actHideHeader-159]
|
||||||
|
_ = x[actBell-160]
|
||||||
|
_ = x[actExclude-161]
|
||||||
|
_ = x[actExcludeMulti-162]
|
||||||
|
_ = x[actAsync-163]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeHeaderLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformHeaderLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMulti"
|
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, 318, 337, 355, 369, 381, 397, 413, 434, 456, 471, 485, 499, 512, 529, 537, 550, 566, 578, 586, 600, 614, 625, 636, 654, 671, 678, 697, 709, 723, 732, 747, 759, 772, 783, 794, 806, 820, 841, 856, 869, 887, 903, 918, 932, 944, 956, 973, 980, 985, 994, 1005, 1016, 1029, 1044, 1055, 1068, 1083, 1090, 1103, 1116, 1133, 1148, 1161, 1175, 1189, 1205, 1225, 1237, 1260, 1277, 1295, 1318, 1340, 1361, 1376, 1395, 1419, 1437, 1454, 1472, 1481, 1491, 1504, 1520, 1532, 1546, 1562, 1580, 1600, 1622, 1636, 1651, 1659, 1665, 1679, 1694, 1704, 1720, 1735, 1745, 1753, 1760, 1769, 1782, 1798, 1813, 1822, 1833, 1842, 1851, 1864, 1873, 1886, 1899, 1906, 1916, 1931}
|
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) {
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ func bonusAt(input *util.Chars, idx int) int16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func normalizeRune(r rune) rune {
|
func normalizeRune(r rune) rune {
|
||||||
if r < 0x00C0 || r > 0x2184 {
|
if r < 0x00C0 || r > 0xFF61 {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +827,7 @@ func exactMatchNaive(caseSensitive bool, normalize bool, forward bool, boundaryC
|
|||||||
|
|
||||||
// For simplicity, only look at the bonus at the first character position
|
// For simplicity, only look at the bonus at the first character position
|
||||||
pidx := 0
|
pidx := 0
|
||||||
bestPos, bonus, bestBonus := -1, int16(0), int16(-1)
|
bestPos, bonus, bbonus, bestBonus := -1, int16(0), int16(0), int16(-1)
|
||||||
for index := 0; index < lenRunes; index++ {
|
for index := 0; index < lenRunes; index++ {
|
||||||
index_ := indexAt(index, lenRunes, forward)
|
index_ := indexAt(index, lenRunes, forward)
|
||||||
char := text.Get(index_)
|
char := text.Get(index_)
|
||||||
@@ -849,7 +849,16 @@ func exactMatchNaive(caseSensitive bool, normalize bool, forward bool, boundaryC
|
|||||||
bonus = bonusAt(text, index_)
|
bonus = bonusAt(text, index_)
|
||||||
}
|
}
|
||||||
if boundaryCheck {
|
if boundaryCheck {
|
||||||
ok = bonus >= bonusBoundary
|
if forward && pidx_ == 0 {
|
||||||
|
bbonus = bonus
|
||||||
|
} else if !forward && pidx_ == lenPattern-1 {
|
||||||
|
if index_ < lenRunes-1 {
|
||||||
|
bbonus = bonusAt(text, index_+1)
|
||||||
|
} else {
|
||||||
|
bbonus = bonusBoundaryWhite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = bbonus >= bonusBoundary
|
||||||
if ok && pidx_ == 0 {
|
if ok && pidx_ == 0 {
|
||||||
ok = index_ == 0 || charClassOf(text.Get(index_-1)) <= charDelimiter
|
ok = index_ == 0 || charClassOf(text.Get(index_-1)) <= charDelimiter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -473,6 +473,103 @@ var normalized = map[rune]rune{
|
|||||||
'ử': 'u',
|
'ử': 'u',
|
||||||
'ữ': 'u',
|
'ữ': 'u',
|
||||||
'ự': 'u',
|
'ự': 'u',
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
|
||||||
|
0xFF01: '!', // Fullwidth exclamation
|
||||||
|
0xFF02: '"', // Fullwidth quotation mark
|
||||||
|
0xFF03: '#', // Fullwidth number sign
|
||||||
|
0xFF04: '$', // Fullwidth dollar sign
|
||||||
|
0xFF05: '%', // Fullwidth percent
|
||||||
|
0xFF06: '&', // Fullwidth ampersand
|
||||||
|
0xFF07: '\'', // Fullwidth apostrophe
|
||||||
|
0xFF08: '(', // Fullwidth left parenthesis
|
||||||
|
0xFF09: ')', // Fullwidth right parenthesis
|
||||||
|
0xFF0A: '*', // Fullwidth asterisk
|
||||||
|
0xFF0B: '+', // Fullwidth plus
|
||||||
|
0xFF0C: ',', // Fullwidth comma
|
||||||
|
0xFF0D: '-', // Fullwidth hyphen-minus
|
||||||
|
0xFF0E: '.', // Fullwidth period
|
||||||
|
0xFF0F: '/', // Fullwidth slash
|
||||||
|
0xFF10: '0',
|
||||||
|
0xFF11: '1',
|
||||||
|
0xFF12: '2',
|
||||||
|
0xFF13: '3',
|
||||||
|
0xFF14: '4',
|
||||||
|
0xFF15: '5',
|
||||||
|
0xFF16: '6',
|
||||||
|
0xFF17: '7',
|
||||||
|
0xFF18: '8',
|
||||||
|
0xFF19: '9',
|
||||||
|
0xFF1A: ':', // Fullwidth colon
|
||||||
|
0xFF1B: ';', // Fullwidth semicolon
|
||||||
|
0xFF1C: '<', // Fullwidth less-than
|
||||||
|
0xFF1D: '=', // Fullwidth equal
|
||||||
|
0xFF1E: '>', // Fullwidth greater-than
|
||||||
|
0xFF1F: '?', // Fullwidth question mark
|
||||||
|
0xFF20: '@', // Fullwidth at sign
|
||||||
|
0xFF21: 'A',
|
||||||
|
0xFF22: 'B',
|
||||||
|
0xFF23: 'C',
|
||||||
|
0xFF24: 'D',
|
||||||
|
0xFF25: 'E',
|
||||||
|
0xFF26: 'F',
|
||||||
|
0xFF27: 'G',
|
||||||
|
0xFF28: 'H',
|
||||||
|
0xFF29: 'I',
|
||||||
|
0xFF2A: 'J',
|
||||||
|
0xFF2B: 'K',
|
||||||
|
0xFF2C: 'L',
|
||||||
|
0xFF2D: 'M',
|
||||||
|
0xFF2E: 'N',
|
||||||
|
0xFF2F: 'O',
|
||||||
|
0xFF30: 'P',
|
||||||
|
0xFF31: 'Q',
|
||||||
|
0xFF32: 'R',
|
||||||
|
0xFF33: 'S',
|
||||||
|
0xFF34: 'T',
|
||||||
|
0xFF35: 'U',
|
||||||
|
0xFF36: 'V',
|
||||||
|
0xFF37: 'W',
|
||||||
|
0xFF38: 'X',
|
||||||
|
0xFF39: 'Y',
|
||||||
|
0xFF3A: 'Z',
|
||||||
|
0xFF3B: '[', // Fullwidth left bracket
|
||||||
|
0xFF3C: '\\', // Fullwidth backslash
|
||||||
|
0xFF3D: ']', // Fullwidth right bracket
|
||||||
|
0xFF3E: '^', // Fullwidth circumflex
|
||||||
|
0xFF3F: '_', // Fullwidth underscore
|
||||||
|
0xFF40: '`', // Fullwidth grave accent
|
||||||
|
0xFF41: 'a',
|
||||||
|
0xFF42: 'b',
|
||||||
|
0xFF43: 'c',
|
||||||
|
0xFF44: 'd',
|
||||||
|
0xFF45: 'e',
|
||||||
|
0xFF46: 'f',
|
||||||
|
0xFF47: 'g',
|
||||||
|
0xFF48: 'h',
|
||||||
|
0xFF49: 'i',
|
||||||
|
0xFF4A: 'j',
|
||||||
|
0xFF4B: 'k',
|
||||||
|
0xFF4C: 'l',
|
||||||
|
0xFF4D: 'm',
|
||||||
|
0xFF4E: 'n',
|
||||||
|
0xFF4F: 'o',
|
||||||
|
0xFF50: 'p',
|
||||||
|
0xFF51: 'q',
|
||||||
|
0xFF52: 'r',
|
||||||
|
0xFF53: 's',
|
||||||
|
0xFF54: 't',
|
||||||
|
0xFF55: 'u',
|
||||||
|
0xFF56: 'v',
|
||||||
|
0xFF57: 'w',
|
||||||
|
0xFF58: 'x',
|
||||||
|
0xFF59: 'y',
|
||||||
|
0xFF5A: 'z',
|
||||||
|
0xFF5B: '{', // Fullwidth left brace
|
||||||
|
0xFF5C: '|', // Fullwidth vertical bar
|
||||||
|
0xFF5D: '}', // Fullwidth right brace
|
||||||
|
0xFF5E: '~', // Fullwidth tilde
|
||||||
|
0xFF61: '.', // Halfwidth ideographic full stop
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizeRunes normalizes latin script letters
|
// NormalizeRunes normalizes latin script letters
|
||||||
@@ -480,7 +577,7 @@ func NormalizeRunes(runes []rune) []rune {
|
|||||||
ret := make([]rune, len(runes))
|
ret := make([]rune, len(runes))
|
||||||
copy(ret, runes)
|
copy(ret, runes)
|
||||||
for idx, r := range runes {
|
for idx, r := range runes {
|
||||||
if r < 0x00C0 || r > 0x2184 {
|
if r < 0x00C0 || r > 0xFF61 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n := normalized[r]
|
n := normalized[r]
|
||||||
|
|||||||
51
src/ansi.go
51
src/ansi.go
@@ -156,13 +156,13 @@ func isCtrlSeqStart(c uint8) bool {
|
|||||||
// nextAnsiEscapeSequence returns the ANSI escape sequence and is equivalent to
|
// nextAnsiEscapeSequence returns the ANSI escape sequence and is equivalent to
|
||||||
// calling FindStringIndex() on the below regex (which was originally used):
|
// calling FindStringIndex() on the below regex (which was originally used):
|
||||||
//
|
//
|
||||||
// "(?:\x1b[\\[()][0-9;:?]*[a-zA-Z@]|\x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)"
|
// "(?:\x1b[\\[()][0-9;:?]*[a-zA-Z@]|\x1b][0-9]+[;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08|\n)"
|
||||||
func nextAnsiEscapeSequence(s string) (int, int) {
|
func nextAnsiEscapeSequence(s string) (int, int) {
|
||||||
// fast check for ANSI escape sequences
|
// fast check for ANSI escape sequences
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < len(s); i++ {
|
for ; i < len(s); i++ {
|
||||||
switch s[i] {
|
switch s[i] {
|
||||||
case '\x0e', '\x0f', '\x1b', '\x08':
|
case '\x0e', '\x0f', '\x1b', '\x08', '\n':
|
||||||
// We ignore the fact that '\x08' cannot be the first char
|
// We ignore the fact that '\x08' cannot be the first char
|
||||||
// in the string and be an escape sequence for the sake of
|
// in the string and be an escape sequence for the sake of
|
||||||
// speed and simplicity.
|
// speed and simplicity.
|
||||||
@@ -174,6 +174,9 @@ func nextAnsiEscapeSequence(s string) (int, int) {
|
|||||||
Loop:
|
Loop:
|
||||||
for ; i < len(s); i++ {
|
for ; i < len(s); i++ {
|
||||||
switch s[i] {
|
switch s[i] {
|
||||||
|
case '\n':
|
||||||
|
// match: `\n`
|
||||||
|
return i, i + 1
|
||||||
case '\x08':
|
case '\x08':
|
||||||
// backtrack to match: `.\x08`
|
// backtrack to match: `.\x08`
|
||||||
if i > 0 && s[i-1] != '\n' {
|
if i > 0 && s[i-1] != '\n' {
|
||||||
@@ -265,13 +268,30 @@ func extractColor(str string, state *ansiState, proc func(string, *ansiState) bo
|
|||||||
output.WriteString(prev)
|
output.WriteString(prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
newState := interpretCode(str[start:idx], state)
|
code := str[start:idx]
|
||||||
if !newState.equals(state) {
|
newState := interpretCode(code, state)
|
||||||
|
if code == "\n" || !newState.equals(state) {
|
||||||
if state != nil {
|
if state != nil {
|
||||||
// Update last offset
|
// Update last offset
|
||||||
(&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
|
(&offsets[len(offsets)-1]).offset[1] = int32(runeCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if code == "\n" {
|
||||||
|
output.WriteRune('\n')
|
||||||
|
runeCount++
|
||||||
|
// Full-background marker
|
||||||
|
if newState.lbg >= 0 {
|
||||||
|
marker := newState
|
||||||
|
marker.attr |= tui.FullBg
|
||||||
|
offsets = append(offsets, ansiOffset{
|
||||||
|
[2]int32{int32(runeCount), int32(runeCount)},
|
||||||
|
marker,
|
||||||
|
})
|
||||||
|
// Reset the full-line background color
|
||||||
|
newState.lbg = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if newState.colored() {
|
if newState.colored() {
|
||||||
// Append new offset
|
// Append new offset
|
||||||
if pstate == nil {
|
if pstate == nil {
|
||||||
@@ -349,6 +369,13 @@ func parseAnsiCode(s string) (int, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
||||||
|
if ansiCode == "\n" {
|
||||||
|
if prevState != nil {
|
||||||
|
return *prevState
|
||||||
|
}
|
||||||
|
return ansiState{-1, -1, 0, -1, nil}
|
||||||
|
}
|
||||||
|
|
||||||
var state ansiState
|
var state ansiState
|
||||||
if prevState == nil {
|
if prevState == nil {
|
||||||
state = ansiState{-1, -1, 0, -1, nil}
|
state = ansiState{-1, -1, 0, -1, nil}
|
||||||
@@ -356,7 +383,7 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
|||||||
state = ansiState{prevState.fg, prevState.bg, prevState.attr, prevState.lbg, prevState.url}
|
state = ansiState{prevState.fg, prevState.bg, prevState.attr, prevState.lbg, prevState.url}
|
||||||
}
|
}
|
||||||
if ansiCode[0] != '\x1b' || ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
|
if ansiCode[0] != '\x1b' || ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
|
||||||
if prevState != nil && strings.HasSuffix(ansiCode, "0K") {
|
if prevState != nil && (strings.HasSuffix(ansiCode, "0K") || strings.HasSuffix(ansiCode, "[K")) {
|
||||||
state.lbg = prevState.bg
|
state.lbg = prevState.bg
|
||||||
} else if strings.HasPrefix(ansiCode, "\x1b]8;") && (strings.HasSuffix(ansiCode, "\x1b\\") || strings.HasSuffix(ansiCode, "\a")) {
|
} else if strings.HasPrefix(ansiCode, "\x1b]8;") && (strings.HasSuffix(ansiCode, "\x1b\\") || strings.HasSuffix(ansiCode, "\a")) {
|
||||||
stLen := 2
|
stLen := 2
|
||||||
@@ -375,10 +402,14 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ansiCode) <= 3 {
|
reset := func() {
|
||||||
state.fg = -1
|
state.fg = -1
|
||||||
state.bg = -1
|
state.bg = -1
|
||||||
state.attr = 0
|
state.attr = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ansiCode) <= 3 {
|
||||||
|
reset()
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
ansiCode = ansiCode[2 : len(ansiCode)-1]
|
ansiCode = ansiCode[2 : len(ansiCode)-1]
|
||||||
@@ -432,9 +463,7 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
|||||||
case 29:
|
case 29:
|
||||||
state.attr = state.attr &^ tui.StrikeThrough
|
state.attr = state.attr &^ tui.StrikeThrough
|
||||||
case 0:
|
case 0:
|
||||||
state.fg = -1
|
reset()
|
||||||
state.bg = -1
|
|
||||||
state.attr = 0
|
|
||||||
state256 = 0
|
state256 = 0
|
||||||
default:
|
default:
|
||||||
if num >= 30 && num <= 37 {
|
if num >= 30 && num <= 37 {
|
||||||
@@ -474,9 +503,7 @@ func interpretCode(ansiCode string, prevState *ansiState) ansiState {
|
|||||||
|
|
||||||
// Empty sequence: reset
|
// Empty sequence: reset
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
state.fg = -1
|
reset()
|
||||||
state.bg = -1
|
|
||||||
state.attr = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if state256 > 0 {
|
if state256 > 0 {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
// (archived from http://ascii-table.com/ansi-escape-sequences-vt-100.php)
|
// (archived from http://ascii-table.com/ansi-escape-sequences-vt-100.php)
|
||||||
// - http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
|
// - http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
|
||||||
// - https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
// - https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||||
var ansiRegexReference = regexp.MustCompile("(?:\x1b[\\[()][0-9;:]*[a-zA-Z@]|\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08)")
|
var ansiRegexReference = regexp.MustCompile("(?:\x1b[\\[()][0-9;:]*[a-zA-Z@]|\x1b][0-9][;:][[:print:]]+(?:\x1b\\\\|\x07)|\x1b.|[\x0e\x0f]|.\x08|\n)")
|
||||||
|
|
||||||
func testParserReference(t testing.TB, str string) {
|
func testParserReference(t testing.TB, str string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ func (c *Chunk) IsFull() bool {
|
|||||||
return c.count == chunkSize
|
return c.count == chunkSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Chunk) lastIndex(minValue int32) int32 {
|
||||||
|
if c.count == 0 {
|
||||||
|
return minValue
|
||||||
|
}
|
||||||
|
return c.items[c.count-1].Index() + 1 // Exclusive
|
||||||
|
}
|
||||||
|
|
||||||
func (cl *ChunkList) lastChunk() *Chunk {
|
func (cl *ChunkList) lastChunk() *Chunk {
|
||||||
return cl.chunks[len(cl.chunks)-1]
|
return cl.chunks[len(cl.chunks)-1]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ const (
|
|||||||
maxPatternLength = 1000
|
maxPatternLength = 1000
|
||||||
maxMulti = math.MaxInt32
|
maxMulti = math.MaxInt32
|
||||||
|
|
||||||
|
// Background processes
|
||||||
|
maxBgProcesses = 30
|
||||||
|
maxBgProcessesPerAction = 3
|
||||||
|
|
||||||
// Matcher
|
// Matcher
|
||||||
numPartitionsMultiplier = 8
|
numPartitionsMultiplier = 8
|
||||||
maxPartitions = 32
|
maxPartitions = 32
|
||||||
|
|||||||
47
src/core.go
47
src/core.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ func (r revision) compatible(other revision) bool {
|
|||||||
// Run starts fzf
|
// Run starts fzf
|
||||||
func Run(opts *Options) (int, error) {
|
func Run(opts *Options) (int, error) {
|
||||||
if opts.Filter == nil {
|
if opts.Filter == nil {
|
||||||
if opts.Tmux != nil && len(os.Getenv("TMUX")) > 0 && opts.Tmux.index >= opts.Height.index {
|
if opts.useTmux() {
|
||||||
return runTmux(os.Args, opts)
|
return runTmux(os.Args, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,20 +75,24 @@ func Run(opts *Options) (int, error) {
|
|||||||
|
|
||||||
var lineAnsiState, prevLineAnsiState *ansiState
|
var lineAnsiState, prevLineAnsiState *ansiState
|
||||||
if opts.Ansi {
|
if opts.Ansi {
|
||||||
if opts.Theme.Colored {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
prevLineAnsiState = lineAnsiState
|
||||||
prevLineAnsiState = lineAnsiState
|
trimmed, offsets, newState := extractColor(byteString(data), lineAnsiState, nil)
|
||||||
trimmed, offsets, newState := extractColor(byteString(data), lineAnsiState, nil)
|
lineAnsiState = newState
|
||||||
lineAnsiState = newState
|
|
||||||
return util.ToChars(stringBytes(trimmed)), offsets
|
// Full line background is found. Add a special marker.
|
||||||
}
|
if offsets != nil && newState != nil && len(*offsets) > 0 && newState.lbg >= 0 {
|
||||||
} else {
|
marker := (*offsets)[len(*offsets)-1]
|
||||||
// When color is disabled but ansi option is given,
|
marker.offset[0] = marker.offset[1]
|
||||||
// we simply strip out ANSI codes from the input
|
marker.color.bg = newState.lbg
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
marker.color.attr = marker.color.attr | tui.FullBg
|
||||||
trimmed, _, _ := extractColor(byteString(data), nil, nil)
|
newOffsets := append(*offsets, marker)
|
||||||
return util.ToChars(stringBytes(trimmed)), nil
|
offsets = &newOffsets
|
||||||
|
|
||||||
|
// Reset the full-line background color
|
||||||
|
lineAnsiState.lbg = -1
|
||||||
}
|
}
|
||||||
|
return util.ToChars(stringBytes(trimmed)), offsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +117,7 @@ func Run(opts *Options) (int, error) {
|
|||||||
nthTransformer := opts.WithNth(opts.Delimiter)
|
nthTransformer := opts.WithNth(opts.Delimiter)
|
||||||
chunkList = NewChunkList(cache, func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(cache, func(item *Item, data []byte) bool {
|
||||||
tokens := Tokenize(byteString(data), opts.Delimiter)
|
tokens := Tokenize(byteString(data), opts.Delimiter)
|
||||||
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
|
if opts.Ansi && len(tokens) > 1 {
|
||||||
var ansiState *ansiState
|
var ansiState *ansiState
|
||||||
if prevLineAnsiState != nil {
|
if prevLineAnsiState != nil {
|
||||||
ansiStateDup := *prevLineAnsiState
|
ansiStateDup := *prevLineAnsiState
|
||||||
@@ -135,7 +140,17 @@ func Run(opts *Options) (int, error) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
item.text, item.colors = ansiProcessor(stringBytes(transformed))
|
item.text, item.colors = ansiProcessor(stringBytes(transformed))
|
||||||
item.text.TrimTrailingWhitespaces()
|
|
||||||
|
// We should not trim trailing whitespaces with background colors
|
||||||
|
var maxColorOffset int32
|
||||||
|
if item.colors != nil {
|
||||||
|
for _, ansi := range *item.colors {
|
||||||
|
if ansi.color.bg >= 0 {
|
||||||
|
maxColorOffset = util.Max32(maxColorOffset, ansi.offset[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.text.TrimTrailingWhitespaces(int(maxColorOffset))
|
||||||
item.text.Index = itemIndex
|
item.text.Index = itemIndex
|
||||||
item.origText = &data
|
item.origText = &data
|
||||||
itemIndex++
|
itemIndex++
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
minIndex := request.chunks[0].items[0].Index()
|
minIndex := request.chunks[0].items[0].Index()
|
||||||
|
maxIndex := request.chunks[numChunks-1].lastIndex(minIndex)
|
||||||
cancelled := util.NewAtomicBool(false)
|
cancelled := util.NewAtomicBool(false)
|
||||||
|
|
||||||
slices := m.sliceChunks(request.chunks)
|
slices := m.sliceChunks(request.chunks)
|
||||||
@@ -236,7 +237,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
partialResult := <-resultChan
|
partialResult := <-resultChan
|
||||||
partialResults[partialResult.index] = partialResult.matches
|
partialResults[partialResult.index] = partialResult.matches
|
||||||
}
|
}
|
||||||
return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex), false
|
return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex), false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset is called to interrupt/signal the ongoing search
|
// Reset is called to interrupt/signal the ongoing search
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "fmt"
|
|||||||
|
|
||||||
// EmptyMerger is a Merger with no data
|
// EmptyMerger is a Merger with no data
|
||||||
func EmptyMerger(revision revision) *Merger {
|
func EmptyMerger(revision revision) *Merger {
|
||||||
return NewMerger(nil, [][]Result{}, false, false, revision, 0)
|
return NewMerger(nil, [][]Result{}, false, false, revision, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merger holds a set of locally sorted lists of items and provides the view of
|
// Merger holds a set of locally sorted lists of items and provides the view of
|
||||||
@@ -22,14 +22,16 @@ type Merger struct {
|
|||||||
pass bool
|
pass bool
|
||||||
revision revision
|
revision revision
|
||||||
minIndex int32
|
minIndex int32
|
||||||
|
maxIndex int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// PassMerger returns a new Merger that simply returns the items in the
|
// PassMerger returns a new Merger that simply returns the items in the
|
||||||
// original order
|
// original order
|
||||||
func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger {
|
func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger {
|
||||||
var minIndex int32
|
var minIndex, maxIndex int32
|
||||||
if len(*chunks) > 0 {
|
if len(*chunks) > 0 {
|
||||||
minIndex = (*chunks)[0].items[0].Index()
|
minIndex = (*chunks)[0].items[0].Index()
|
||||||
|
maxIndex = (*chunks)[len(*chunks)-1].lastIndex(minIndex)
|
||||||
}
|
}
|
||||||
mg := Merger{
|
mg := Merger{
|
||||||
pattern: nil,
|
pattern: nil,
|
||||||
@@ -38,7 +40,8 @@ func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger {
|
|||||||
count: 0,
|
count: 0,
|
||||||
pass: true,
|
pass: true,
|
||||||
revision: revision,
|
revision: revision,
|
||||||
minIndex: minIndex}
|
minIndex: minIndex,
|
||||||
|
maxIndex: maxIndex}
|
||||||
|
|
||||||
for _, chunk := range *mg.chunks {
|
for _, chunk := range *mg.chunks {
|
||||||
mg.count += chunk.count
|
mg.count += chunk.count
|
||||||
@@ -47,7 +50,7 @@ func PassMerger(chunks *[]*Chunk, tac bool, revision revision) *Merger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMerger returns a new Merger
|
// NewMerger returns a new Merger
|
||||||
func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revision revision, minIndex int32) *Merger {
|
func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revision revision, minIndex int32, maxIndex int32) *Merger {
|
||||||
mg := Merger{
|
mg := Merger{
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
lists: lists,
|
lists: lists,
|
||||||
@@ -59,7 +62,8 @@ func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool, revisi
|
|||||||
final: false,
|
final: false,
|
||||||
count: 0,
|
count: 0,
|
||||||
revision: revision,
|
revision: revision,
|
||||||
minIndex: minIndex}
|
minIndex: minIndex,
|
||||||
|
maxIndex: maxIndex}
|
||||||
|
|
||||||
for _, list := range mg.lists {
|
for _, list := range mg.lists {
|
||||||
mg.count += len(list)
|
mg.count += len(list)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func TestMergerUnsorted(t *testing.T) {
|
|||||||
cnt := len(items)
|
cnt := len(items)
|
||||||
|
|
||||||
// Not sorted: same order
|
// Not sorted: same order
|
||||||
mg := NewMerger(nil, lists, false, false, revision{}, 0)
|
mg := NewMerger(nil, lists, false, false, revision{}, 0, 0)
|
||||||
assert(t, cnt == mg.Length(), "Invalid Length")
|
assert(t, cnt == mg.Length(), "Invalid Length")
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
assert(t, items[i] == mg.Get(i), "Invalid Get")
|
assert(t, items[i] == mg.Get(i), "Invalid Get")
|
||||||
@@ -70,7 +70,7 @@ func TestMergerSorted(t *testing.T) {
|
|||||||
cnt := len(items)
|
cnt := len(items)
|
||||||
|
|
||||||
// Sorted sorted order
|
// Sorted sorted order
|
||||||
mg := NewMerger(nil, lists, true, false, revision{}, 0)
|
mg := NewMerger(nil, lists, true, false, revision{}, 0, 0)
|
||||||
assert(t, cnt == mg.Length(), "Invalid Length")
|
assert(t, cnt == mg.Length(), "Invalid Length")
|
||||||
sort.Sort(ByRelevance(items))
|
sort.Sort(ByRelevance(items))
|
||||||
for i := 0; i < cnt; i++ {
|
for i := 0; i < cnt; i++ {
|
||||||
@@ -80,7 +80,7 @@ func TestMergerSorted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inverse order
|
// Inverse order
|
||||||
mg2 := NewMerger(nil, lists, true, false, revision{}, 0)
|
mg2 := NewMerger(nil, lists, true, false, revision{}, 0, 0)
|
||||||
for i := cnt - 1; i >= 0; i-- {
|
for i := cnt - 1; i >= 0; i-- {
|
||||||
if items[i] != mg2.Get(i) {
|
if items[i] != mg2.Get(i) {
|
||||||
t.Error("Not sorted", items[i], mg2.Get(i))
|
t.Error("Not sorted", items[i], mg2.Get(i))
|
||||||
|
|||||||
356
src/options.go
356
src/options.go
@@ -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
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ Usage: fzf [options]
|
|||||||
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
||||||
--border[=STYLE] Draw border around the finder
|
--border[=STYLE] Draw border around the finder
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
||||||
top|bottom|left|right|none] (default: rounded)
|
top|bottom|left|right|line|none] (default: rounded)
|
||||||
--border-label=LABEL Label to print on the border
|
--border-label=LABEL Label to print on the border
|
||||||
--border-label-pos=COL Position of the border label
|
--border-label-pos=COL Position of the border label
|
||||||
[POSITIVE_INTEGER: columns from left|
|
[POSITIVE_INTEGER: columns from left|
|
||||||
@@ -140,7 +140,7 @@ Usage: fzf [options]
|
|||||||
--filepath-word Make word-wise movements respect path separators
|
--filepath-word Make word-wise movements respect path separators
|
||||||
--input-border[=STYLE] Draw border around the input section
|
--input-border[=STYLE] Draw border around the input section
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
||||||
top|bottom|left|right|none] (default: rounded)
|
top|bottom|left|right|line|none] (default: rounded)
|
||||||
--input-label=LABEL Label to print on the input border
|
--input-label=LABEL Label to print on the input border
|
||||||
--input-label-pos=COL Position of the input label
|
--input-label-pos=COL Position of the input label
|
||||||
[POSITIVE_INTEGER: columns from left|
|
[POSITIVE_INTEGER: columns from left|
|
||||||
@@ -168,7 +168,7 @@ Usage: fzf [options]
|
|||||||
--header-first Print header before the prompt line
|
--header-first Print header before the prompt line
|
||||||
--header-border[=STYLE] Draw border around the header section
|
--header-border[=STYLE] Draw border around the header section
|
||||||
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
||||||
top|bottom|left|right|none] (default: rounded)
|
top|bottom|left|right|line|none] (default: rounded)
|
||||||
--header-lines-border[=STYLE]
|
--header-lines-border[=STYLE]
|
||||||
Display header from --header-lines with a separate border.
|
Display header from --header-lines with a separate border.
|
||||||
Pass 'none' to still separate it but without a border.
|
Pass 'none' to still separate it but without a border.
|
||||||
@@ -178,6 +178,17 @@ Usage: fzf [options]
|
|||||||
NEGATIVE_INTEGER: columns from right][:bottom]
|
NEGATIVE_INTEGER: columns from right][:bottom]
|
||||||
(default: 0 or center)
|
(default: 0 or center)
|
||||||
|
|
||||||
|
FOOTER
|
||||||
|
--footer=STR String to print as footer
|
||||||
|
--footer-border[=STYLE] Draw border around the footer section
|
||||||
|
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
||||||
|
top|bottom|left|right|line|none] (default: line)
|
||||||
|
--footer-label=LABEL Label to print on the footer border
|
||||||
|
--footer-label-pos=COL Position of the footer label
|
||||||
|
[POSITIVE_INTEGER: columns from left|
|
||||||
|
NEGATIVE_INTEGER: columns from right][:bottom]
|
||||||
|
(default: 0 or center)
|
||||||
|
|
||||||
SCRIPTING
|
SCRIPTING
|
||||||
-q, --query=STR Start the finder with the given query
|
-q, --query=STR Start the finder with the given query
|
||||||
-1, --select-1 Automatically select the only match
|
-1, --select-1 Automatically select the only match
|
||||||
@@ -579,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
|
||||||
@@ -599,6 +611,7 @@ type Options struct {
|
|||||||
Header []string
|
Header []string
|
||||||
HeaderLines int
|
HeaderLines int
|
||||||
HeaderFirst bool
|
HeaderFirst bool
|
||||||
|
Footer []string
|
||||||
Gap int
|
Gap int
|
||||||
GapLine *string
|
GapLine *string
|
||||||
Ellipsis *string
|
Ellipsis *string
|
||||||
@@ -610,8 +623,10 @@ type Options struct {
|
|||||||
InputBorderShape tui.BorderShape
|
InputBorderShape tui.BorderShape
|
||||||
HeaderBorderShape tui.BorderShape
|
HeaderBorderShape tui.BorderShape
|
||||||
HeaderLinesShape tui.BorderShape
|
HeaderLinesShape tui.BorderShape
|
||||||
|
FooterBorderShape tui.BorderShape
|
||||||
InputLabel labelOpts
|
InputLabel labelOpts
|
||||||
HeaderLabel labelOpts
|
HeaderLabel labelOpts
|
||||||
|
FooterLabel labelOpts
|
||||||
BorderLabel labelOpts
|
BorderLabel labelOpts
|
||||||
ListLabel labelOpts
|
ListLabel labelOpts
|
||||||
PreviewLabel labelOpts
|
PreviewLabel labelOpts
|
||||||
@@ -696,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,
|
||||||
@@ -716,6 +732,7 @@ func defaultOptions() *Options {
|
|||||||
Header: make([]string, 0),
|
Header: make([]string, 0),
|
||||||
HeaderLines: 0,
|
HeaderLines: 0,
|
||||||
HeaderFirst: false,
|
HeaderFirst: false,
|
||||||
|
Footer: make([]string, 0),
|
||||||
Gap: 0,
|
Gap: 0,
|
||||||
Ellipsis: nil,
|
Ellipsis: nil,
|
||||||
Scrollbar: nil,
|
Scrollbar: nil,
|
||||||
@@ -880,12 +897,9 @@ func parseAlgo(str string) (algo.Algo, error) {
|
|||||||
return nil, errors.New("invalid algorithm (expected: v1 or v2)")
|
return nil, errors.New("invalid algorithm (expected: v1 or v2)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBorder(str string, optional bool, allowLine bool) (tui.BorderShape, error) {
|
func parseBorder(str string, optional bool) (tui.BorderShape, error) {
|
||||||
switch str {
|
switch str {
|
||||||
case "line":
|
case "line":
|
||||||
if !allowLine {
|
|
||||||
return tui.BorderNone, errors.New("'line' is only allowed for preview border")
|
|
||||||
}
|
|
||||||
return tui.BorderLine, nil
|
return tui.BorderLine, nil
|
||||||
case "rounded":
|
case "rounded":
|
||||||
return tui.BorderRounded, nil
|
return tui.BorderRounded, nil
|
||||||
@@ -920,15 +934,12 @@ func parseBorder(str string, optional bool, allowLine bool) (tui.BorderShape, er
|
|||||||
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, ",,,") {
|
||||||
@@ -944,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":
|
||||||
@@ -957,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-_":
|
||||||
@@ -996,12 +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":
|
||||||
|
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":
|
||||||
@@ -1010,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":
|
||||||
@@ -1036,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":
|
||||||
@@ -1044,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":
|
||||||
@@ -1077,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-") {
|
||||||
@@ -1090,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) {
|
||||||
@@ -1195,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()
|
||||||
@@ -1282,6 +1412,8 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
switch components[0] {
|
switch components[0] {
|
||||||
case "query", "input", "input-fg":
|
case "query", "input", "input-fg":
|
||||||
mergeAttr(&theme.Input)
|
mergeAttr(&theme.Input)
|
||||||
|
case "ghost":
|
||||||
|
mergeAttr(&theme.Ghost)
|
||||||
case "disabled":
|
case "disabled":
|
||||||
mergeAttr(&theme.Disabled)
|
mergeAttr(&theme.Disabled)
|
||||||
case "fg":
|
case "fg":
|
||||||
@@ -1346,6 +1478,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
mergeAttr(&theme.HeaderBorder)
|
mergeAttr(&theme.HeaderBorder)
|
||||||
case "header-label":
|
case "header-label":
|
||||||
mergeAttr(&theme.HeaderLabel)
|
mergeAttr(&theme.HeaderLabel)
|
||||||
|
case "footer-border":
|
||||||
|
mergeAttr(&theme.FooterBorder)
|
||||||
|
case "footer-label":
|
||||||
|
mergeAttr(&theme.FooterLabel)
|
||||||
case "spinner":
|
case "spinner":
|
||||||
mergeAttr(&theme.Spinner)
|
mergeAttr(&theme.Spinner)
|
||||||
case "info":
|
case "info":
|
||||||
@@ -1358,6 +1494,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
|
|||||||
mergeAttr(&theme.Header)
|
mergeAttr(&theme.Header)
|
||||||
case "header-bg":
|
case "header-bg":
|
||||||
mergeAttr(&theme.HeaderBg)
|
mergeAttr(&theme.HeaderBg)
|
||||||
|
case "footer", "footer-fg":
|
||||||
|
mergeAttr(&theme.Footer)
|
||||||
|
case "footer-bg":
|
||||||
|
mergeAttr(&theme.FooterBg)
|
||||||
case "gap-line":
|
case "gap-line":
|
||||||
mergeAttr(&theme.GapLine)
|
mergeAttr(&theme.GapLine)
|
||||||
default:
|
default:
|
||||||
@@ -1413,7 +1553,7 @@ const (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
executeRegexp = regexp.MustCompile(
|
executeRegexp = regexp.MustCompile(
|
||||||
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header|search|nth|pointer|ghost)|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-]+")
|
||||||
}
|
}
|
||||||
@@ -1525,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":
|
||||||
@@ -1539,12 +1681,14 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actCancel)
|
appendAction(actCancel)
|
||||||
case "clear-query":
|
case "clear-query":
|
||||||
appendAction(actClearQuery)
|
appendAction(actClearQuery)
|
||||||
case "clear-selection":
|
case "clear-multi", "clear-selection":
|
||||||
appendAction(actClearSelection)
|
appendAction(actClearSelection)
|
||||||
case "forward-char":
|
case "forward-char":
|
||||||
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":
|
||||||
@@ -1553,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":
|
||||||
@@ -1561,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":
|
||||||
@@ -1681,6 +1829,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
|||||||
appendAction(actExclude)
|
appendAction(actExclude)
|
||||||
case "exclude-multi":
|
case "exclude-multi":
|
||||||
appendAction(actExcludeMulti)
|
appendAction(actExcludeMulti)
|
||||||
|
case "bg-cancel":
|
||||||
|
appendAction(actBgCancel)
|
||||||
default:
|
default:
|
||||||
t := isExecuteAction(specLower)
|
t := isExecuteAction(specLower)
|
||||||
if t == actIgnore {
|
if t == actIgnore {
|
||||||
@@ -1708,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:
|
||||||
@@ -1753,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
|
||||||
}
|
}
|
||||||
@@ -1798,6 +1948,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actPreview
|
return actPreview
|
||||||
case "change-header":
|
case "change-header":
|
||||||
return actChangeHeader
|
return actChangeHeader
|
||||||
|
case "change-footer":
|
||||||
|
return actChangeFooter
|
||||||
case "change-list-label":
|
case "change-list-label":
|
||||||
return actChangeListLabel
|
return actChangeListLabel
|
||||||
case "change-border-label":
|
case "change-border-label":
|
||||||
@@ -1808,6 +1960,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actChangeInputLabel
|
return actChangeInputLabel
|
||||||
case "change-header-label":
|
case "change-header-label":
|
||||||
return actChangeHeaderLabel
|
return actChangeHeaderLabel
|
||||||
|
case "change-footer-label":
|
||||||
|
return actChangeFooterLabel
|
||||||
case "change-ghost":
|
case "change-ghost":
|
||||||
return actChangeGhost
|
return actChangeGhost
|
||||||
case "change-pointer":
|
case "change-pointer":
|
||||||
@@ -1848,6 +2002,10 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actTransformInputLabel
|
return actTransformInputLabel
|
||||||
case "transform-header-label":
|
case "transform-header-label":
|
||||||
return actTransformHeaderLabel
|
return actTransformHeaderLabel
|
||||||
|
case "transform-footer-label":
|
||||||
|
return actTransformFooterLabel
|
||||||
|
case "transform-footer":
|
||||||
|
return actTransformFooter
|
||||||
case "transform-header":
|
case "transform-header":
|
||||||
return actTransformHeader
|
return actTransformHeader
|
||||||
case "transform-ghost":
|
case "transform-ghost":
|
||||||
@@ -1862,6 +2020,38 @@ func isExecuteAction(str string) actionType {
|
|||||||
return actTransformQuery
|
return actTransformQuery
|
||||||
case "transform-search":
|
case "transform-search":
|
||||||
return actTransformSearch
|
return actTransformSearch
|
||||||
|
case "bg-transform":
|
||||||
|
return actBgTransform
|
||||||
|
case "bg-transform-list-label":
|
||||||
|
return actBgTransformListLabel
|
||||||
|
case "bg-transform-border-label":
|
||||||
|
return actBgTransformBorderLabel
|
||||||
|
case "bg-transform-preview-label":
|
||||||
|
return actBgTransformPreviewLabel
|
||||||
|
case "bg-transform-input-label":
|
||||||
|
return actBgTransformInputLabel
|
||||||
|
case "bg-transform-header-label":
|
||||||
|
return actBgTransformHeaderLabel
|
||||||
|
case "bg-transform-footer-label":
|
||||||
|
return actBgTransformFooterLabel
|
||||||
|
case "bg-transform-footer":
|
||||||
|
return actBgTransformFooter
|
||||||
|
case "bg-transform-header":
|
||||||
|
return actBgTransformHeader
|
||||||
|
case "bg-transform-ghost":
|
||||||
|
return actBgTransformGhost
|
||||||
|
case "bg-transform-nth":
|
||||||
|
return actBgTransformNth
|
||||||
|
case "bg-transform-pointer":
|
||||||
|
return actBgTransformPointer
|
||||||
|
case "bg-transform-prompt":
|
||||||
|
return actBgTransformPrompt
|
||||||
|
case "bg-transform-query":
|
||||||
|
return actBgTransformQuery
|
||||||
|
case "bg-transform-search":
|
||||||
|
return actBgTransformSearch
|
||||||
|
case "trigger":
|
||||||
|
return actTrigger
|
||||||
case "search":
|
case "search":
|
||||||
return actSearch
|
return actSearch
|
||||||
}
|
}
|
||||||
@@ -1869,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
|
||||||
}
|
}
|
||||||
@@ -2408,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
|
||||||
}
|
}
|
||||||
@@ -2669,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 {
|
||||||
@@ -2727,6 +2924,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
if opts.HeaderLines, err = nextInt("number of header lines required"); err != nil {
|
if opts.HeaderLines, err = nextInt("number of header lines required"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case "--no-footer":
|
||||||
|
opts.Footer = []string{}
|
||||||
|
case "--footer":
|
||||||
|
str, err := nextString("footer string required")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts.Footer = strLines(str)
|
||||||
case "--header-first":
|
case "--header-first":
|
||||||
opts.HeaderFirst = true
|
opts.HeaderFirst = true
|
||||||
case "--no-header-first":
|
case "--no-header-first":
|
||||||
@@ -2771,7 +2976,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.Preview.border = tui.BorderNone
|
opts.Preview.border = tui.BorderNone
|
||||||
case "--preview-border":
|
case "--preview-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.Preview.border, err = parseBorder(arg, !hasArg, true); err != nil {
|
if opts.Preview.border, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--height":
|
case "--height":
|
||||||
@@ -2810,14 +3015,23 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.BorderShape = tui.BorderNone
|
opts.BorderShape = tui.BorderNone
|
||||||
case "--border":
|
case "--border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.BorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--list-border":
|
case "--list-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.ListBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.ListBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if opts.ListBorderShape == tui.BorderLine {
|
||||||
|
if hasArg {
|
||||||
|
// '--list-border line' is not allowed
|
||||||
|
return errors.New("list border cannot be 'line'")
|
||||||
|
}
|
||||||
|
// This is when '--style full:line' is previously specified and
|
||||||
|
// '--list-border' is specified without an argument.
|
||||||
|
opts.ListBorderShape = tui.BorderRounded
|
||||||
|
}
|
||||||
case "--no-list-border":
|
case "--no-list-border":
|
||||||
opts.ListBorderShape = tui.BorderNone
|
opts.ListBorderShape = tui.BorderNone
|
||||||
case "--no-list-label":
|
case "--no-list-label":
|
||||||
@@ -2839,14 +3053,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
opts.HeaderBorderShape = tui.BorderNone
|
opts.HeaderBorderShape = tui.BorderNone
|
||||||
case "--header-border":
|
case "--header-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
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, false); err != nil {
|
if opts.HeaderLinesShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--no-header-label":
|
case "--no-header-label":
|
||||||
@@ -2863,11 +3077,32 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
|||||||
if err := parseLabelPosition(&opts.HeaderLabel, pos); err != nil {
|
if err := parseLabelPosition(&opts.HeaderLabel, pos); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case "--no-footer-border":
|
||||||
|
opts.FooterBorderShape = tui.BorderNone
|
||||||
|
case "--footer-border":
|
||||||
|
hasArg, arg := optionalNextString()
|
||||||
|
if opts.FooterBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "--no-footer-label":
|
||||||
|
opts.FooterLabel.label = ""
|
||||||
|
case "--footer-label":
|
||||||
|
if opts.FooterLabel.label, err = nextString("footer label required"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "--footer-label-pos":
|
||||||
|
pos, err := nextString("footer label position required (positive or negative integer or 'center')")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := parseLabelPosition(&opts.FooterLabel, pos); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case "--no-input-border":
|
case "--no-input-border":
|
||||||
opts.InputBorderShape = tui.BorderNone
|
opts.InputBorderShape = tui.BorderNone
|
||||||
case "--input-border":
|
case "--input-border":
|
||||||
hasArg, arg := optionalNextString()
|
hasArg, arg := optionalNextString()
|
||||||
if opts.InputBorderShape, err = parseBorder(arg, !hasArg, false); err != nil {
|
if opts.InputBorderShape, err = parseBorder(arg, !hasArg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "--no-input-label":
|
case "--no-input-label":
|
||||||
@@ -3075,6 +3310,7 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
opts.ListBorderShape = tui.BorderUndefined
|
opts.ListBorderShape = tui.BorderUndefined
|
||||||
opts.InputBorderShape = tui.BorderUndefined
|
opts.InputBorderShape = tui.BorderUndefined
|
||||||
opts.HeaderBorderShape = tui.BorderUndefined
|
opts.HeaderBorderShape = tui.BorderUndefined
|
||||||
|
opts.FooterBorderShape = tui.BorderUndefined
|
||||||
opts.Preview.border = defaultBorderShape
|
opts.Preview.border = defaultBorderShape
|
||||||
opts.Preview.info = true
|
opts.Preview.info = true
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
@@ -3086,6 +3322,7 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
opts.ListBorderShape = tui.BorderUndefined
|
opts.ListBorderShape = tui.BorderUndefined
|
||||||
opts.InputBorderShape = tui.BorderUndefined
|
opts.InputBorderShape = tui.BorderUndefined
|
||||||
opts.HeaderBorderShape = tui.BorderUndefined
|
opts.HeaderBorderShape = tui.BorderUndefined
|
||||||
|
opts.FooterBorderShape = tui.BorderLine
|
||||||
opts.Preview.border = tui.BorderLine
|
opts.Preview.border = tui.BorderLine
|
||||||
opts.Preview.info = false
|
opts.Preview.info = false
|
||||||
opts.InfoStyle = infoDefault
|
opts.InfoStyle = infoDefault
|
||||||
@@ -3101,16 +3338,22 @@ func applyPreset(opts *Options, preset string) error {
|
|||||||
}
|
}
|
||||||
if len(tokens) == 2 && len(tokens[1]) > 0 {
|
if len(tokens) == 2 && len(tokens[1]) > 0 {
|
||||||
var err error
|
var err error
|
||||||
defaultBorderShape, err = parseBorder(tokens[1], false, false)
|
defaultBorderShape, err = parseBorder(tokens[1], false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.ListBorderShape = defaultBorderShape
|
if defaultBorderShape != tui.BorderLine {
|
||||||
|
opts.ListBorderShape = defaultBorderShape
|
||||||
|
}
|
||||||
opts.InputBorderShape = defaultBorderShape
|
opts.InputBorderShape = defaultBorderShape
|
||||||
opts.HeaderBorderShape = defaultBorderShape
|
opts.HeaderBorderShape = defaultBorderShape
|
||||||
|
opts.FooterBorderShape = defaultBorderShape
|
||||||
opts.Preview.border = defaultBorderShape
|
opts.Preview.border = defaultBorderShape
|
||||||
|
if defaultBorderShape == tui.BorderLine {
|
||||||
|
opts.BorderShape = defaultBorderShape
|
||||||
|
}
|
||||||
opts.Preview.info = true
|
opts.Preview.info = true
|
||||||
opts.InfoStyle = infoInlineRight
|
opts.InfoStyle = infoInlineRight
|
||||||
opts.Theme.Gutter = tui.NewColorAttr()
|
opts.Theme.Gutter = tui.NewColorAttr()
|
||||||
@@ -3121,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3183,6 +3432,10 @@ func noSeparatorLine(style infoStyle, separator bool) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opts *Options) useTmux() bool {
|
||||||
|
return opts.Tmux != nil && len(os.Getenv("TMUX")) > 0 && opts.Tmux.index >= opts.Height.index
|
||||||
|
}
|
||||||
|
|
||||||
func (opts *Options) noSeparatorLine() bool {
|
func (opts *Options) noSeparatorLine() bool {
|
||||||
if opts.Inputless {
|
if opts.Inputless {
|
||||||
return true
|
return true
|
||||||
@@ -3214,17 +3467,12 @@ func postProcessOptions(opts *Options) error {
|
|||||||
opts.HeaderBorderShape = tui.BorderNone
|
opts.HeaderBorderShape = tui.BorderNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.FooterBorderShape == tui.BorderUndefined {
|
||||||
|
opts.FooterBorderShape = tui.BorderLine
|
||||||
|
}
|
||||||
|
|
||||||
if opts.HeaderLinesShape == tui.BorderNone {
|
if opts.HeaderLinesShape == tui.BorderNone {
|
||||||
opts.HeaderLinesShape = tui.BorderPhantom
|
opts.HeaderLinesShape = tui.BorderPhantom
|
||||||
} else if opts.HeaderLinesShape == tui.BorderUndefined {
|
|
||||||
// In reverse-list layout, header lines should be at the top, while
|
|
||||||
// ordinary header should be at the bottom. So let's use a separate
|
|
||||||
// window for the header lines.
|
|
||||||
if opts.Layout == layoutReverseList {
|
|
||||||
opts.HeaderLinesShape = tui.BorderPhantom
|
|
||||||
} else {
|
|
||||||
opts.HeaderLinesShape = tui.BorderNone
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Pointer == nil {
|
if opts.Pointer == nil {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ type colorOffset struct {
|
|||||||
url *url
|
url *url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (co colorOffset) IsFullBgMarker(at int32) bool {
|
||||||
|
return at == co.offset[0] && at == co.offset[1] && co.color.Attr()&tui.FullBg > 0
|
||||||
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
item *Item
|
item *Item
|
||||||
points [4]uint16
|
points [4]uint16
|
||||||
@@ -124,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
|
||||||
}
|
}
|
||||||
@@ -149,12 +153,20 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
|
|||||||
color bool
|
color bool
|
||||||
match bool
|
match bool
|
||||||
nth bool
|
nth bool
|
||||||
|
fbg tui.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
cols := make([]cellInfo, maxCol)
|
cols := make([]cellInfo, maxCol+1)
|
||||||
|
for idx := range cols {
|
||||||
|
cols[idx].fbg = -1
|
||||||
|
}
|
||||||
for colorIndex, ansi := range itemColors {
|
for colorIndex, ansi := range itemColors {
|
||||||
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
|
if ansi.offset[0] == ansi.offset[1] && ansi.color.attr&tui.FullBg > 0 {
|
||||||
cols[i] = cellInfo{colorIndex, true, false, false}
|
cols[ansi.offset[0]].fbg = ansi.color.lbg
|
||||||
|
} else {
|
||||||
|
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
|
||||||
|
cols[i] = cellInfo{colorIndex, true, false, false, cols[i].fbg}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +188,12 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
|
|||||||
// ------------ ---- -- ----
|
// ------------ ---- -- ----
|
||||||
// ++++++++ ++++++++++
|
// ++++++++ ++++++++++
|
||||||
// --++++++++-- --++++++++++---
|
// --++++++++-- --++++++++++---
|
||||||
var curr cellInfo = cellInfo{0, false, false, false}
|
curr := cellInfo{0, false, false, false, -1}
|
||||||
start := 0
|
start := 0
|
||||||
ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
|
ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
|
||||||
|
if !theme.Colored {
|
||||||
|
return tui.NewColorPair(-1, -1, ansi.color.attr).MergeAttr(base)
|
||||||
|
}
|
||||||
fg := ansi.color.fg
|
fg := ansi.color.fg
|
||||||
bg := ansi.color.bg
|
bg := ansi.color.bg
|
||||||
if fg == -1 {
|
if fg == -1 {
|
||||||
@@ -191,6 +206,13 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
|
|||||||
}
|
}
|
||||||
var colors []colorOffset
|
var colors []colorOffset
|
||||||
add := func(idx int) {
|
add := func(idx int) {
|
||||||
|
if curr.fbg >= 0 {
|
||||||
|
colors = append(colors, colorOffset{
|
||||||
|
offset: [2]int32{int32(start), int32(start)},
|
||||||
|
color: tui.NewColorPair(-1, curr.fbg, tui.FullBg),
|
||||||
|
match: false,
|
||||||
|
url: nil})
|
||||||
|
}
|
||||||
if (curr.color || curr.nth || curr.match) && idx > start {
|
if (curr.color || curr.nth || curr.match) && idx > start {
|
||||||
if curr.match {
|
if curr.match {
|
||||||
var color tui.ColorPair
|
var color tui.ColorPair
|
||||||
@@ -200,7 +222,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
|
|||||||
color = colBase.Merge(colMatch)
|
color = colBase.Merge(colMatch)
|
||||||
}
|
}
|
||||||
var url *url
|
var url *url
|
||||||
if curr.color && theme.Colored {
|
if curr.color {
|
||||||
ansi := itemColors[curr.index]
|
ansi := itemColors[curr.index]
|
||||||
url = ansi.color.url
|
url = ansi.color.url
|
||||||
origColor := ansiToColorPair(ansi, colMatch)
|
origColor := ansiToColorPair(ansi, colMatch)
|
||||||
@@ -215,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)
|
||||||
@@ -225,10 +247,11 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
|
|||||||
offset: [2]int32{int32(start), int32(idx)}, color: color, match: true, url: url})
|
offset: [2]int32{int32(start), int32(idx)}, color: color, match: true, url: url})
|
||||||
} else if curr.color {
|
} else if curr.color {
|
||||||
ansi := itemColors[curr.index]
|
ansi := itemColors[curr.index]
|
||||||
color := ansiToColorPair(ansi, colBase)
|
base := colBase
|
||||||
if curr.nth {
|
if curr.nth {
|
||||||
color = color.WithAttr(attrNth)
|
base = base.WithAttr(attrNth)
|
||||||
}
|
}
|
||||||
|
color := ansiToColorPair(ansi, base)
|
||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
offset: [2]int32{int32(start), int32(idx)},
|
offset: [2]int32{int32(start), int32(idx)},
|
||||||
color: color,
|
color: color,
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func TestColorOffset(t *testing.T) {
|
|||||||
assert(9, 35, 37, tui.NewColorPair(4, 8, tui.Bold))
|
assert(9, 35, 37, tui.NewColorPair(4, 8, tui.Bold))
|
||||||
expected := tui.Bold | attr
|
expected := tui.Bold | attr
|
||||||
if attr == tui.AttrRegular {
|
if attr == tui.AttrRegular {
|
||||||
expected = tui.AttrRegular
|
expected = tui.Bold
|
||||||
}
|
}
|
||||||
assert(10, 37, 39, tui.NewColorPair(4, 8, expected))
|
assert(10, 37, 39, tui.NewColorPair(4, 8, expected))
|
||||||
assert(11, 39, 40, tui.NewColorPair(4, 8, tui.Bold))
|
assert(11, 39, 40, tui.NewColorPair(4, 8, tui.Bold))
|
||||||
|
|||||||
1297
src/terminal.go
1297
src/terminal.go
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func replacePlaceholderTest(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
|
func replacePlaceholderTest(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems [3][]*Item) string {
|
||||||
replaced, _ := replacePlaceholder(replacePlaceholderParams{
|
replaced, _ := replacePlaceholder(replacePlaceholderParams{
|
||||||
template: template,
|
template: template,
|
||||||
stripAnsi: stripAnsi,
|
stripAnsi: stripAnsi,
|
||||||
@@ -30,11 +30,11 @@ func replacePlaceholderTest(template string, stripAnsi bool, delimiter Delimiter
|
|||||||
|
|
||||||
func TestReplacePlaceholder(t *testing.T) {
|
func TestReplacePlaceholder(t *testing.T) {
|
||||||
item1 := newItem(" foo'bar \x1b[31mbaz\x1b[m")
|
item1 := newItem(" foo'bar \x1b[31mbaz\x1b[m")
|
||||||
items1 := []*Item{item1, item1}
|
items1 := [3][]*Item{{item1}, {item1}, nil}
|
||||||
items2 := []*Item{
|
items2 := [3][]*Item{
|
||||||
newItem("foo'bar \x1b[31mbaz\x1b[m"),
|
{newItem("foo'bar \x1b[31mbaz\x1b[m")},
|
||||||
newItem("foo'bar \x1b[31mbaz\x1b[m"),
|
{newItem("foo'bar \x1b[31mbaz\x1b[m"),
|
||||||
newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}
|
newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}, nil}
|
||||||
|
|
||||||
delim := "'"
|
delim := "'"
|
||||||
var regex *regexp.Regexp
|
var regex *regexp.Regexp
|
||||||
@@ -145,11 +145,11 @@ func TestReplacePlaceholder(t *testing.T) {
|
|||||||
checkFormat("echo {{.O}} {{.O}}")
|
checkFormat("echo {{.O}} {{.O}}")
|
||||||
|
|
||||||
// No match
|
// No match
|
||||||
result = replacePlaceholderTest("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, nil})
|
result = replacePlaceholderTest("echo {}/{+}", true, Delimiter{}, printsep, false, "query", [3][]*Item{nil, nil, nil})
|
||||||
check("echo /")
|
check("echo /")
|
||||||
|
|
||||||
// No match, but with selections
|
// No match, but with selections
|
||||||
result = replacePlaceholderTest("echo {}/{+}", true, Delimiter{}, printsep, false, "query", []*Item{nil, item1})
|
result = replacePlaceholderTest("echo {}/{+}", true, Delimiter{}, printsep, false, "query", [3][]*Item{nil, {item1}, nil})
|
||||||
checkFormat("echo /{{.O}} foo{{.I}}bar baz{{.O}}")
|
checkFormat("echo /{{.O}} foo{{.I}}bar baz{{.O}}")
|
||||||
|
|
||||||
// String delimiter
|
// String delimiter
|
||||||
@@ -166,17 +166,18 @@ func TestReplacePlaceholder(t *testing.T) {
|
|||||||
Test single placeholders, but focus on the placeholders' parameters (e.g. flags).
|
Test single placeholders, but focus on the placeholders' parameters (e.g. flags).
|
||||||
see: TestParsePlaceholder
|
see: TestParsePlaceholder
|
||||||
*/
|
*/
|
||||||
items3 := []*Item{
|
items3 := [3][]*Item{
|
||||||
// single line
|
// single line
|
||||||
newItem("1a 1b 1c 1d 1e 1f"),
|
{newItem("1a 1b 1c 1d 1e 1f")},
|
||||||
// multi line
|
// multi line
|
||||||
newItem("1a 1b 1c 1d 1e 1f"),
|
{newItem("1a 1b 1c 1d 1e 1f"),
|
||||||
newItem("2a 2b 2c 2d 2e 2f"),
|
newItem("2a 2b 2c 2d 2e 2f"),
|
||||||
newItem("3a 3b 3c 3d 3e 3f"),
|
newItem("3a 3b 3c 3d 3e 3f"),
|
||||||
newItem("4a 4b 4c 4d 4e 4f"),
|
newItem("4a 4b 4c 4d 4e 4f"),
|
||||||
newItem("5a 5b 5c 5d 5e 5f"),
|
newItem("5a 5b 5c 5d 5e 5f"),
|
||||||
newItem("6a 6b 6c 6d 6e 6f"),
|
newItem("6a 6b 6c 6d 6e 6f"),
|
||||||
newItem("7a 7b 7c 7d 7e 7f"),
|
newItem("7a 7b 7c 7d 7e 7f")},
|
||||||
|
nil,
|
||||||
}
|
}
|
||||||
stripAnsi := false
|
stripAnsi := false
|
||||||
forcePlus := false
|
forcePlus := false
|
||||||
@@ -557,14 +558,14 @@ func newItem(str string) *Item {
|
|||||||
return &Item{origText: &bytes, text: util.ToChars([]byte(trimmed))}
|
return &Item{origText: &bytes, text: util.ToChars([]byte(trimmed))}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions tested in this file require array of items (allItems). The array needs
|
// Functions tested in this file require array of items (allItems).
|
||||||
// to consist of at least two nils. This is helper function.
|
// This is helper function.
|
||||||
func newItems(str ...string) []*Item {
|
func newItems(str ...string) [3][]*Item {
|
||||||
result := make([]*Item, util.Max(len(str), 2))
|
result := make([]*Item, len(str))
|
||||||
for i, s := range str {
|
for i, s := range str {
|
||||||
result[i] = newItem(s)
|
result[i] = newItem(s)
|
||||||
}
|
}
|
||||||
return result
|
return [3][]*Item{result, nil, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// (for logging purposes)
|
// (for logging purposes)
|
||||||
@@ -588,7 +589,7 @@ func templateToString(format string, data any) string {
|
|||||||
type give struct {
|
type give struct {
|
||||||
template string
|
template string
|
||||||
query string
|
query string
|
||||||
allItems []*Item
|
allItems [3][]*Item
|
||||||
}
|
}
|
||||||
type want struct {
|
type want struct {
|
||||||
/*
|
/*
|
||||||
@@ -626,25 +627,25 @@ func testCommands(t *testing.T, tests []testCase) {
|
|||||||
// evaluate the test cases
|
// evaluate the test cases
|
||||||
for idx, test := range tests {
|
for idx, test := range tests {
|
||||||
gotOutput := replacePlaceholderTest(
|
gotOutput := replacePlaceholderTest(
|
||||||
test.give.template, stripAnsi, delimiter, printsep, forcePlus,
|
test.template, stripAnsi, delimiter, printsep, forcePlus,
|
||||||
test.give.query,
|
test.query,
|
||||||
test.give.allItems)
|
test.allItems)
|
||||||
switch {
|
switch {
|
||||||
case test.want.output != "":
|
case test.output != "":
|
||||||
if gotOutput != test.want.output {
|
if gotOutput != test.output {
|
||||||
t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
|
t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
|
||||||
idx,
|
idx,
|
||||||
test.give.template, test.give.query, test.give.allItems,
|
test.template, test.query, test.allItems,
|
||||||
gotOutput, test.want.output)
|
gotOutput, test.output)
|
||||||
}
|
}
|
||||||
case test.want.match != "":
|
case test.match != "":
|
||||||
wantMatch := strings.ReplaceAll(test.want.match, `\`, `\\`)
|
wantMatch := strings.ReplaceAll(test.match, `\`, `\\`)
|
||||||
wantRegex := regexp.MustCompile(wantMatch)
|
wantRegex := regexp.MustCompile(wantMatch)
|
||||||
if !wantRegex.MatchString(gotOutput) {
|
if !wantRegex.MatchString(gotOutput) {
|
||||||
t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
|
t.Errorf("tests[%v]:\ngave{\n\ttemplate: '%s',\n\tquery: '%s',\n\tallItems: %s}\nand got '%s',\nbut want '%s'",
|
||||||
idx,
|
idx,
|
||||||
test.give.template, test.give.query, test.give.allItems,
|
test.template, test.query, test.allItems,
|
||||||
gotOutput, test.want.match)
|
gotOutput, test.match)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Errorf("tests[%v]: test case does not describe 'want' property", idx)
|
t.Errorf("tests[%v]: test case does not describe 'want' property", idx)
|
||||||
|
|||||||
@@ -11,10 +11,14 @@ func runTmux(args []string, opts *Options) (int, error) {
|
|||||||
// Prepare arguments
|
// Prepare arguments
|
||||||
fzf, rest := args[0], args[1:]
|
fzf, rest := args[0], args[1:]
|
||||||
args = []string{"--bind=ctrl-z:ignore"}
|
args = []string{"--bind=ctrl-z:ignore"}
|
||||||
if !opts.Tmux.border && opts.BorderShape == tui.BorderUndefined {
|
if !opts.Tmux.border && (opts.BorderShape == tui.BorderUndefined || opts.BorderShape == tui.BorderLine) {
|
||||||
// We append --border option at the end, because `--style=full:STYLE`
|
// We append --border option at the end, because `--style=full:STYLE`
|
||||||
// may have changed the default border style.
|
// may have changed the default border style.
|
||||||
rest = append(rest, "--border")
|
if tui.DefaultBorderShape == tui.BorderRounded {
|
||||||
|
rest = append(rest, "--border=rounded")
|
||||||
|
} else {
|
||||||
|
rest = append(rest, "--border=sharp")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if opts.Tmux.border && opts.Margin == defaultMargin() {
|
if opts.Tmux.border && opts.Margin == defaultMargin() {
|
||||||
args = append(args, "--margin=0,1")
|
args = append(args, "--margin=0,1")
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ var DefaultBorderShape = BorderRounded
|
|||||||
func (a Attr) Merge(b Attr) Attr {
|
func (a Attr) Merge(b Attr) Attr {
|
||||||
if b&AttrRegular > 0 {
|
if b&AttrRegular > 0 {
|
||||||
// Only keep bold attribute set by the system
|
// Only keep bold attribute set by the system
|
||||||
return b | (a & BoldForce)
|
return (b &^ AttrRegular) | (a & BoldForce)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a | b
|
return (a &^ AttrRegular) | b
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,6 +24,7 @@ const (
|
|||||||
AttrRegular = Attr(1 << 8)
|
AttrRegular = Attr(1 << 8)
|
||||||
AttrClear = Attr(1 << 9)
|
AttrClear = Attr(1 << 9)
|
||||||
BoldForce = Attr(1 << 10)
|
BoldForce = Attr(1 << 10)
|
||||||
|
FullBg = Attr(1 << 11)
|
||||||
|
|
||||||
Bold = Attr(1)
|
Bold = Attr(1)
|
||||||
Dim = Attr(1 << 1)
|
Dim = Attr(1 << 1)
|
||||||
|
|||||||
@@ -37,84 +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[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 = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeader"
|
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}
|
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) {
|
||||||
|
|||||||
295
src/tui/light.go
295
src/tui/light.go
@@ -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]
|
||||||
@@ -829,11 +1057,14 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, wind
|
|||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
w.fg = r.theme.Header.Color
|
w.fg = r.theme.Header.Color
|
||||||
w.bg = r.theme.HeaderBg.Color
|
w.bg = r.theme.HeaderBg.Color
|
||||||
|
case WindowFooter:
|
||||||
|
w.fg = r.theme.Footer.Color
|
||||||
|
w.bg = r.theme.FooterBg.Color
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
w.fg = r.theme.PreviewFg.Color
|
w.fg = r.theme.PreviewFg.Color
|
||||||
w.bg = r.theme.PreviewBg.Color
|
w.bg = r.theme.PreviewBg.Color
|
||||||
}
|
}
|
||||||
if erase && !w.bg.IsDefault() && w.border.shape != BorderNone {
|
if erase && !w.bg.IsDefault() && w.border.shape != BorderNone && w.height > 0 {
|
||||||
// fzf --color bg:blue --border --padding 1,2
|
// fzf --color bg:blue --border --padding 1,2
|
||||||
w.Erase()
|
w.Erase()
|
||||||
}
|
}
|
||||||
@@ -889,6 +1120,8 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
@@ -914,6 +1147,8 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
@@ -941,6 +1176,8 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
|
|||||||
color = ColInputBorder
|
color = ColInputBorder
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
color = ColHeaderBorder
|
color = ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
color = ColFooterBorder
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
|
|||||||
335
src/tui/light_test.go
Normal file
335
src/tui/light_test.go
Normal 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")
|
||||||
|
|
||||||
|
}
|
||||||
170
src/tui/tcell.go
170
src/tui/tcell.go
@@ -103,6 +103,7 @@ const (
|
|||||||
AttrRegular = Attr(1 << 7)
|
AttrRegular = Attr(1 << 7)
|
||||||
AttrClear = Attr(1 << 8)
|
AttrClear = Attr(1 << 8)
|
||||||
BoldForce = Attr(1 << 10)
|
BoldForce = Attr(1 << 10)
|
||||||
|
FullBg = Attr(1 << 11)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Bell() {
|
func (r *FullscreenRenderer) Bell() {
|
||||||
@@ -161,10 +162,10 @@ func (c Color) Style() tcell.Color {
|
|||||||
func (a Attr) Merge(b Attr) Attr {
|
func (a Attr) Merge(b Attr) Attr {
|
||||||
if b&AttrRegular > 0 {
|
if b&AttrRegular > 0 {
|
||||||
// Only keep bold attribute set by the system
|
// Only keep bold attribute set by the system
|
||||||
return b | (a & BoldForce)
|
return (b &^ AttrRegular) | (a & BoldForce)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a | b
|
return (a &^ AttrRegular) | b
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the following as private members of FullscreenRenderer instance
|
// handle the following as private members of FullscreenRenderer instance
|
||||||
@@ -353,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 {
|
||||||
@@ -379,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 {
|
||||||
@@ -441,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}
|
||||||
}
|
}
|
||||||
@@ -448,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}
|
||||||
}
|
}
|
||||||
@@ -459,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}
|
||||||
}
|
}
|
||||||
@@ -470,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}
|
||||||
}
|
}
|
||||||
@@ -481,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}
|
||||||
}
|
}
|
||||||
@@ -496,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}
|
||||||
@@ -568,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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,6 +756,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
|||||||
normal = ColNormal
|
normal = ColNormal
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
normal = ColHeader
|
normal = ColHeader
|
||||||
|
case WindowFooter:
|
||||||
|
normal = ColFooter
|
||||||
case WindowInput:
|
case WindowInput:
|
||||||
normal = ColInput
|
normal = ColInput
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
@@ -865,6 +1023,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
|||||||
style = ColListBorder.style()
|
style = ColListBorder.style()
|
||||||
case WindowHeader:
|
case WindowHeader:
|
||||||
style = ColHeaderBorder.style()
|
style = ColHeaderBorder.style()
|
||||||
|
case WindowFooter:
|
||||||
|
style = ColFooterBorder.style()
|
||||||
case WindowInput:
|
case WindowInput:
|
||||||
style = ColInputBorder.style()
|
style = ColInputBorder.style()
|
||||||
case WindowPreview:
|
case WindowPreview:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func TtyIn(ttyDefault string) (*os.File, error) {
|
|||||||
return openTtyIn(ttyDefault)
|
return openTtyIn(ttyDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TtyIn returns terminal device to write to
|
// TtyOut returns terminal device to write to
|
||||||
func TtyOut(ttyDefault string) (*os.File, error) {
|
func TtyOut(ttyDefault string) (*os.File, error) {
|
||||||
return openTtyOut(ttyDefault)
|
return openTtyOut(ttyDefault)
|
||||||
}
|
}
|
||||||
|
|||||||
145
src/tui/tui.go
145
src/tui/tui.go
@@ -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,8 @@ const (
|
|||||||
Jump
|
Jump
|
||||||
JumpCancel
|
JumpCancel
|
||||||
ClickHeader
|
ClickHeader
|
||||||
|
ClickFooter
|
||||||
|
Multi
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t EventType) AsEvent() Event {
|
func (t EventType) AsEvent() Event {
|
||||||
@@ -246,6 +303,14 @@ const (
|
|||||||
colMagenta
|
colMagenta
|
||||||
colCyan
|
colCyan
|
||||||
colWhite
|
colWhite
|
||||||
|
colGrey
|
||||||
|
colBrightRed
|
||||||
|
colBrightGreen
|
||||||
|
colBrightYellow
|
||||||
|
colBrightBlue
|
||||||
|
colBrightMagenta
|
||||||
|
colBrightCyan
|
||||||
|
colBrightWhite
|
||||||
)
|
)
|
||||||
|
|
||||||
type FillReturn int
|
type FillReturn int
|
||||||
@@ -273,6 +338,10 @@ func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
|
|||||||
return ColorPair{fg, bg, attr}
|
return ColorPair{fg, bg, attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NoColorPair() ColorPair {
|
||||||
|
return ColorPair{-1, -1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
func (p ColorPair) Fg() Color {
|
func (p ColorPair) Fg() Color {
|
||||||
return p.fg
|
return p.fg
|
||||||
}
|
}
|
||||||
@@ -285,6 +354,10 @@ func (p ColorPair) Attr() Attr {
|
|||||||
return p.attr
|
return p.attr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p ColorPair) IsFullBgMarker() bool {
|
||||||
|
return p.attr&FullBg > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (p ColorPair) HasBg() bool {
|
func (p ColorPair) HasBg() bool {
|
||||||
return p.attr&Reverse == 0 && p.bg != colDefault ||
|
return p.attr&Reverse == 0 && p.bg != colDefault ||
|
||||||
p.attr&Reverse > 0 && p.fg != colDefault
|
p.attr&Reverse > 0 && p.fg != colDefault
|
||||||
@@ -329,6 +402,7 @@ func (p ColorPair) MergeNonDefault(other ColorPair) ColorPair {
|
|||||||
type ColorTheme struct {
|
type ColorTheme struct {
|
||||||
Colored bool
|
Colored bool
|
||||||
Input ColorAttr
|
Input ColorAttr
|
||||||
|
Ghost ColorAttr
|
||||||
Disabled ColorAttr
|
Disabled ColorAttr
|
||||||
Fg ColorAttr
|
Fg ColorAttr
|
||||||
Bg ColorAttr
|
Bg ColorAttr
|
||||||
@@ -358,6 +432,10 @@ type ColorTheme struct {
|
|||||||
HeaderBg ColorAttr
|
HeaderBg ColorAttr
|
||||||
HeaderBorder ColorAttr
|
HeaderBorder ColorAttr
|
||||||
HeaderLabel ColorAttr
|
HeaderLabel ColorAttr
|
||||||
|
Footer ColorAttr
|
||||||
|
FooterBg ColorAttr
|
||||||
|
FooterBorder ColorAttr
|
||||||
|
FooterLabel ColorAttr
|
||||||
Separator ColorAttr
|
Separator ColorAttr
|
||||||
Scrollbar ColorAttr
|
Scrollbar ColorAttr
|
||||||
Border ColorAttr
|
Border ColorAttr
|
||||||
@@ -490,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: ' ',
|
||||||
@@ -611,6 +689,7 @@ const (
|
|||||||
WindowPreview
|
WindowPreview
|
||||||
WindowInput
|
WindowInput
|
||||||
WindowHeader
|
WindowHeader
|
||||||
|
WindowFooter
|
||||||
)
|
)
|
||||||
|
|
||||||
type Renderer interface {
|
type Renderer interface {
|
||||||
@@ -701,9 +780,11 @@ var (
|
|||||||
ColNormal ColorPair
|
ColNormal ColorPair
|
||||||
ColInput ColorPair
|
ColInput ColorPair
|
||||||
ColDisabled ColorPair
|
ColDisabled ColorPair
|
||||||
|
ColGhost ColorPair
|
||||||
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
|
||||||
@@ -718,6 +799,9 @@ var (
|
|||||||
ColHeader ColorPair
|
ColHeader ColorPair
|
||||||
ColHeaderBorder ColorPair
|
ColHeaderBorder ColorPair
|
||||||
ColHeaderLabel ColorPair
|
ColHeaderLabel ColorPair
|
||||||
|
ColFooter ColorPair
|
||||||
|
ColFooterBorder ColorPair
|
||||||
|
ColFooterLabel ColorPair
|
||||||
ColSeparator ColorPair
|
ColSeparator ColorPair
|
||||||
ColScrollbar ColorPair
|
ColScrollbar ColorPair
|
||||||
ColGapLine ColorPair
|
ColGapLine ColorPair
|
||||||
@@ -756,10 +840,12 @@ func EmptyTheme() *ColorTheme {
|
|||||||
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Marker: ColorAttr{colUndefined, AttrUndefined},
|
Marker: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Header: ColorAttr{colUndefined, AttrUndefined},
|
Header: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Footer: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Border: ColorAttr{colUndefined, AttrUndefined},
|
Border: ColorAttr{colUndefined, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
ListLabel: ColorAttr{colUndefined, AttrUndefined},
|
ListLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
ListBorder: ColorAttr{colUndefined, AttrUndefined},
|
ListBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
@@ -775,6 +861,9 @@ func EmptyTheme() *ColorTheme {
|
|||||||
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -804,6 +893,7 @@ func NoColorTheme() *ColorTheme {
|
|||||||
Header: ColorAttr{colDefault, AttrUndefined},
|
Header: ColorAttr{colDefault, AttrUndefined},
|
||||||
Border: ColorAttr{colDefault, AttrUndefined},
|
Border: ColorAttr{colDefault, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{colDefault, AttrUndefined},
|
BorderLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
Ghost: ColorAttr{colDefault, Dim},
|
||||||
Disabled: ColorAttr{colDefault, AttrUndefined},
|
Disabled: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: ColorAttr{colDefault, AttrUndefined},
|
PreviewFg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: ColorAttr{colDefault, AttrUndefined},
|
PreviewBg: ColorAttr{colDefault, AttrUndefined},
|
||||||
@@ -821,6 +911,9 @@ func NoColorTheme() *ColorTheme {
|
|||||||
HeaderBg: ColorAttr{colDefault, AttrUndefined},
|
HeaderBg: ColorAttr{colDefault, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
|
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
|
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
GapLine: ColorAttr{colDefault, AttrUndefined},
|
GapLine: ColorAttr{colDefault, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -838,18 +931,20 @@ 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},
|
||||||
Border: ColorAttr{colBlack, AttrUndefined},
|
Footer: ColorAttr{colCyan, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{colWhite, AttrUndefined},
|
Border: ColorAttr{colDefault, Dim},
|
||||||
|
BorderLabel: ColorAttr{colDefault, AttrUndefined},
|
||||||
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
@@ -864,6 +959,12 @@ func init() {
|
|||||||
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputLabel: ColorAttr{colUndefined, AttrUndefined},
|
InputLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -888,8 +989,10 @@ func init() {
|
|||||||
Cursor: ColorAttr{161, AttrUndefined},
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Marker: ColorAttr{168, AttrUndefined},
|
Marker: ColorAttr{168, AttrUndefined},
|
||||||
Header: ColorAttr{109, AttrUndefined},
|
Header: ColorAttr{109, AttrUndefined},
|
||||||
|
Footer: ColorAttr{109, AttrUndefined},
|
||||||
Border: ColorAttr{59, AttrUndefined},
|
Border: ColorAttr{59, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{145, AttrUndefined},
|
BorderLabel: ColorAttr{145, AttrUndefined},
|
||||||
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
@@ -904,6 +1007,12 @@ func init() {
|
|||||||
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
InputBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
InputBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
InputLabel: ColorAttr{colUndefined, AttrUndefined},
|
InputLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -928,8 +1037,10 @@ func init() {
|
|||||||
Cursor: ColorAttr{161, AttrUndefined},
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Marker: ColorAttr{168, AttrUndefined},
|
Marker: ColorAttr{168, AttrUndefined},
|
||||||
Header: ColorAttr{31, AttrUndefined},
|
Header: ColorAttr{31, AttrUndefined},
|
||||||
|
Footer: ColorAttr{31, AttrUndefined},
|
||||||
Border: ColorAttr{145, AttrUndefined},
|
Border: ColorAttr{145, AttrUndefined},
|
||||||
BorderLabel: ColorAttr{59, AttrUndefined},
|
BorderLabel: ColorAttr{59, AttrUndefined},
|
||||||
|
Ghost: ColorAttr{colUndefined, Dim},
|
||||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
@@ -947,6 +1058,9 @@ func init() {
|
|||||||
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterBorder: ColorAttr{colUndefined, AttrUndefined},
|
||||||
|
FooterLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||||
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
GapLine: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Nth: ColorAttr{colUndefined, AttrUndefined},
|
Nth: ColorAttr{colUndefined, AttrUndefined},
|
||||||
}
|
}
|
||||||
@@ -982,6 +1096,7 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp
|
|||||||
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
|
||||||
theme.Marker = o(baseTheme.Marker, theme.Marker)
|
theme.Marker = o(baseTheme.Marker, theme.Marker)
|
||||||
theme.Header = o(baseTheme.Header, theme.Header)
|
theme.Header = o(baseTheme.Header, theme.Header)
|
||||||
|
theme.Footer = o(baseTheme.Footer, theme.Footer)
|
||||||
theme.Border = o(baseTheme.Border, theme.Border)
|
theme.Border = o(baseTheme.Border, theme.Border)
|
||||||
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
|
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
|
||||||
|
|
||||||
@@ -995,6 +1110,7 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp
|
|||||||
theme.SelectedFg = o(theme.ListFg, theme.SelectedFg)
|
theme.SelectedFg = o(theme.ListFg, theme.SelectedFg)
|
||||||
theme.SelectedBg = o(theme.ListBg, theme.SelectedBg)
|
theme.SelectedBg = o(theme.ListBg, theme.SelectedBg)
|
||||||
theme.SelectedMatch = o(theme.Match, theme.SelectedMatch)
|
theme.SelectedMatch = o(theme.Match, theme.SelectedMatch)
|
||||||
|
theme.Ghost = o(theme.Input, theme.Ghost)
|
||||||
theme.Disabled = o(theme.Input, theme.Disabled)
|
theme.Disabled = o(theme.Input, theme.Disabled)
|
||||||
theme.Gutter = o(theme.DarkBg, theme.Gutter)
|
theme.Gutter = o(theme.DarkBg, theme.Gutter)
|
||||||
theme.PreviewFg = o(theme.Fg, theme.PreviewFg)
|
theme.PreviewFg = o(theme.Fg, theme.PreviewFg)
|
||||||
@@ -1034,6 +1150,10 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp
|
|||||||
theme.HeaderBorder = o(theme.Border, theme.HeaderBorder)
|
theme.HeaderBorder = o(theme.Border, theme.HeaderBorder)
|
||||||
theme.HeaderLabel = o(theme.BorderLabel, theme.HeaderLabel)
|
theme.HeaderLabel = o(theme.BorderLabel, theme.HeaderLabel)
|
||||||
|
|
||||||
|
theme.FooterBg = o(theme.Bg, theme.FooterBg)
|
||||||
|
theme.FooterBorder = o(theme.Border, theme.FooterBorder)
|
||||||
|
theme.FooterLabel = o(theme.BorderLabel, theme.FooterLabel)
|
||||||
|
|
||||||
initPalette(theme)
|
initPalette(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1051,15 +1171,17 @@ func initPalette(theme *ColorTheme) {
|
|||||||
ColNormal = pair(theme.ListFg, theme.ListBg)
|
ColNormal = pair(theme.ListFg, theme.ListBg)
|
||||||
ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
|
ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
|
||||||
ColInput = pair(theme.Input, theme.InputBg)
|
ColInput = pair(theme.Input, theme.InputBg)
|
||||||
ColDisabled = pair(theme.Disabled, theme.ListBg)
|
ColGhost = pair(theme.Ghost, theme.InputBg)
|
||||||
|
ColDisabled = pair(theme.Disabled, theme.InputBg)
|
||||||
ColMatch = pair(theme.Match, theme.ListBg)
|
ColMatch = pair(theme.Match, theme.ListBg)
|
||||||
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)
|
||||||
@@ -1086,6 +1208,9 @@ func initPalette(theme *ColorTheme) {
|
|||||||
ColHeader = pair(theme.Header, theme.HeaderBg)
|
ColHeader = pair(theme.Header, theme.HeaderBg)
|
||||||
ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
|
ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
|
||||||
ColHeaderLabel = pair(theme.HeaderLabel, theme.HeaderBg)
|
ColHeaderLabel = pair(theme.HeaderLabel, theme.HeaderBg)
|
||||||
|
ColFooter = pair(theme.Footer, theme.FooterBg)
|
||||||
|
ColFooterBorder = pair(theme.FooterBorder, theme.FooterBg)
|
||||||
|
ColFooterLabel = pair(theme.FooterLabel, theme.FooterBg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runeWidth(r rune) int {
|
func runeWidth(r rune) int {
|
||||||
|
|||||||
@@ -184,9 +184,10 @@ func (chars *Chars) TrailingWhitespaces() int {
|
|||||||
return whitespaces
|
return whitespaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chars *Chars) TrimTrailingWhitespaces() {
|
func (chars *Chars) TrimTrailingWhitespaces(maxIndex int) {
|
||||||
whitespaces := chars.TrailingWhitespaces()
|
whitespaces := chars.TrailingWhitespaces()
|
||||||
chars.slice = chars.slice[0 : len(chars.slice)-whitespaces]
|
end := len(chars.slice) - whitespaces
|
||||||
|
chars.slice = chars.slice[0:Max(end, maxIndex)]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (chars *Chars) TrimSuffix(runes []rune) {
|
func (chars *Chars) TrimSuffix(runes []rune) {
|
||||||
|
|||||||
39
src/util/concurrent_set.go
Normal file
39
src/util/concurrent_set.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// ConcurrentSet is a thread-safe set implementation.
|
||||||
|
type ConcurrentSet[T comparable] struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
items map[T]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConcurrentSet creates a new ConcurrentSet.
|
||||||
|
func NewConcurrentSet[T comparable]() *ConcurrentSet[T] {
|
||||||
|
return &ConcurrentSet[T]{
|
||||||
|
items: make(map[T]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds an item to the set.
|
||||||
|
func (s *ConcurrentSet[T]) Add(item T) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
s.items[item] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes an item from the set.
|
||||||
|
func (s *ConcurrentSet[T]) Remove(item T) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
delete(s.items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach iterates over each item in the set and applies the provided function.
|
||||||
|
func (s *ConcurrentSet[T]) ForEach(fn func(item T)) {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
for item := range s.items {
|
||||||
|
fn(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,24 +97,12 @@ func Min32(first int32, second int32) int32 {
|
|||||||
|
|
||||||
// Constrain32 limits the given 32-bit integer with the upper and lower bounds
|
// Constrain32 limits the given 32-bit integer with the upper and lower bounds
|
||||||
func Constrain32(val int32, min int32, max int32) int32 {
|
func Constrain32(val int32, min int32, max int32) int32 {
|
||||||
if val < min {
|
return Max32(Min32(val, max), min)
|
||||||
return min
|
|
||||||
}
|
|
||||||
if val > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constrain limits the given integer with the upper and lower bounds
|
// Constrain limits the given integer with the upper and lower bounds
|
||||||
func Constrain(val int, min int, max int) int {
|
func Constrain(val int, min int, max int) int {
|
||||||
if val < min {
|
return Max(Min(val, max), min)
|
||||||
return min
|
|
||||||
}
|
|
||||||
if val > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AsUint16(val int) uint16 {
|
func AsUint16(val int) uint16 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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]) }
|
||||||
@@ -1859,9 +1898,9 @@ class TestCore < TestInteractive
|
|||||||
line = nil
|
line = nil
|
||||||
tmux.until { |lines| line = lines.index('> 1') }
|
tmux.until { |lines| line = lines.index('> 1') }
|
||||||
tmux.send_keys :PgDn
|
tmux.send_keys :PgDn
|
||||||
tmux.until { |lines| assert_includes lines[line + 4], "> 5" }
|
tmux.until { |lines| assert_includes lines[line + 4], '> 5' }
|
||||||
tmux.send_keys :Space
|
tmux.send_keys :Space
|
||||||
tmux.until { |lines| assert_includes lines[line + 2], "> 5" }
|
tmux.until { |lines| assert_includes lines[line + 2], '> 5' }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_no_input_query
|
def test_no_input_query
|
||||||
@@ -1930,7 +1969,10 @@ class TestCore < TestInteractive
|
|||||||
|
|
||||||
def test_change_header_on_header_window
|
def test_change_header_on_header_window
|
||||||
tmux.send_keys %(seq 100 | #{FZF} --list-border --input-border --bind 'start:change-header(foo),space:change-header(bar)'), :Enter
|
tmux.send_keys %(seq 100 | #{FZF} --list-border --input-border --bind 'start:change-header(foo),space:change-header(bar)'), :Enter
|
||||||
tmux.until { |lines| assert lines.any_include?('foo') }
|
tmux.until do |lines|
|
||||||
|
assert lines.any_include?('100/100')
|
||||||
|
assert lines.any_include?('foo')
|
||||||
|
end
|
||||||
tmux.send_keys :Space
|
tmux.send_keys :Space
|
||||||
tmux.until { |lines| assert lines.any_include?('bar') }
|
tmux.until { |lines| assert lines.any_include?('bar') }
|
||||||
end
|
end
|
||||||
@@ -1939,4 +1981,122 @@ class TestCore < TestInteractive
|
|||||||
tmux.send_keys %(echo -en "foo\n" | fzf --read0 --no-multi-line), :Enter
|
tmux.send_keys %(echo -en "foo\n" | fzf --read0 --no-multi-line), :Enter
|
||||||
tmux.until { |lines| assert_includes lines, '> foo␊' }
|
tmux.until { |lines| assert_includes lines, '> foo␊' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_async_transform
|
||||||
|
time = Time.now
|
||||||
|
tmux.send_keys %(
|
||||||
|
seq 100 | #{FZF} --style full --border --preview : \
|
||||||
|
--bind 'focus:bg-transform-header(sleep 0.5; echo th.)' \
|
||||||
|
--bind 'focus:+bg-transform-footer(sleep 0.5; echo tf.)' \
|
||||||
|
--bind 'focus:+bg-transform-border-label(sleep 0.5; echo tbl.)' \
|
||||||
|
--bind "focus:+bg-transform-preview-label(sleep 0.5; echo tpl.)" \
|
||||||
|
--bind 'focus:+bg-transform-input-label(sleep 0.5; echo til.)' \
|
||||||
|
--bind 'focus:+bg-transform-list-label(sleep 0.5; echo tll.)' \
|
||||||
|
--bind 'focus:+bg-transform-header-label(sleep 0.5; echo thl.)' \
|
||||||
|
--bind 'focus:+bg-transform-footer-label(sleep 0.5; echo tfl.)' \
|
||||||
|
--bind 'focus:+bg-transform-prompt(sleep 0.5; echo tp.)' \
|
||||||
|
--bind 'focus:+bg-transform-ghost(sleep 0.5; echo tg.)'
|
||||||
|
).strip, :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert lines.any_include?('100/100')
|
||||||
|
%w[th tf tbl tpl til tll thl tfl tp tg].each do
|
||||||
|
assert lines.any_include?("#{it}.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elapsed = Time.now - time
|
||||||
|
assert elapsed < 2
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_bg_cancel
|
||||||
|
tmux.send_keys %(seq 0 1 | #{FZF} --bind 'space:bg-cancel+bg-transform-header(sleep {}; echo [{}])'), :Enter
|
||||||
|
tmux.until { assert_equal 2, it.match_count }
|
||||||
|
tmux.send_keys '1'
|
||||||
|
tmux.until { assert_equal 1, it.match_count }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { assert_equal 2, it.match_count }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { |lines| assert lines.any_include?('[0]') }
|
||||||
|
sleep 2
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert lines.any_include?('[0]')
|
||||||
|
refute lines.any_include?('[1]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_order
|
||||||
|
tmux.send_keys %(seq 100 | #{FZF} --bind='focus:preview(echo boom)+change-footer(bam)'), :Enter
|
||||||
|
tmux.until { assert_equal 100, it.match_count }
|
||||||
|
tmux.until { assert it.any_include?('boom') }
|
||||||
|
tmux.until { assert it.any_include?('bam') }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_multi_event
|
||||||
|
tmux.send_keys %(seq 100 | #{FZF} --multi --bind 'multi:transform-footer:(( FZF_SELECT_COUNT )) && echo "Selected $FZF_SELECT_COUNT item(s)"'), :Enter
|
||||||
|
tmux.until { assert_equal 100, it.match_count }
|
||||||
|
tmux.send_keys :Tab
|
||||||
|
tmux.until { assert_equal 1, it.select_count }
|
||||||
|
tmux.until { assert it.any_include?('Selected 1 item(s)') }
|
||||||
|
tmux.send_keys :Tab
|
||||||
|
tmux.until { assert_equal 0, it.select_count }
|
||||||
|
tmux.until { refute it.any_include?('Selected') }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_preserve_selection_on_revision_bump
|
||||||
|
tmux.send_keys %(seq 100 | #{FZF} --multi --sync --query "'1" --bind 'a:select-all+change-header(pressed a),b:change-header(pressed b)+change-nth(1),c:exclude'), :Enter
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 20, it.match_count
|
||||||
|
assert_equal 0, it.select_count
|
||||||
|
end
|
||||||
|
tmux.send_keys :a
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 20, it.match_count
|
||||||
|
assert_equal 20, it.select_count
|
||||||
|
assert it.any_include?('pressed a')
|
||||||
|
end
|
||||||
|
tmux.send_keys :b
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 20, it.match_count
|
||||||
|
assert_equal 20, it.select_count
|
||||||
|
refute it.any_include?('pressed a')
|
||||||
|
assert it.any_include?('pressed b')
|
||||||
|
end
|
||||||
|
tmux.send_keys :a
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 20, it.match_count
|
||||||
|
assert_equal 20, it.select_count
|
||||||
|
assert it.any_include?('pressed a')
|
||||||
|
refute it.any_include?('pressed b')
|
||||||
|
end
|
||||||
|
tmux.send_keys :c
|
||||||
|
tmux.until do
|
||||||
|
assert_equal 19, it.match_count
|
||||||
|
assert_equal 19, it.select_count
|
||||||
|
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
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -304,11 +304,11 @@ class TestFilter < TestBase
|
|||||||
def test_boundary_match
|
def test_boundary_match
|
||||||
# Underscore boundaries should be ranked lower
|
# Underscore boundaries should be ranked lower
|
||||||
{
|
{
|
||||||
default: [' x '] + %w[/x/ [x] -x- -x_ _x- _x_],
|
default: [' xyz '] + %w[/xyz/ [xyz] -xyz- -xyz_ _xyz- _xyz_],
|
||||||
path: ['/x/', ' x '] + %w[[x] -x- -x_ _x- _x_],
|
path: ['/xyz/', ' xyz '] + %w[[xyz] -xyz- -xyz_ _xyz- _xyz_],
|
||||||
history: ['[x]', '-x-', ' x '] + %w[/x/ -x_ _x- _x_]
|
history: ['[xyz]', '-xyz-', ' xyz '] + %w[/xyz/ -xyz_ _xyz- _xyz_]
|
||||||
}.each do |scheme, expected|
|
}.each do |scheme, expected|
|
||||||
result = `printf -- 'xxx\n-xx\nxx-\n_x_\n_x-\n-x_\n[x]\n-x-\n x \n/x/\n' | #{FZF} -f"'x'" --scheme=#{scheme}`.lines(chomp: true)
|
result = `printf -- 'xxyzx\n-xxyz\nxyzx-\n_xyz_\n_xyz-\n-xyz_\n[xyz]\n-xyz-\n xyz \n/xyz/\n' | #{FZF} -f"'xyz'" --scheme=#{scheme}`.lines(chomp: true)
|
||||||
assert_equal expected, result
|
assert_equal expected, result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ class TestLayout < TestInteractive
|
|||||||
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
|
||||||
block = <<~OUTPUT
|
block = <<~OUTPUT
|
||||||
> 4
|
> 4
|
||||||
997/997
|
|
||||||
>
|
|
||||||
3
|
3
|
||||||
2
|
2
|
||||||
1
|
1
|
||||||
|
997/997
|
||||||
|
>
|
||||||
foobar
|
foobar
|
||||||
OUTPUT
|
OUTPUT
|
||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
@@ -53,10 +53,10 @@ class TestLayout < TestInteractive
|
|||||||
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
|
||||||
block = <<~OUTPUT
|
block = <<~OUTPUT
|
||||||
foobar
|
foobar
|
||||||
|
> < 997/997
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
> < 997/997
|
|
||||||
> 4
|
> 4
|
||||||
OUTPUT
|
OUTPUT
|
||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
@@ -148,10 +148,10 @@ class TestLayout < TestInteractive
|
|||||||
│
|
│
|
||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
│ 2/2
|
|
||||||
│ >
|
|
||||||
│ 2
|
│ 2
|
||||||
│ 1
|
│ 1
|
||||||
|
│ 2/2
|
||||||
|
│ >
|
||||||
│ foo
|
│ foo
|
||||||
╰───────
|
╰───────
|
||||||
OUTPUT
|
OUTPUT
|
||||||
@@ -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
|
||||||
@@ -609,11 +609,11 @@ class TestLayout < TestInteractive
|
|||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
╰──────────
|
╰──────────
|
||||||
|
2
|
||||||
|
1
|
||||||
98/98 ─
|
98/98 ─
|
||||||
>
|
>
|
||||||
╭──────────
|
╭──────────
|
||||||
│ 2
|
|
||||||
│ 1
|
|
||||||
│ hello
|
│ hello
|
||||||
╰──────────
|
╰──────────
|
||||||
BLOCK
|
BLOCK
|
||||||
@@ -666,12 +666,12 @@ class TestLayout < TestInteractive
|
|||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
╰──────────
|
╰──────────
|
||||||
|
98/98 ─
|
||||||
|
>
|
||||||
╔══════════
|
╔══════════
|
||||||
║ 2
|
║ 2
|
||||||
║ 1
|
║ 1
|
||||||
╚══════════
|
╚══════════
|
||||||
98/98 ─
|
|
||||||
>
|
|
||||||
BLOCK
|
BLOCK
|
||||||
tmux.until { assert_block(block1, it) }
|
tmux.until { assert_block(block1, it) }
|
||||||
|
|
||||||
@@ -979,6 +979,128 @@ class TestLayout < TestInteractive
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_layout_default_with_footer
|
||||||
|
prefix = %[
|
||||||
|
seq 3 | #{FZF} --no-list-border --height ~100% \
|
||||||
|
--border sharp --footer "$(seq 201 202)" --footer-label FOOT --footer-label-pos 3 \
|
||||||
|
--header-label HEAD --header-label-pos 3:bottom \
|
||||||
|
--bind 'space:transform-footer-label(echo foot)+change-header-label(head)'
|
||||||
|
].strip + ' '
|
||||||
|
suffixes = [
|
||||||
|
%(),
|
||||||
|
%[--header "$(seq 101 102)"],
|
||||||
|
%[--header "$(seq 101 102)" --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2 --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp],
|
||||||
|
%[--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 --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-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)" --footer-border sharp --input-border line],
|
||||||
|
%[--header "$(seq 101 102)" --style full:sharp --header-first]
|
||||||
|
]
|
||||||
|
output = <<~BLOCK
|
||||||
|
┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌───────── ┌──────── ┌──────── ┌─────────
|
||||||
|
│ 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 │ 202 │ 202 │ │ 201 │ │ 201
|
||||||
|
│ ──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 │ └────── │ └───────
|
||||||
|
│ 2 │ 2 │ 2 │ 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 │ > 1 │ │ 2
|
||||||
|
│ > │ 102 │ > │ 102 │ > │ │ 101 │ > │ │ 101 │ │ 101 │ │ 101 │ └────── │ └─────── │ └────── │ 101 │ │ > 1
|
||||||
|
└──────── │ 3/3 ─ │ 101 │ 1/1 ─ │ 101 │ │ 102 │ ┌────── │ │ 102 │ │ 102 │ │ 102 │ ┌────── │ ┌─────── │ ┌────── │ 102 │ └───────
|
||||||
|
│ > │ 102 │ > │ 102 │ └─HEAD─ │ │ 101 │ └─HEAD─ │ └─HEAD─ │ └─HEAD─ │ │ 101 │ │ 1/1 │ │ 101 │ ─────── │ ┌───────
|
||||||
|
└──────── └──────── └──────── └──────── │ 3/3 ─ │ │ 102 │ 1/1 ─ │ 1/1 ─ │ 1/1 ─ │ │ 102 │ │ > │ │ 102 │ 3/3 │ │ >
|
||||||
|
│ > │ └─HEAD─ │ > │ > │ > │ └─HEAD─ │ └─────── │ └─HEAD─ │ > │ └───────
|
||||||
|
└──────── └──────── └──────── └──────── └──────── │ 1/1 ─ │ ┌─────── └──────── └──────── │ ┌───────
|
||||||
|
│ > │ │ 101 │ │ 101
|
||||||
|
└──────── │ │ 102 │ │ 102
|
||||||
|
│ └─HEAD── │ └─HEAD──
|
||||||
|
└───────── └─────────
|
||||||
|
BLOCK
|
||||||
|
|
||||||
|
expects = []
|
||||||
|
output.each_line.first.scan(/\S+/) do
|
||||||
|
offset = Regexp.last_match.offset(0)
|
||||||
|
expects << output.lines.filter_map { it[offset[0]...offset[1]]&.strip }.take_while { !it.empty? }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
suffixes.zip(expects).each do |suffix, block|
|
||||||
|
tmux.send_keys(prefix + suffix, :Enter)
|
||||||
|
tmux.until { assert_block(block, it) }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { assert_block(block.downcase, it) }
|
||||||
|
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_layout_reverse_list_with_footer
|
||||||
|
prefix = %[
|
||||||
|
seq 3 | #{FZF} --layout reverse-list --no-list-border --height ~100% \
|
||||||
|
--border sharp --footer "$(seq 201 202)" --footer-label FOOT --footer-label-pos 3 \
|
||||||
|
--header-label HEAD --header-label-pos 3:bottom \
|
||||||
|
--bind 'space:transform-footer-label(echo foot)+change-header-label(head)'
|
||||||
|
].strip + ' '
|
||||||
|
suffixes = [
|
||||||
|
%(),
|
||||||
|
%[--header "$(seq 101 102)"],
|
||||||
|
%[--header "$(seq 101 102)" --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2],
|
||||||
|
%[--header "$(seq 101 102)" --header-lines 2 --header-first],
|
||||||
|
%[--header "$(seq 101 102)" --header-border sharp],
|
||||||
|
%[--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-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 --no-input],
|
||||||
|
%[--header "$(seq 101 102)" --footer-border sharp --input-border line],
|
||||||
|
%[--header "$(seq 101 102)" --style full:sharp --header-first]
|
||||||
|
]
|
||||||
|
output = <<~BLOCK
|
||||||
|
┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌──────── ┌───────── ┌──────── ┌──────── ┌─────────
|
||||||
|
│ 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
|
||||||
|
│ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT─ │ ──FOOT── │ ──FOOT─ │ │ 202 │ │ 202
|
||||||
|
│ > 1 │ > 1 │ > 1 │ 1 │ 1 │ > 1 │ > 1 │ 1 │ ┌────── │ ┌─────── │ ┌────── │ └────── │ └───────
|
||||||
|
│ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ 2 │ │ 1 │ │ 1 │ │ 1 │ > 1 │ ┌───────
|
||||||
|
│ 3 │ 3 │ 3 │ > 3 │ > 3 │ 3 │ 3 │ > 3 │ │ 2 │ │ 2 │ │ 2 │ 2 │ │ > 1
|
||||||
|
│ 3/3 ─ │ 101 │ 3/3 ─ │ 101 │ 1/1 ─ │ ┌────── │ 3/3 ─ │ ┌────── │ └────── │ └─────── │ └────── │ 3 │ │ 2
|
||||||
|
│ > │ 102 │ > │ 102 │ > │ │ 101 │ > │ │ 101 │ > 3 │ > 3 │ > 3 │ 101 │ │ 3
|
||||||
|
└──────── │ 3/3 ─ │ 101 │ 1/1 ─ │ 101 │ │ 102 │ ┌────── │ │ 102 │ ┌────── │ ┌─────── │ ┌────── │ 102 │ └───────
|
||||||
|
│ > │ 102 │ > │ 102 │ └─HEAD─ │ │ 101 │ └─HEAD─ │ │ 101 │ │ 1/1 │ │ 101 │ ─────── │ ┌───────
|
||||||
|
└──────── └──────── └──────── └──────── │ 3/3 ─ │ │ 102 │ 1/1 ─ │ │ 102 │ │ > │ │ 102 │ 3/3 │ │ >
|
||||||
|
│ > │ └─HEAD─ │ > │ └─HEAD─ │ └─────── │ └─HEAD─ │ > │ └───────
|
||||||
|
└──────── └──────── └──────── │ 1/1 ─ │ ┌─────── └──────── └──────── │ ┌───────
|
||||||
|
│ > │ │ 101 │ │ 101
|
||||||
|
└──────── │ │ 102 │ │ 102
|
||||||
|
│ └─HEAD── │ └─HEAD──
|
||||||
|
└───────── └─────────
|
||||||
|
BLOCK
|
||||||
|
|
||||||
|
expects = []
|
||||||
|
output.each_line.first.scan(/\S+/) do
|
||||||
|
offset = Regexp.last_match.offset(0)
|
||||||
|
expects << output.lines.filter_map { it[offset[0]...offset[1]]&.strip }.take_while { !it.empty? }.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
suffixes.zip(expects).each do |suffix, block|
|
||||||
|
tmux.send_keys(prefix + suffix, :Enter)
|
||||||
|
tmux.until { assert_block(block, it) }
|
||||||
|
tmux.send_keys :Space
|
||||||
|
tmux.until { assert_block(block.downcase, it) }
|
||||||
|
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_change_header_and_label_at_once
|
def test_change_header_and_label_at_once
|
||||||
tmux.send_keys %(seq 10 | #{FZF} --border sharp --header-border sharp --header-label-pos 3 --bind 'focus:change-header(header)+change-header-label(label)'), :Enter
|
tmux.send_keys %(seq 10 | #{FZF} --border sharp --header-border sharp --header-label-pos 3 --bind 'focus:change-header(header)+change-header-label(label)'), :Enter
|
||||||
block = <<~BLOCK
|
block = <<~BLOCK
|
||||||
@@ -992,6 +1114,36 @@ class TestLayout < TestInteractive
|
|||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_label_truncation
|
||||||
|
command = <<~CMD
|
||||||
|
seq 10 | #{FZF} --style full --border --header-lines=1 --preview ':' \\
|
||||||
|
--border-label "#{'b' * 1000}" \\
|
||||||
|
--preview-label "#{'p' * 1000}" \\
|
||||||
|
--header-label "#{'h' * 1000}" \\
|
||||||
|
--header-label "#{'h' * 1000}" \\
|
||||||
|
--input-label "#{'i' * 1000}" \\
|
||||||
|
--list-label "#{'l' * 1000}"
|
||||||
|
CMD
|
||||||
|
writelines(command.lines.map(&:chomp))
|
||||||
|
tmux.send_keys("sh #{tempname}", :Enter)
|
||||||
|
tmux.until do |lines|
|
||||||
|
text = lines.join
|
||||||
|
assert_includes text, 'b··'
|
||||||
|
assert_includes text, 'l··p'
|
||||||
|
assert_includes text, 'p··'
|
||||||
|
assert_includes text, 'h··'
|
||||||
|
assert_includes text, 'i··'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_separator_no_ellipsis
|
||||||
|
tmux.send_keys %(seq 10 | #{FZF} --separator "$(seq 1000 | tr '\\n' ' ')"), :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
assert_equal 10, lines.match_count
|
||||||
|
refute_includes lines.join, '··'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_header_border_no_pointer_and_marker
|
def test_header_border_no_pointer_and_marker
|
||||||
tmux.send_keys %(seq 10 | #{FZF} --header-lines 1 --header-border sharp --no-list-border --pointer '' --marker ''), :Enter
|
tmux.send_keys %(seq 10 | #{FZF} --header-lines 1 --header-border sharp --no-list-border --pointer '' --marker ''), :Enter
|
||||||
block = <<~BLOCK
|
block = <<~BLOCK
|
||||||
@@ -1003,4 +1155,115 @@ class TestLayout < TestInteractive
|
|||||||
BLOCK
|
BLOCK
|
||||||
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
|
||||||
|
skip unless ENV['LONGTEST']
|
||||||
|
|
||||||
|
base = [
|
||||||
|
'--pointer=@',
|
||||||
|
'--exact',
|
||||||
|
'--query=123',
|
||||||
|
'--header="$(seq 101 103)"',
|
||||||
|
'--header-lines=3',
|
||||||
|
'--footer "$(seq 201 203)"',
|
||||||
|
'--preview "echo foobar"'
|
||||||
|
]
|
||||||
|
options = [
|
||||||
|
['--separator==', '--no-separator'],
|
||||||
|
['--info=default', '--info=inline', '--info=inline-right'],
|
||||||
|
['--no-input-border', '--input-border'],
|
||||||
|
['--no-header-border', '--header-border=none', '--header-border'],
|
||||||
|
['--no-header-lines-border', '--header-lines-border'],
|
||||||
|
['--no-footer-border', '--footer-border'],
|
||||||
|
['--no-list-border', '--list-border'],
|
||||||
|
['--preview-window=right', '--preview-window=up', '--preview-window=down', '--preview-window=left'],
|
||||||
|
['--header-first', '--no-header-first'],
|
||||||
|
['--layout=default', '--layout=reverse', '--layout=reverse-list']
|
||||||
|
]
|
||||||
|
# Combination of all options
|
||||||
|
combinations = options[0].product(*options.drop(1))
|
||||||
|
combinations.each_with_index do |combination, index|
|
||||||
|
opts = base + combination
|
||||||
|
command = %(seq 1001 2000 | #{FZF} #{opts.join(' ')})
|
||||||
|
puts "# #{index + 1}/#{combinations.length}\n#{command}"
|
||||||
|
tmux.send_keys command, :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
layout = combination.find { it.start_with?('--layout=') }.split('=').last
|
||||||
|
header_first = combination.include?('--header-first')
|
||||||
|
|
||||||
|
# Input
|
||||||
|
input = lines.index { it.include?('> 123') }
|
||||||
|
assert(input)
|
||||||
|
|
||||||
|
# Info
|
||||||
|
info = lines.index { it.include?('11/997') }
|
||||||
|
assert(info)
|
||||||
|
|
||||||
|
assert(layout == 'reverse' ? input <= info : input >= info)
|
||||||
|
|
||||||
|
# List
|
||||||
|
item1 = lines.index { it.include?('1230') }
|
||||||
|
item2 = lines.index { it.include?('1231') }
|
||||||
|
assert_equal(item1, layout == 'default' ? item2 + 1 : item2 - 1)
|
||||||
|
|
||||||
|
# Preview
|
||||||
|
assert(lines.any? { it.include?('foobar') })
|
||||||
|
|
||||||
|
# Header
|
||||||
|
header1 = lines.index { it.include?('101') }
|
||||||
|
header2 = lines.index { it.include?('102') }
|
||||||
|
assert_equal(header2, header1 + 1)
|
||||||
|
assert((layout == 'reverse') == header_first ? input > header1 : input < header1)
|
||||||
|
|
||||||
|
# Footer
|
||||||
|
footer1 = lines.index { it.include?('201') }
|
||||||
|
footer2 = lines.index { it.include?('202') }
|
||||||
|
assert_equal(footer2, footer1 + 1)
|
||||||
|
assert(layout == 'reverse' ? footer1 > item2 : footer1 < item2)
|
||||||
|
|
||||||
|
# Header lines
|
||||||
|
hline1 = lines.index { it.include?('1001') }
|
||||||
|
hline2 = lines.index { it.include?('1002') }
|
||||||
|
assert_equal(hline1, layout == 'default' ? hline2 + 1 : hline2 - 1)
|
||||||
|
assert(layout == 'reverse' ? hline1 > header1 : hline1 < header1)
|
||||||
|
end
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -189,6 +189,20 @@ class TestPreview < TestInteractive
|
|||||||
tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /123//0 9} ' }
|
tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /123//0 9} ' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_preview_asterisk
|
||||||
|
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_includes lines[1], ' [1/1/1 2 3 4 5/0 1 2 3 4] ' }
|
||||||
|
tmux.send_keys :BTab
|
||||||
|
tmux.until { |lines| assert_includes lines[1], ' [2/1/1 2 3 4 5/0 1 2 3 4] ' }
|
||||||
|
tmux.send_keys :BTab
|
||||||
|
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.until { |lines| assert_includes lines[1], ' [5/1 2/5/4] ' }
|
||||||
|
tmux.send_keys '5'
|
||||||
|
tmux.until { |lines| assert_includes lines[1], ' [/1 2//] ' }
|
||||||
|
end
|
||||||
|
|
||||||
def test_preview_file
|
def test_preview_file
|
||||||
tmux.send_keys %[(echo foo bar; echo bar foo) | #{FZF} --multi --preview 'cat {+f} {+f2} {+nf} {+fn}' --print0], :Enter
|
tmux.send_keys %[(echo foo bar; echo bar foo) | #{FZF} --multi --preview 'cat {+f} {+f2} {+nf} {+fn}' --print0], :Enter
|
||||||
tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
|
tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }
|
||||||
|
|||||||
Reference in New Issue
Block a user