mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-04 14:12:11 -07:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a71c471405 | ||
|
3858086047 | ||
|
dffef3d9f3 | ||
|
de1c6b8727 | ||
|
6f17f412ba | ||
|
746961bf43 | ||
|
182a6d99fd | ||
|
af31088481 | ||
|
43425158f4 | ||
|
8524ea7441 | ||
|
6a65006f55 | ||
|
d75ed841a9 | ||
|
3cd2547e91 | ||
|
8c661d4e8c | ||
|
4b332d831e | ||
|
22487810ba | ||
|
c49e65d926 | ||
|
2e8814bb57 | ||
|
dc557c0d4c | ||
|
a2beb159f1 | ||
|
7ce427ff47 |
1
.github/ISSUE_TEMPLATE.md
vendored
1
.github/ISSUE_TEMPLATE.md
vendored
@@ -11,6 +11,7 @@
|
|||||||
- [ ] Linux
|
- [ ] Linux
|
||||||
- [ ] Mac OS X
|
- [ ] Mac OS X
|
||||||
- [ ] Windows
|
- [ ] Windows
|
||||||
|
- [ ] Windows Subsystem for Linux
|
||||||
- [ ] Etc.
|
- [ ] Etc.
|
||||||
- Shell
|
- Shell
|
||||||
- [ ] bash
|
- [ ] bash
|
||||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,6 +1,32 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.15.9
|
||||||
|
------
|
||||||
|
- Fixed rendering glitches introduced in 0.15.8
|
||||||
|
- The default escape delay is reduced to 50ms and is configurable via
|
||||||
|
`$ESCDELAY`
|
||||||
|
- Scroll indicator at the top-right corner of the preview window is always
|
||||||
|
displayed when there's overflow
|
||||||
|
- Can now be built with ncurses 6 or tcell to support extra features
|
||||||
|
- *ncurses 6*
|
||||||
|
- Supports more than 256 color pairs
|
||||||
|
- Supports italics
|
||||||
|
- *tcell*
|
||||||
|
- 24-bit color support
|
||||||
|
- See https://github.com/junegunn/fzf/blob/master/src/README.md#build
|
||||||
|
|
||||||
|
0.15.8
|
||||||
|
------
|
||||||
|
- Updated ANSI processor to handle more VT-100 escape sequences
|
||||||
|
- Added `--no-bold` (and `--bold`) option
|
||||||
|
- Improved escape sequence processing for WSL
|
||||||
|
- Added support for `alt-[0-9]`, `f11`, and `f12` for `--bind` and `--expect`
|
||||||
|
|
||||||
|
0.15.7
|
||||||
|
------
|
||||||
|
- Fixed panic when color is disabled and header lines contain ANSI colors
|
||||||
|
|
||||||
0.15.6
|
0.15.6
|
||||||
------
|
------
|
||||||
- Windows binaries! (@kelleyma49)
|
- Windows binaries! (@kelleyma49)
|
||||||
|
@@ -209,6 +209,8 @@ If you use vi mode on bash, you need to add `set -o vi` *before* `source
|
|||||||
~/.fzf.bash` in your .bashrc, so that it correctly sets up key bindings for vi
|
~/.fzf.bash` in your .bashrc, so that it correctly sets up key bindings for vi
|
||||||
mode.
|
mode.
|
||||||
|
|
||||||
|
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
|
||||||
|
|
||||||
Fuzzy completion for bash and zsh
|
Fuzzy completion for bash and zsh
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
4
install
4
install
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
[[ "$@" =~ --pre ]] && version=0.15.6 pre=1 ||
|
[[ "$@" =~ --pre ]] && version=0.15.9 pre=1 ||
|
||||||
version=0.15.6 pre=0
|
version=0.15.9 pre=0
|
||||||
|
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
|
@@ -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 "Nov 2016" "fzf 0.15.6" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Nov 2016" "fzf 0.15.9" "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
|
||||||
|
@@ -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 "Nov 2016" "fzf 0.15.6" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Nov 2016" "fzf 0.15.9" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -216,6 +216,9 @@ e.g. \fBfzf --color=bg+:24\fR
|
|||||||
\fBheader \fRHeader
|
\fBheader \fRHeader
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
|
.B "--no-bold"
|
||||||
|
Do not use bold text
|
||||||
|
.TP
|
||||||
.B "--black"
|
.B "--black"
|
||||||
Use black background
|
Use black background
|
||||||
.SS History
|
.SS History
|
||||||
@@ -388,7 +391,8 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
|
|||||||
.B AVAILABLE KEYS: (SYNONYMS)
|
.B AVAILABLE KEYS: (SYNONYMS)
|
||||||
\fIctrl-[a-z]\fR
|
\fIctrl-[a-z]\fR
|
||||||
\fIalt-[a-z]\fR
|
\fIalt-[a-z]\fR
|
||||||
\fIf[1-10]\fR
|
\fIalt-[0-9]\fR
|
||||||
|
\fIf[1-12]\fR
|
||||||
\fIenter\fR (\fIreturn\fR \fIctrl-m\fR)
|
\fIenter\fR (\fIreturn\fR \fIctrl-m\fR)
|
||||||
\fIspace\fR
|
\fIspace\fR
|
||||||
\fIbspace\fR (\fIbs\fR)
|
\fIbspace\fR (\fIbs\fR)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# Key bindings
|
# Key bindings
|
||||||
# ------------
|
# ------------
|
||||||
__fzf_select__() {
|
__fzf_select__() {
|
||||||
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type f -print \
|
-o -type f -print \
|
||||||
-o -type d -print \
|
-o -type d -print \
|
||||||
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
|
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
|
||||||
@@ -41,7 +41,7 @@ fzf-file-widget() {
|
|||||||
|
|
||||||
__fzf_cd__() {
|
__fzf_cd__() {
|
||||||
local cmd dir
|
local cmd dir
|
||||||
cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
|
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
|
||||||
dir=$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS") && printf 'cd %q' "$dir"
|
dir=$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS") && printf 'cd %q' "$dir"
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
function fzf-file-widget
|
function fzf-file-widget
|
||||||
set -q FZF_CTRL_T_COMMAND; or set -l FZF_CTRL_T_COMMAND "
|
set -q FZF_CTRL_T_COMMAND; or set -l FZF_CTRL_T_COMMAND "
|
||||||
command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type f -print \
|
-o -type f -print \
|
||||||
-o -type d -print \
|
-o -type d -print \
|
||||||
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"
|
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"
|
||||||
@@ -34,7 +34,7 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
function fzf-cd-widget
|
function fzf-cd-widget
|
||||||
set -q FZF_ALT_C_COMMAND; or set -l FZF_ALT_C_COMMAND "
|
set -q FZF_ALT_C_COMMAND; or set -l FZF_ALT_C_COMMAND "
|
||||||
command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"
|
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"
|
||||||
# Fish hangs if the command before pipe redirects (2> /dev/null)
|
# Fish hangs if the command before pipe redirects (2> /dev/null)
|
||||||
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m $FZF_ALT_C_OPTS > $TMPDIR/fzf.result"
|
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m $FZF_ALT_C_OPTS > $TMPDIR/fzf.result"
|
||||||
|
@@ -4,7 +4,7 @@ if [[ $- == *i* ]]; then
|
|||||||
|
|
||||||
# CTRL-T - Paste the selected file path(s) into the command line
|
# CTRL-T - Paste the selected file path(s) into the command line
|
||||||
__fsel() {
|
__fsel() {
|
||||||
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type f -print \
|
-o -type f -print \
|
||||||
-o -type d -print \
|
-o -type d -print \
|
||||||
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
|
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
|
||||||
@@ -33,7 +33,7 @@ bindkey '^T' fzf-file-widget
|
|||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
fzf-cd-widget() {
|
fzf-cd-widget() {
|
||||||
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
|
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
|
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
|
||||||
setopt localoptions pipefail 2> /dev/null
|
setopt localoptions pipefail 2> /dev/null
|
||||||
cd "${$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS"):-.}"
|
cd "${$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS"):-.}"
|
||||||
|
@@ -70,10 +70,10 @@ clean:
|
|||||||
cd fzf && rm -f fzf-*
|
cd fzf && rm -f fzf-*
|
||||||
|
|
||||||
fzf/$(BINARY32): deps
|
fzf/$(BINARY32): deps
|
||||||
cd fzf && GOARCH=386 CGO_ENABLED=1 go build -a -ldflags -w -tags "$(TAGS)" -o $(BINARY32)
|
cd fzf && GOARCH=386 CGO_ENABLED=1 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARY32)
|
||||||
|
|
||||||
fzf/$(BINARY64): deps
|
fzf/$(BINARY64): deps
|
||||||
cd fzf && go build -a -ldflags -w -tags "$(TAGS)" -o $(BINARY64)
|
cd fzf && go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARY64)
|
||||||
|
|
||||||
$(BINDIR)/fzf: fzf/$(BINARY) | $(BINDIR)
|
$(BINDIR)/fzf: fzf/$(BINARY) | $(BINDIR)
|
||||||
cp -f fzf/$(BINARY) $(BINDIR)
|
cp -f fzf/$(BINARY) $(BINDIR)
|
||||||
|
@@ -61,6 +61,45 @@ make install
|
|||||||
make linux
|
make linux
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### With ncurses 6
|
||||||
|
|
||||||
|
The official binaries of fzf are built with ncurses 5 because it's widely
|
||||||
|
supported by different platforms. However ncurses 5 is old and has a number of
|
||||||
|
limitations.
|
||||||
|
|
||||||
|
1. Does not support more than 256 color pairs (See [357][357])
|
||||||
|
2. Does not support italics
|
||||||
|
3. Does not support 24-bit color
|
||||||
|
|
||||||
|
[357]: https://github.com/junegunn/fzf/issues/357
|
||||||
|
|
||||||
|
But you can manually build fzf with ncurses 6 to overcome some of these
|
||||||
|
limitations. ncurses 6 supports up to 32767 color pairs (1), and supports
|
||||||
|
italics (2). To build fzf with ncurses 6, you have to install it first. On
|
||||||
|
macOS, you can use Homebrew to install it.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew install homebrew/dupes/ncurses
|
||||||
|
LDFLAGS="-L/usr/local/opt/ncurses/lib" make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### With tcell
|
||||||
|
|
||||||
|
[tcell][tcell] is a portable alternative to ncurses and we currently use it to
|
||||||
|
build Windows binaries. tcell has many benefits but most importantly, it
|
||||||
|
supports 24-bit colors. To build fzf with tcell:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
TAGS=tcell make install
|
||||||
|
```
|
||||||
|
|
||||||
|
However, note that tcell has its own issues.
|
||||||
|
|
||||||
|
- Poor rendering performance compared to ncurses
|
||||||
|
- Does not support bracketed-paste mode
|
||||||
|
- Does not support italics unlike ncurses 6
|
||||||
|
- Some wide characters are not correctly displayed
|
||||||
|
|
||||||
Test
|
Test
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -88,6 +127,8 @@ Third-party libraries used
|
|||||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||||
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
||||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||||
|
- [tcell](https://github.com/gdamore/tcell)
|
||||||
|
- Licensed under [Apache License 2.0](https://github.com/gdamore/tcell/blob/master/LICENSE)
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
@@ -99,4 +140,4 @@ License
|
|||||||
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
||||||
[ncurses]: https://www.gnu.org/software/ncurses/
|
[ncurses]: https://www.gnu.org/software/ncurses/
|
||||||
[req]: http://golang.org/doc/install
|
[req]: http://golang.org/doc/install
|
||||||
[termbox]: https://github.com/nsf/termbox-go
|
[tcell]: https://github.com/gdamore/tcell
|
||||||
|
39
src/ansi.go
39
src/ansi.go
@@ -35,7 +35,16 @@ func (s *ansiState) equals(t *ansiState) bool {
|
|||||||
var ansiRegex *regexp.Regexp
|
var ansiRegex *regexp.Regexp
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*.|[\x0e\x0f]")
|
/*
|
||||||
|
References:
|
||||||
|
- https://github.com/gnachman/iTerm2
|
||||||
|
- http://ascii-table.com/ansi-escape-sequences.php
|
||||||
|
- http://ascii-table.com/ansi-escape-sequences-vt-100.php
|
||||||
|
- http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
|
||||||
|
*/
|
||||||
|
// The following regular expression will include not all but most of the
|
||||||
|
// frequently used ANSI sequences
|
||||||
|
ansiRegex = regexp.MustCompile("\x1b[\\[()][0-9;]*[a-zA-Z@]|\x1b.|[\x08\x0e\x0f]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
|
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
|
||||||
@@ -100,7 +109,7 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
} else {
|
} else {
|
||||||
state = &ansiState{prevState.fg, prevState.bg, prevState.attr}
|
state = &ansiState{prevState.fg, prevState.bg, prevState.attr}
|
||||||
}
|
}
|
||||||
if ansiCode[0] != '\x1b' || ansiCode[len(ansiCode)-1] != 'm' {
|
if ansiCode[0] != '\x1b' || ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,15 +143,17 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
case 49:
|
case 49:
|
||||||
state.bg = -1
|
state.bg = -1
|
||||||
case 1:
|
case 1:
|
||||||
state.attr = tui.Bold
|
state.attr = state.attr | tui.Bold
|
||||||
case 2:
|
case 2:
|
||||||
state.attr = tui.Dim
|
state.attr = state.attr | tui.Dim
|
||||||
|
case 3:
|
||||||
|
state.attr = state.attr | tui.Italic
|
||||||
case 4:
|
case 4:
|
||||||
state.attr = tui.Underline
|
state.attr = state.attr | tui.Underline
|
||||||
case 5:
|
case 5:
|
||||||
state.attr = tui.Blink
|
state.attr = state.attr | tui.Blink
|
||||||
case 7:
|
case 7:
|
||||||
state.attr = tui.Reverse
|
state.attr = state.attr | tui.Reverse
|
||||||
case 0:
|
case 0:
|
||||||
init()
|
init()
|
||||||
default:
|
default:
|
||||||
@@ -158,6 +169,8 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
switch num {
|
switch num {
|
||||||
|
case 2:
|
||||||
|
state256 = 10 // MAGIC
|
||||||
case 5:
|
case 5:
|
||||||
state256++
|
state256++
|
||||||
default:
|
default:
|
||||||
@@ -166,8 +179,20 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
case 2:
|
case 2:
|
||||||
*ptr = tui.Color(num)
|
*ptr = tui.Color(num)
|
||||||
state256 = 0
|
state256 = 0
|
||||||
|
case 10:
|
||||||
|
*ptr = tui.Color(1<<24) | tui.Color(num<<16)
|
||||||
|
state256++
|
||||||
|
case 11:
|
||||||
|
*ptr = *ptr | tui.Color(num<<8)
|
||||||
|
state256++
|
||||||
|
case 12:
|
||||||
|
*ptr = *ptr | tui.Color(num)
|
||||||
|
state256 = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if state256 > 0 {
|
||||||
|
*ptr = -1
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ func TestExtractColor(t *testing.T) {
|
|||||||
output, ansiOffsets, newState := extractColor(src, state, nil)
|
output, ansiOffsets, newState := extractColor(src, state, nil)
|
||||||
state = newState
|
state = newState
|
||||||
if output != "hello world" {
|
if output != "hello world" {
|
||||||
t.Errorf("Invalid output: {}", output)
|
t.Errorf("Invalid output: %s %s", output, []rune(output))
|
||||||
}
|
}
|
||||||
fmt.Println(src, ansiOffsets, clean)
|
fmt.Println(src, ansiOffsets, clean)
|
||||||
assertion(ansiOffsets, state)
|
assertion(ansiOffsets, state)
|
||||||
@@ -56,7 +56,7 @@ func TestExtractColor(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
state = nil
|
state = nil
|
||||||
src = "\x1b[1mhello \x1b[mworld"
|
src = "\x1b[1mhello \x1b[mw\x1b7o\x1b8r\x1b(Bl\x1b[2@d"
|
||||||
check(func(offsets *[]ansiOffset, state *ansiState) {
|
check(func(offsets *[]ansiOffset, state *ansiState) {
|
||||||
if len(*offsets) != 1 {
|
if len(*offsets) != 1 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Current version
|
// Current version
|
||||||
version = "0.15.6"
|
version = "0.15.9"
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||||
|
@@ -57,6 +57,7 @@ const usage = `usage: fzf [options]
|
|||||||
--ansi Enable processing of ANSI color codes
|
--ansi Enable processing of ANSI color codes
|
||||||
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
||||||
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
||||||
|
--no-bold Do not use bold text
|
||||||
|
|
||||||
History
|
History
|
||||||
--history=FILE History file
|
--history=FILE History file
|
||||||
@@ -144,6 +145,7 @@ type Options struct {
|
|||||||
Mouse bool
|
Mouse bool
|
||||||
Theme *tui.ColorTheme
|
Theme *tui.ColorTheme
|
||||||
Black bool
|
Black bool
|
||||||
|
Bold bool
|
||||||
Reverse bool
|
Reverse bool
|
||||||
Cycle bool
|
Cycle bool
|
||||||
Hscroll bool
|
Hscroll bool
|
||||||
@@ -189,6 +191,7 @@ func defaultOptions() *Options {
|
|||||||
Mouse: true,
|
Mouse: true,
|
||||||
Theme: tui.EmptyTheme(),
|
Theme: tui.EmptyTheme(),
|
||||||
Black: false,
|
Black: false,
|
||||||
|
Bold: true,
|
||||||
Reverse: false,
|
Reverse: false,
|
||||||
Cycle: false,
|
Cycle: false,
|
||||||
Hscroll: true,
|
Hscroll: true,
|
||||||
@@ -327,6 +330,10 @@ func isAlphabet(char uint8) bool {
|
|||||||
return char >= 'a' && char <= 'z'
|
return char >= 'a' && char <= 'z'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isNumeric(char uint8) bool {
|
||||||
|
return char >= '0' && char <= '9'
|
||||||
|
}
|
||||||
|
|
||||||
func parseAlgo(str string) algo.Algo {
|
func parseAlgo(str string) algo.Algo {
|
||||||
switch str {
|
switch str {
|
||||||
case "v1":
|
case "v1":
|
||||||
@@ -403,11 +410,17 @@ func parseKeyChords(str string, message string) map[int]string {
|
|||||||
chord = tui.DoubleClick
|
chord = tui.DoubleClick
|
||||||
case "f10":
|
case "f10":
|
||||||
chord = tui.F10
|
chord = tui.F10
|
||||||
|
case "f11":
|
||||||
|
chord = tui.F11
|
||||||
|
case "f12":
|
||||||
|
chord = tui.F12
|
||||||
default:
|
default:
|
||||||
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||||
chord = tui.CtrlA + int(lkey[5]) - 'a'
|
chord = tui.CtrlA + int(lkey[5]) - 'a'
|
||||||
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
|
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
|
||||||
chord = tui.AltA + int(lkey[4]) - 'a'
|
chord = tui.AltA + int(lkey[4]) - 'a'
|
||||||
|
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isNumeric(lkey[4]) {
|
||||||
|
chord = tui.Alt0 + int(lkey[4]) - '0'
|
||||||
} 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' {
|
||||||
chord = tui.F1 + int(key[1]) - '1'
|
chord = tui.F1 + int(key[1]) - '1'
|
||||||
} else if utf8.RuneCountInString(key) == 1 {
|
} else if utf8.RuneCountInString(key) == 1 {
|
||||||
@@ -910,6 +923,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Black = true
|
opts.Black = true
|
||||||
case "--no-black":
|
case "--no-black":
|
||||||
opts.Black = false
|
opts.Black = false
|
||||||
|
case "--bold":
|
||||||
|
opts.Bold = true
|
||||||
|
case "--no-bold":
|
||||||
|
opts.Bold = false
|
||||||
case "--reverse":
|
case "--reverse":
|
||||||
opts.Reverse = true
|
opts.Reverse = true
|
||||||
case "--no-reverse":
|
case "--no-reverse":
|
||||||
|
@@ -147,19 +147,21 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
|
|||||||
} else {
|
} else {
|
||||||
ansi := itemColors[curr-1]
|
ansi := itemColors[curr-1]
|
||||||
fg := ansi.color.fg
|
fg := ansi.color.fg
|
||||||
if fg == -1 {
|
|
||||||
if current {
|
|
||||||
fg = theme.Current
|
|
||||||
} else {
|
|
||||||
fg = theme.Fg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bg := ansi.color.bg
|
bg := ansi.color.bg
|
||||||
if bg == -1 {
|
if theme != nil {
|
||||||
if current {
|
if fg == -1 {
|
||||||
bg = theme.DarkBg
|
if current {
|
||||||
} else {
|
fg = theme.Current
|
||||||
bg = theme.Bg
|
} else {
|
||||||
|
fg = theme.Fg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bg == -1 {
|
||||||
|
if current {
|
||||||
|
bg = theme.DarkBg
|
||||||
|
} else {
|
||||||
|
bg = theme.Bg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
|
@@ -70,6 +70,7 @@ type Terminal struct {
|
|||||||
header0 []string
|
header0 []string
|
||||||
ansi bool
|
ansi bool
|
||||||
margin [4]sizeSpec
|
margin [4]sizeSpec
|
||||||
|
strong tui.Attr
|
||||||
window *tui.Window
|
window *tui.Window
|
||||||
bwindow *tui.Window
|
bwindow *tui.Window
|
||||||
pwindow *tui.Window
|
pwindow *tui.Window
|
||||||
@@ -189,6 +190,7 @@ const (
|
|||||||
func defaultKeymap() map[int]actionType {
|
func defaultKeymap() map[int]actionType {
|
||||||
keymap := make(map[int]actionType)
|
keymap := make(map[int]actionType)
|
||||||
keymap[tui.Invalid] = actInvalid
|
keymap[tui.Invalid] = actInvalid
|
||||||
|
keymap[tui.Resize] = actClearScreen
|
||||||
keymap[tui.CtrlA] = actBeginningOfLine
|
keymap[tui.CtrlA] = actBeginningOfLine
|
||||||
keymap[tui.CtrlB] = actBackwardChar
|
keymap[tui.CtrlB] = actBackwardChar
|
||||||
keymap[tui.CtrlC] = actAbort
|
keymap[tui.CtrlC] = actAbort
|
||||||
@@ -256,6 +258,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
if len(opts.Preview.command) > 0 {
|
if len(opts.Preview.command) > 0 {
|
||||||
previewBox = util.NewEventBox()
|
previewBox = util.NewEventBox()
|
||||||
}
|
}
|
||||||
|
strongAttr := tui.Bold
|
||||||
|
if !opts.Bold {
|
||||||
|
strongAttr = tui.AttrRegular
|
||||||
|
}
|
||||||
return &Terminal{
|
return &Terminal{
|
||||||
initDelay: delay,
|
initDelay: delay,
|
||||||
inlineInfo: opts.InlineInfo,
|
inlineInfo: opts.InlineInfo,
|
||||||
@@ -279,6 +285,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
printQuery: opts.PrintQuery,
|
printQuery: opts.PrintQuery,
|
||||||
history: opts.History,
|
history: opts.History,
|
||||||
margin: opts.Margin,
|
margin: opts.Margin,
|
||||||
|
strong: strongAttr,
|
||||||
cycle: opts.Cycle,
|
cycle: opts.Cycle,
|
||||||
header: header,
|
header: header,
|
||||||
header0: header,
|
header0: header,
|
||||||
@@ -532,24 +539,24 @@ func (t *Terminal) placeCursor() {
|
|||||||
|
|
||||||
func (t *Terminal) printPrompt() {
|
func (t *Terminal) printPrompt() {
|
||||||
t.move(0, 0, true)
|
t.move(0, 0, true)
|
||||||
t.window.CPrint(tui.ColPrompt, tui.Bold, t.prompt)
|
t.window.CPrint(tui.ColPrompt, t.strong, t.prompt)
|
||||||
t.window.CPrint(tui.ColNormal, tui.Bold, string(t.input))
|
t.window.CPrint(tui.ColNormal, t.strong, string(t.input))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printInfo() {
|
func (t *Terminal) printInfo() {
|
||||||
if t.inlineInfo {
|
if t.inlineInfo {
|
||||||
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
|
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
t.window.CPrint(tui.ColSpinner, tui.Bold, " < ")
|
t.window.CPrint(tui.ColSpinner, t.strong, " < ")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColPrompt, tui.Bold, " < ")
|
t.window.CPrint(tui.ColPrompt, t.strong, " < ")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.move(1, 0, true)
|
t.move(1, 0, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
||||||
t.window.CPrint(tui.ColSpinner, tui.Bold, _spinner[idx])
|
t.window.CPrint(tui.ColSpinner, t.strong, _spinner[idx])
|
||||||
}
|
}
|
||||||
t.move(1, 2, false)
|
t.move(1, 2, false)
|
||||||
}
|
}
|
||||||
@@ -627,17 +634,17 @@ func (t *Terminal) printItem(result *Result, i int, current bool) {
|
|||||||
} else if current {
|
} else if current {
|
||||||
label = ">"
|
label = ">"
|
||||||
}
|
}
|
||||||
t.window.CPrint(tui.ColCursor, tui.Bold, label)
|
t.window.CPrint(tui.ColCursor, t.strong, label)
|
||||||
if current {
|
if current {
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(tui.ColSelected, tui.Bold, ">")
|
t.window.CPrint(tui.ColSelected, t.strong, ">")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColCurrent, tui.Bold, " ")
|
t.window.CPrint(tui.ColCurrent, t.strong, " ")
|
||||||
}
|
}
|
||||||
t.printHighlighted(result, tui.Bold, tui.ColCurrent, tui.ColCurrentMatch, true)
|
t.printHighlighted(result, t.strong, tui.ColCurrent, tui.ColCurrentMatch, true)
|
||||||
} else {
|
} else {
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(tui.ColSelected, tui.Bold, ">")
|
t.window.CPrint(tui.ColSelected, t.strong, ">")
|
||||||
} else {
|
} else {
|
||||||
t.window.Print(" ")
|
t.window.Print(" ")
|
||||||
}
|
}
|
||||||
@@ -826,7 +833,7 @@ func (t *Terminal) printPreview() {
|
|||||||
}
|
}
|
||||||
return t.pwindow.Fill(str)
|
return t.pwindow.Fill(str)
|
||||||
})
|
})
|
||||||
if t.previewer.offset > 0 {
|
if t.previewer.lines > t.pwindow.Height {
|
||||||
offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
|
offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
|
||||||
t.pwindow.Move(0, t.pwindow.Width-len(offset))
|
t.pwindow.Move(0, t.pwindow.Width-len(offset))
|
||||||
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, offset)
|
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, offset)
|
||||||
|
@@ -10,8 +10,12 @@ package tui
|
|||||||
#cgo static LDFLAGS: -l:libncursesw.a -l:libtinfo.a -l:libgpm.a -ldl
|
#cgo static LDFLAGS: -l:libncursesw.a -l:libtinfo.a -l:libgpm.a -ldl
|
||||||
#cgo android static LDFLAGS: -l:libncurses.a -fPIE -march=armv7-a -mfpu=neon -mhard-float -Wl,--no-warn-mismatch
|
#cgo android static LDFLAGS: -l:libncurses.a -fPIE -march=armv7-a -mfpu=neon -mhard-float -Wl,--no-warn-mismatch
|
||||||
|
|
||||||
SCREEN *c_newterm () {
|
FILE* c_tty() {
|
||||||
return newterm(NULL, stderr, stdin);
|
return fopen("/dev/tty", "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
SCREEN* c_newterm(FILE* tty) {
|
||||||
|
return newterm(NULL, stderr, tty);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
@@ -19,24 +23,26 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ColorPair int16
|
type ColorPair int16
|
||||||
type Attr C.int
|
type Attr C.uint
|
||||||
type WindowImpl C.WINDOW
|
type WindowImpl C.WINDOW
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Bold = C.A_BOLD
|
Bold Attr = C.A_BOLD
|
||||||
Dim = C.A_DIM
|
Dim = C.A_DIM
|
||||||
Blink = C.A_BLINK
|
Blink = C.A_BLINK
|
||||||
Reverse = C.A_REVERSE
|
Reverse = C.A_REVERSE
|
||||||
Underline = C.A_UNDERLINE
|
Underline = C.A_UNDERLINE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Italic Attr = C.A_VERTICAL << 1 // FIXME
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttrRegular Attr = 0
|
AttrRegular Attr = 0
|
||||||
)
|
)
|
||||||
@@ -59,14 +65,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_in *os.File
|
|
||||||
_screen *C.SCREEN
|
_screen *C.SCREEN
|
||||||
_colorMap map[int]ColorPair
|
_colorMap map[int]ColorPair
|
||||||
_colorFn func(ColorPair, Attr) C.int
|
_colorFn func(ColorPair, Attr) (C.short, C.int)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_colorMap = make(map[int]ColorPair)
|
_colorMap = make(map[int]ColorPair)
|
||||||
|
if strings.HasPrefix(C.GoString(C.curses_version()), "ncurses 5") {
|
||||||
|
Italic = C.A_NORMAL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Attr) Merge(b Attr) Attr {
|
func (a Attr) Merge(b Attr) Attr {
|
||||||
@@ -81,18 +89,13 @@ func DefaultTheme() *ColorTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Init(theme *ColorTheme, black bool, mouse bool) {
|
func Init(theme *ColorTheme, black bool, mouse bool) {
|
||||||
{
|
|
||||||
in, err := os.OpenFile("/dev/tty", syscall.O_RDONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
panic("Failed to open /dev/tty")
|
|
||||||
}
|
|
||||||
_in = in
|
|
||||||
// Break STDIN
|
|
||||||
// syscall.Dup2(int(in.Fd()), int(os.Stdin.Fd()))
|
|
||||||
}
|
|
||||||
|
|
||||||
C.setlocale(C.LC_ALL, C.CString(""))
|
C.setlocale(C.LC_ALL, C.CString(""))
|
||||||
_screen = C.c_newterm()
|
tty := C.c_tty()
|
||||||
|
if tty == nil {
|
||||||
|
fmt.Println("Failed to open /dev/tty")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
_screen = C.c_newterm(tty)
|
||||||
if _screen == nil {
|
if _screen == nil {
|
||||||
fmt.Println("Invalid $TERM: " + os.Getenv("TERM"))
|
fmt.Println("Invalid $TERM: " + os.Getenv("TERM"))
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
@@ -100,9 +103,22 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
|
|||||||
C.set_term(_screen)
|
C.set_term(_screen)
|
||||||
if mouse {
|
if mouse {
|
||||||
C.mousemask(C.ALL_MOUSE_EVENTS, nil)
|
C.mousemask(C.ALL_MOUSE_EVENTS, nil)
|
||||||
|
C.mouseinterval(0)
|
||||||
}
|
}
|
||||||
C.noecho()
|
C.noecho()
|
||||||
C.raw() // stty dsusp undef
|
C.raw() // stty dsusp undef
|
||||||
|
C.nonl()
|
||||||
|
C.keypad(C.stdscr, true)
|
||||||
|
|
||||||
|
delay := 50
|
||||||
|
delayEnv := os.Getenv("ESCDELAY")
|
||||||
|
if len(delayEnv) > 0 {
|
||||||
|
num, err := strconv.Atoi(delayEnv)
|
||||||
|
if err == nil && num >= 0 {
|
||||||
|
delay = num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
C.set_escdelay(C.int(delay))
|
||||||
|
|
||||||
_color = theme != nil
|
_color = theme != nil
|
||||||
if _color {
|
if _color {
|
||||||
@@ -114,6 +130,13 @@ func Init(theme *ColorTheme, black bool, mouse bool) {
|
|||||||
} else {
|
} else {
|
||||||
_colorFn = attrMono
|
_colorFn = attrMono
|
||||||
}
|
}
|
||||||
|
|
||||||
|
C.nodelay(C.stdscr, true)
|
||||||
|
ch := C.getch()
|
||||||
|
if ch != C.ERR {
|
||||||
|
C.ungetch(ch)
|
||||||
|
}
|
||||||
|
C.nodelay(C.stdscr, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPairs(theme *ColorTheme) {
|
func initPairs(theme *ColorTheme) {
|
||||||
@@ -153,10 +176,12 @@ func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
|||||||
C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
|
C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal))))
|
||||||
}
|
}
|
||||||
if border {
|
if border {
|
||||||
attr := _colorFn(ColBorder, 0)
|
pair, attr := _colorFn(ColBorder, 0)
|
||||||
|
C.wcolor_set(win, pair, nil)
|
||||||
C.wattron(win, attr)
|
C.wattron(win, attr)
|
||||||
C.box(win, 0, 0)
|
C.box(win, 0, 0)
|
||||||
C.wattroff(win, attr)
|
C.wattroff(win, attr)
|
||||||
|
C.wcolor_set(win, 0, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Window{
|
return &Window{
|
||||||
@@ -168,21 +193,15 @@ func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func attrColored(pair ColorPair, a Attr) C.int {
|
func attrColored(pair ColorPair, a Attr) (C.short, C.int) {
|
||||||
var attr C.int
|
return C.short(pair), C.int(a)
|
||||||
if pair > 0 {
|
|
||||||
attr = C.COLOR_PAIR(C.int(pair))
|
|
||||||
}
|
|
||||||
return attr | C.int(a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func attrMono(pair ColorPair, a Attr) C.int {
|
func attrMono(pair ColorPair, a Attr) (C.short, C.int) {
|
||||||
var attr C.int
|
var attr C.int
|
||||||
switch pair {
|
switch pair {
|
||||||
case ColCurrent:
|
case ColCurrent:
|
||||||
if C.int(a)&C.A_BOLD == C.A_BOLD {
|
attr = C.A_REVERSE
|
||||||
attr = C.A_REVERSE
|
|
||||||
}
|
|
||||||
case ColMatch:
|
case ColMatch:
|
||||||
attr = C.A_UNDERLINE
|
attr = C.A_UNDERLINE
|
||||||
case ColCurrentMatch:
|
case ColCurrentMatch:
|
||||||
@@ -191,7 +210,7 @@ func attrMono(pair ColorPair, a Attr) C.int {
|
|||||||
if C.int(a)&C.A_BOLD == C.A_BOLD {
|
if C.int(a)&C.A_BOLD == C.A_BOLD {
|
||||||
attr = attr | C.A_BOLD
|
attr = attr | C.A_BOLD
|
||||||
}
|
}
|
||||||
return attr
|
return 0, attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaxX() int {
|
func MaxX() int {
|
||||||
@@ -232,11 +251,13 @@ func (w *Window) Print(text string) {
|
|||||||
}, text)))
|
}, text)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) CPrint(pair ColorPair, a Attr, text string) {
|
func (w *Window) CPrint(pair ColorPair, attr Attr, text string) {
|
||||||
attr := _colorFn(pair, a)
|
p, a := _colorFn(pair, attr)
|
||||||
C.wattron(w.win(), attr)
|
C.wcolor_set(w.win(), p, nil)
|
||||||
|
C.wattron(w.win(), a)
|
||||||
w.Print(text)
|
w.Print(text)
|
||||||
C.wattroff(w.win(), attr)
|
C.wattroff(w.win(), a)
|
||||||
|
C.wcolor_set(w.win(), 0, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Clear() {
|
func Clear() {
|
||||||
@@ -256,11 +277,13 @@ func (w *Window) Fill(str string) bool {
|
|||||||
return C.waddstr(w.win(), C.CString(str)) == C.OK
|
return C.waddstr(w.win(), C.CString(str)) == C.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) CFill(str string, fg Color, bg Color, a Attr) bool {
|
func (w *Window) CFill(str string, fg Color, bg Color, attr Attr) bool {
|
||||||
attr := _colorFn(PairFor(fg, bg), a)
|
pair := PairFor(fg, bg)
|
||||||
C.wattron(w.win(), attr)
|
C.wcolor_set(w.win(), C.short(pair), nil)
|
||||||
|
C.wattron(w.win(), C.int(attr))
|
||||||
ret := w.Fill(str)
|
ret := w.Fill(str)
|
||||||
C.wattroff(w.win(), attr)
|
C.wattroff(w.win(), C.int(attr))
|
||||||
|
C.wcolor_set(w.win(), 0, nil)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,6 +295,10 @@ func RefreshWindows(windows []*Window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PairFor(fg Color, bg Color) ColorPair {
|
func PairFor(fg Color, bg Color) ColorPair {
|
||||||
|
// ncurses does not support 24-bit colors
|
||||||
|
if fg.is24() || bg.is24() {
|
||||||
|
return ColDefault
|
||||||
|
}
|
||||||
key := (int(fg) << 8) + int(bg)
|
key := (int(fg) << 8) + int(bg)
|
||||||
if found, prs := _colorMap[key]; prs {
|
if found, prs := _colorMap[key]; prs {
|
||||||
return found
|
return found
|
||||||
@@ -283,246 +310,169 @@ func PairFor(fg Color, bg Color) ColorPair {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
func getch(nonblock bool) int {
|
func consume(expects ...rune) bool {
|
||||||
b := make([]byte, 1)
|
for _, r := range expects {
|
||||||
syscall.SetNonblock(int(_in.Fd()), nonblock)
|
if int(C.getch()) != int(r) {
|
||||||
_, err := _in.Read(b)
|
return false
|
||||||
if err != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int(b[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetBytes() []byte {
|
|
||||||
c := getch(false)
|
|
||||||
retries := 0
|
|
||||||
if c == 27 {
|
|
||||||
// Wait for additional keys after ESC for 100ms (10 * 10ms)
|
|
||||||
retries = 10
|
|
||||||
}
|
|
||||||
_buf = append(_buf, byte(c))
|
|
||||||
|
|
||||||
for {
|
|
||||||
c = getch(true)
|
|
||||||
if c == -1 {
|
|
||||||
if retries > 0 {
|
|
||||||
retries--
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
retries = 0
|
|
||||||
_buf = append(_buf, byte(c))
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
return _buf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 27 (91 79) 77 type x y
|
func escSequence() Event {
|
||||||
func mouseSequence(sz *int) Event {
|
C.nodelay(C.stdscr, true)
|
||||||
if len(_buf) < 6 {
|
defer func() {
|
||||||
return Event{Invalid, 0, nil}
|
C.nodelay(C.stdscr, false)
|
||||||
}
|
}()
|
||||||
*sz = 6
|
c := C.getch()
|
||||||
switch _buf[3] {
|
switch c {
|
||||||
case 32, 36, 40, 48, // mouse-down / shift / cmd / ctrl
|
case C.ERR:
|
||||||
35, 39, 43, 51: // mouse-up / shift / cmd / ctrl
|
|
||||||
mod := _buf[3] >= 36
|
|
||||||
down := _buf[3]%2 == 0
|
|
||||||
x := int(_buf[4] - 33)
|
|
||||||
y := int(_buf[5] - 33)
|
|
||||||
double := false
|
|
||||||
if down {
|
|
||||||
now := time.Now()
|
|
||||||
if now.Sub(_prevDownTime) < doubleClickDuration {
|
|
||||||
_clickY = append(_clickY, y)
|
|
||||||
} else {
|
|
||||||
_clickY = []int{y}
|
|
||||||
}
|
|
||||||
_prevDownTime = now
|
|
||||||
} else {
|
|
||||||
if len(_clickY) > 1 && _clickY[0] == _clickY[1] &&
|
|
||||||
time.Now().Sub(_prevDownTime) < doubleClickDuration {
|
|
||||||
double = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Event{Mouse, 0, &MouseEvent{y, x, 0, down, double, mod}}
|
|
||||||
case 96, 100, 104, 112, // scroll-up / shift / cmd / ctrl
|
|
||||||
97, 101, 105, 113: // scroll-down / shift / cmd / ctrl
|
|
||||||
mod := _buf[3] >= 100
|
|
||||||
s := 1 - int(_buf[3]%2)*2
|
|
||||||
x := int(_buf[4] - 33)
|
|
||||||
y := int(_buf[5] - 33)
|
|
||||||
return Event{Mouse, 0, &MouseEvent{y, x, s, false, false, mod}}
|
|
||||||
}
|
|
||||||
return Event{Invalid, 0, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
func escSequence(sz *int) Event {
|
|
||||||
if len(_buf) < 2 {
|
|
||||||
return Event{ESC, 0, nil}
|
return Event{ESC, 0, nil}
|
||||||
}
|
case CtrlM:
|
||||||
*sz = 2
|
|
||||||
switch _buf[1] {
|
|
||||||
case 13:
|
|
||||||
return Event{AltEnter, 0, nil}
|
return Event{AltEnter, 0, nil}
|
||||||
case 32:
|
case '/':
|
||||||
return Event{AltSpace, 0, nil}
|
|
||||||
case 47:
|
|
||||||
return Event{AltSlash, 0, nil}
|
return Event{AltSlash, 0, nil}
|
||||||
case 98:
|
case ' ':
|
||||||
return Event{AltB, 0, nil}
|
return Event{AltSpace, 0, nil}
|
||||||
case 100:
|
case 127, C.KEY_BACKSPACE:
|
||||||
return Event{AltD, 0, nil}
|
|
||||||
case 102:
|
|
||||||
return Event{AltF, 0, nil}
|
|
||||||
case 127:
|
|
||||||
return Event{AltBS, 0, nil}
|
return Event{AltBS, 0, nil}
|
||||||
case 91, 79:
|
case '[':
|
||||||
if len(_buf) < 3 {
|
// Bracketed paste mode (printf "\e[?2004h")
|
||||||
|
// \e[200~ TEXT \e[201~
|
||||||
|
if consume('2', '0', '0', '~') {
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
}
|
}
|
||||||
*sz = 3
|
}
|
||||||
switch _buf[2] {
|
if c >= 'a' && c <= 'z' {
|
||||||
case 68:
|
return Event{AltA + int(c) - 'a', 0, nil}
|
||||||
return Event{Left, 0, nil}
|
}
|
||||||
case 67:
|
|
||||||
return Event{Right, 0, nil}
|
if c >= '0' && c <= '9' {
|
||||||
case 66:
|
return Event{Alt0 + int(c) - '0', 0, nil}
|
||||||
return Event{Down, 0, nil}
|
}
|
||||||
case 65:
|
|
||||||
return Event{Up, 0, nil}
|
// Don't care. Ignore the rest.
|
||||||
case 90:
|
for ; c != C.ERR; c = C.getch() {
|
||||||
return Event{BTab, 0, nil}
|
|
||||||
case 72:
|
|
||||||
return Event{Home, 0, nil}
|
|
||||||
case 70:
|
|
||||||
return Event{End, 0, nil}
|
|
||||||
case 77:
|
|
||||||
return mouseSequence(sz)
|
|
||||||
case 80:
|
|
||||||
return Event{F1, 0, nil}
|
|
||||||
case 81:
|
|
||||||
return Event{F2, 0, nil}
|
|
||||||
case 82:
|
|
||||||
return Event{F3, 0, nil}
|
|
||||||
case 83:
|
|
||||||
return Event{F4, 0, nil}
|
|
||||||
case 49, 50, 51, 52, 53, 54:
|
|
||||||
if len(_buf) < 4 {
|
|
||||||
return Event{Invalid, 0, nil}
|
|
||||||
}
|
|
||||||
*sz = 4
|
|
||||||
switch _buf[2] {
|
|
||||||
case 50:
|
|
||||||
if len(_buf) == 5 && _buf[4] == 126 {
|
|
||||||
*sz = 5
|
|
||||||
switch _buf[3] {
|
|
||||||
case 48:
|
|
||||||
return Event{F9, 0, nil}
|
|
||||||
case 49:
|
|
||||||
return Event{F10, 0, nil}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Bracketed paste mode \e[200~ / \e[201
|
|
||||||
if _buf[3] == 48 && (_buf[4] == 48 || _buf[4] == 49) && _buf[5] == 126 {
|
|
||||||
*sz = 6
|
|
||||||
return Event{Invalid, 0, nil}
|
|
||||||
}
|
|
||||||
return Event{Invalid, 0, nil} // INS
|
|
||||||
case 51:
|
|
||||||
return Event{Del, 0, nil}
|
|
||||||
case 52:
|
|
||||||
return Event{End, 0, nil}
|
|
||||||
case 53:
|
|
||||||
return Event{PgUp, 0, nil}
|
|
||||||
case 54:
|
|
||||||
return Event{PgDn, 0, nil}
|
|
||||||
case 49:
|
|
||||||
switch _buf[3] {
|
|
||||||
case 126:
|
|
||||||
return Event{Home, 0, nil}
|
|
||||||
case 53, 55, 56, 57:
|
|
||||||
if len(_buf) == 5 && _buf[4] == 126 {
|
|
||||||
*sz = 5
|
|
||||||
switch _buf[3] {
|
|
||||||
case 53:
|
|
||||||
return Event{F5, 0, nil}
|
|
||||||
case 55:
|
|
||||||
return Event{F6, 0, nil}
|
|
||||||
case 56:
|
|
||||||
return Event{F7, 0, nil}
|
|
||||||
case 57:
|
|
||||||
return Event{F8, 0, nil}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Event{Invalid, 0, nil}
|
|
||||||
case 59:
|
|
||||||
if len(_buf) != 6 {
|
|
||||||
return Event{Invalid, 0, nil}
|
|
||||||
}
|
|
||||||
*sz = 6
|
|
||||||
switch _buf[4] {
|
|
||||||
case 50:
|
|
||||||
switch _buf[5] {
|
|
||||||
case 68:
|
|
||||||
return Event{Home, 0, nil}
|
|
||||||
case 67:
|
|
||||||
return Event{End, 0, nil}
|
|
||||||
}
|
|
||||||
case 53:
|
|
||||||
switch _buf[5] {
|
|
||||||
case 68:
|
|
||||||
return Event{SLeft, 0, nil}
|
|
||||||
case 67:
|
|
||||||
return Event{SRight, 0, nil}
|
|
||||||
}
|
|
||||||
} // _buf[4]
|
|
||||||
} // _buf[3]
|
|
||||||
} // _buf[2]
|
|
||||||
} // _buf[2]
|
|
||||||
} // _buf[1]
|
|
||||||
if _buf[1] >= 'a' && _buf[1] <= 'z' {
|
|
||||||
return Event{AltA + int(_buf[1]) - 'a', 0, nil}
|
|
||||||
}
|
}
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Invalid, 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetChar() Event {
|
func GetChar() Event {
|
||||||
if len(_buf) == 0 {
|
c := C.getch()
|
||||||
_buf = GetBytes()
|
switch c {
|
||||||
}
|
case C.ERR:
|
||||||
if len(_buf) == 0 {
|
return Event{Invalid, 0, nil}
|
||||||
panic("Empty _buffer")
|
case C.KEY_UP:
|
||||||
}
|
return Event{Up, 0, nil}
|
||||||
|
case C.KEY_DOWN:
|
||||||
sz := 1
|
return Event{Down, 0, nil}
|
||||||
defer func() {
|
case C.KEY_LEFT:
|
||||||
_buf = _buf[sz:]
|
return Event{Left, 0, nil}
|
||||||
}()
|
case C.KEY_RIGHT:
|
||||||
|
return Event{Right, 0, nil}
|
||||||
switch _buf[0] {
|
case C.KEY_HOME:
|
||||||
case CtrlC:
|
return Event{Home, 0, nil}
|
||||||
return Event{CtrlC, 0, nil}
|
case C.KEY_END:
|
||||||
case CtrlG:
|
return Event{End, 0, nil}
|
||||||
return Event{CtrlG, 0, nil}
|
case C.KEY_BACKSPACE:
|
||||||
case CtrlQ:
|
return Event{BSpace, 0, nil}
|
||||||
return Event{CtrlQ, 0, nil}
|
case C.KEY_F0 + 1:
|
||||||
|
return Event{F1, 0, nil}
|
||||||
|
case C.KEY_F0 + 2:
|
||||||
|
return Event{F2, 0, nil}
|
||||||
|
case C.KEY_F0 + 3:
|
||||||
|
return Event{F3, 0, nil}
|
||||||
|
case C.KEY_F0 + 4:
|
||||||
|
return Event{F4, 0, nil}
|
||||||
|
case C.KEY_F0 + 5:
|
||||||
|
return Event{F5, 0, nil}
|
||||||
|
case C.KEY_F0 + 6:
|
||||||
|
return Event{F6, 0, nil}
|
||||||
|
case C.KEY_F0 + 7:
|
||||||
|
return Event{F7, 0, nil}
|
||||||
|
case C.KEY_F0 + 8:
|
||||||
|
return Event{F8, 0, nil}
|
||||||
|
case C.KEY_F0 + 9:
|
||||||
|
return Event{F9, 0, nil}
|
||||||
|
case C.KEY_F0 + 10:
|
||||||
|
return Event{F10, 0, nil}
|
||||||
|
case C.KEY_F0 + 11:
|
||||||
|
return Event{F11, 0, nil}
|
||||||
|
case C.KEY_F0 + 12:
|
||||||
|
return Event{F12, 0, nil}
|
||||||
|
case C.KEY_DC:
|
||||||
|
return Event{Del, 0, nil}
|
||||||
|
case C.KEY_PPAGE:
|
||||||
|
return Event{PgUp, 0, nil}
|
||||||
|
case C.KEY_NPAGE:
|
||||||
|
return Event{PgDn, 0, nil}
|
||||||
|
case C.KEY_BTAB:
|
||||||
|
return Event{BTab, 0, nil}
|
||||||
|
case C.KEY_ENTER:
|
||||||
|
return Event{CtrlM, 0, nil}
|
||||||
|
case C.KEY_SLEFT:
|
||||||
|
return Event{SLeft, 0, nil}
|
||||||
|
case C.KEY_SRIGHT:
|
||||||
|
return Event{SRight, 0, nil}
|
||||||
|
case C.KEY_MOUSE:
|
||||||
|
var me C.MEVENT
|
||||||
|
if C.getmouse(&me) != C.ERR {
|
||||||
|
mod := ((me.bstate & C.BUTTON_SHIFT) | (me.bstate & C.BUTTON_CTRL) | (me.bstate & C.BUTTON_ALT)) > 0
|
||||||
|
x := int(me.x)
|
||||||
|
y := int(me.y)
|
||||||
|
/* Cannot use BUTTON1_DOUBLE_CLICKED due to mouseinterval(0) */
|
||||||
|
if (me.bstate & C.BUTTON1_PRESSED) > 0 {
|
||||||
|
now := time.Now()
|
||||||
|
if now.Sub(_prevDownTime) < doubleClickDuration {
|
||||||
|
_clickY = append(_clickY, y)
|
||||||
|
} else {
|
||||||
|
_clickY = []int{y}
|
||||||
|
_prevDownTime = now
|
||||||
|
}
|
||||||
|
return Event{Mouse, 0, &MouseEvent{y, x, 0, true, false, mod}}
|
||||||
|
} else if (me.bstate & C.BUTTON1_RELEASED) > 0 {
|
||||||
|
double := false
|
||||||
|
if len(_clickY) > 1 && _clickY[0] == _clickY[1] &&
|
||||||
|
time.Now().Sub(_prevDownTime) < doubleClickDuration {
|
||||||
|
double = true
|
||||||
|
}
|
||||||
|
return Event{Mouse, 0, &MouseEvent{y, x, 0, false, double, mod}}
|
||||||
|
} else if (me.bstate&0x8000000) > 0 || (me.bstate&0x80) > 0 {
|
||||||
|
return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, mod}}
|
||||||
|
} else if (me.bstate & C.BUTTON4_PRESSED) > 0 {
|
||||||
|
return Event{Mouse, 0, &MouseEvent{y, x, 1, false, false, mod}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Event{Invalid, 0, nil}
|
||||||
|
case C.KEY_RESIZE:
|
||||||
|
return Event{Resize, 0, nil}
|
||||||
|
case ESC:
|
||||||
|
return escSequence()
|
||||||
case 127:
|
case 127:
|
||||||
return Event{BSpace, 0, nil}
|
return Event{BSpace, 0, nil}
|
||||||
case ESC:
|
}
|
||||||
return escSequence(&sz)
|
// CTRL-A ~ CTRL-Z
|
||||||
|
if c >= CtrlA && c <= CtrlZ {
|
||||||
|
return Event{int(c), 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CTRL-A ~ CTRL-Z
|
// Multi-byte character
|
||||||
if _buf[0] <= CtrlZ {
|
buffer := []byte{byte(c)}
|
||||||
return Event{int(_buf[0]), 0, nil}
|
for {
|
||||||
|
r, _ := utf8.DecodeRune(buffer)
|
||||||
|
if r != utf8.RuneError {
|
||||||
|
return Event{Rune, r, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := C.getch()
|
||||||
|
if c == C.ERR {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c >= C.KEY_CODE_YES {
|
||||||
|
C.ungetch(c)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buffer = append(buffer, byte(c))
|
||||||
}
|
}
|
||||||
r, rsz := utf8.DecodeRune(_buf)
|
return Event{Invalid, 0, nil}
|
||||||
if r == utf8.RuneError {
|
|
||||||
return Event{ESC, 0, nil}
|
|
||||||
}
|
|
||||||
sz = rsz
|
|
||||||
return Event{Rune, r, nil}
|
|
||||||
}
|
}
|
||||||
|
@@ -11,8 +11,9 @@ import (
|
|||||||
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
// https://github.com/gdamore/tcell/pull/135
|
||||||
"github.com/gdamore/tcell/encoding"
|
"github.com/junegunn/tcell"
|
||||||
|
"github.com/junegunn/tcell/encoding"
|
||||||
|
|
||||||
"github.com/junegunn/go-runewidth"
|
"github.com/junegunn/go-runewidth"
|
||||||
)
|
)
|
||||||
@@ -43,11 +44,12 @@ type WindowTcell struct {
|
|||||||
type WindowImpl WindowTcell
|
type WindowImpl WindowTcell
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Bold = Attr(tcell.AttrBold)
|
Bold Attr = Attr(tcell.AttrBold)
|
||||||
Dim = Attr(tcell.AttrDim)
|
Dim = Attr(tcell.AttrDim)
|
||||||
Blink = Attr(tcell.AttrBlink)
|
Blink = Attr(tcell.AttrBlink)
|
||||||
Reverse = Attr(tcell.AttrReverse)
|
Reverse = Attr(tcell.AttrReverse)
|
||||||
Underline = Attr(tcell.AttrUnderline)
|
Underline = Attr(tcell.AttrUnderline)
|
||||||
|
Italic = Attr(tcell.AttrNone) // Not supported
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -183,7 +185,7 @@ func GetChar() Event {
|
|||||||
ev := _screen.PollEvent()
|
ev := _screen.PollEvent()
|
||||||
switch ev := ev.(type) {
|
switch ev := ev.(type) {
|
||||||
case *tcell.EventResize:
|
case *tcell.EventResize:
|
||||||
return Event{Invalid, 0, nil}
|
return Event{Resize, 0, nil}
|
||||||
|
|
||||||
// process mouse events:
|
// process mouse events:
|
||||||
case *tcell.EventMouse:
|
case *tcell.EventMouse:
|
||||||
@@ -207,8 +209,8 @@ func GetChar() Event {
|
|||||||
_clickY = append(_clickY, x)
|
_clickY = append(_clickY, x)
|
||||||
} else {
|
} else {
|
||||||
_clickY = []int{x}
|
_clickY = []int{x}
|
||||||
|
_prevDownTime = now
|
||||||
}
|
}
|
||||||
_prevDownTime = now
|
|
||||||
} else {
|
} else {
|
||||||
if len(_clickY) > 1 && _clickY[0] == _clickY[1] &&
|
if len(_clickY) > 1 && _clickY[0] == _clickY[1] &&
|
||||||
time.Now().Sub(_prevDownTime) < doubleClickDuration {
|
time.Now().Sub(_prevDownTime) < doubleClickDuration {
|
||||||
@@ -326,9 +328,9 @@ func GetChar() Event {
|
|||||||
case tcell.KeyF10:
|
case tcell.KeyF10:
|
||||||
return Event{F10, 0, nil}
|
return Event{F10, 0, nil}
|
||||||
case tcell.KeyF11:
|
case tcell.KeyF11:
|
||||||
return Event{Invalid, 0, nil}
|
return Event{F11, 0, nil}
|
||||||
case tcell.KeyF12:
|
case tcell.KeyF12:
|
||||||
return Event{Invalid, 0, nil}
|
return Event{F12, 0, nil}
|
||||||
|
|
||||||
// ev.Ch doesn't work for some reason for space:
|
// ev.Ch doesn't work for some reason for space:
|
||||||
case tcell.KeyRune:
|
case tcell.KeyRune:
|
||||||
@@ -343,6 +345,9 @@ func GetChar() Event {
|
|||||||
if r >= 'a' && r <= 'z' {
|
if r >= 'a' && r <= 'z' {
|
||||||
return Event{AltA + int(r) - 'a', 0, nil}
|
return Event{AltA + int(r) - 'a', 0, nil}
|
||||||
}
|
}
|
||||||
|
if r >= '0' && r <= '9' {
|
||||||
|
return Event{Alt0 + int(r) - '0', 0, nil}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Event{Rune, r, nil}
|
return Event{Rune, r, nil}
|
||||||
|
|
||||||
|
@@ -37,6 +37,7 @@ const (
|
|||||||
ESC
|
ESC
|
||||||
|
|
||||||
Invalid
|
Invalid
|
||||||
|
Resize
|
||||||
Mouse
|
Mouse
|
||||||
DoubleClick
|
DoubleClick
|
||||||
|
|
||||||
@@ -67,18 +68,24 @@ const (
|
|||||||
F8
|
F8
|
||||||
F9
|
F9
|
||||||
F10
|
F10
|
||||||
|
F11
|
||||||
|
F12
|
||||||
|
|
||||||
AltEnter
|
AltEnter
|
||||||
AltSpace
|
AltSpace
|
||||||
AltSlash
|
AltSlash
|
||||||
AltBS
|
AltBS
|
||||||
AltA
|
|
||||||
|
Alt0
|
||||||
|
)
|
||||||
|
|
||||||
|
const ( // Reset iota
|
||||||
|
AltA = Alt0 + 'a' - '0' + iota
|
||||||
AltB
|
AltB
|
||||||
AltC
|
AltC
|
||||||
AltD
|
AltD
|
||||||
AltE
|
AltE
|
||||||
AltF
|
AltF
|
||||||
|
|
||||||
AltZ = AltA + 'z' - 'a'
|
AltZ = AltA + 'z' - 'a'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,7 +93,11 @@ const (
|
|||||||
doubleClickDuration = 500 * time.Millisecond
|
doubleClickDuration = 500 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
type Color int16
|
type Color int32
|
||||||
|
|
||||||
|
func (c Color) is24() bool {
|
||||||
|
return c > 0 && (c&(1<<24)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
colUndefined Color = -2
|
colUndefined Color = -2
|
||||||
@@ -136,7 +147,6 @@ type MouseEvent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_buf []byte
|
|
||||||
_color bool
|
_color bool
|
||||||
_prevDownTime time.Time
|
_prevDownTime time.Time
|
||||||
_clickY []int
|
_clickY []int
|
||||||
|
Reference in New Issue
Block a user