m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-14 14:23:47 -05:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Junegunn Choi
8b0d0342d4 0.15.3 2016-09-29 03:05:20 +09:00
Junegunn Choi
957c12e7d7 Fix SEGV when trying to render preview but the window is closed
Close #677
2016-09-29 02:53:05 +09:00
Junegunn Choi
3b5ae0f8a2 Fix failing unit tests on ANSI attributes 2016-09-29 01:06:47 +09:00
Junegunn Choi
1fc5659842 Add support for more ANSI color attributes (#674)
Dim, underline, blink, reverse
2016-09-29 00:54:27 +09:00
Junegunn Choi
1acd2adce2 Update man page: missing actions 2016-09-26 15:33:46 +09:00
Junegunn Choi
1bc223d4b3 0.15.2 2016-09-25 22:20:43 +09:00
Junegunn Choi
bef405bfa5 Ignore VT100-related escape codes 2016-09-25 19:03:08 +09:00
Junegunn Choi
0612074abe Support high intensity colors
Close #671
2016-09-25 18:11:35 +09:00
Junegunn Choi
3bf51d8362 Merge pull request #670 from maverickwoo/fix-668
[bash-completion] Fix #668
2016-09-25 05:15:24 +09:00
Maverick Woo
2c8479a7c5 Fix #668
Handle uppercase letters in program names. This also deals with `-` and
`.`, both of which are quite common in program names, e.g., `xdg-open`
and `foo.sh`.
2016-09-24 15:39:13 -04:00
Junegunn Choi
8c8b5b313e Add preview-page-up and preview-page-down actions 2016-09-25 04:12:44 +09:00
Junegunn Choi
66d55fd893 Make preview windows scrollable
Close #669

You can use your mouse or binadble preview-up and preview-down actions
to scroll the content of the preview window.

    fzf --preview 'highlight -O ansi {}' --bind alt-j:preview-down,alt-k:preview-up
2016-09-25 02:02:00 +09:00
Junegunn Choi
7fa5e6c861 0.15.1 2016-09-21 01:28:24 +09:00
Junegunn Choi
00f96aae76 Avoid rendering delay when displaying extremely long lines
Related #666
2016-09-21 01:23:41 +09:00
Junegunn Choi
a749e6bd16 Fix temp directory in a test case 2016-09-21 01:15:35 +09:00
Junegunn Choi
791076d366 Fix panic when pattern occurs after 2^15-th column
Fix #666
2016-09-21 01:15:06 +09:00
16 changed files with 233 additions and 87 deletions

View File

@@ -1,6 +1,27 @@
CHANGELOG
=========
0.15.3
------
- Added support for more ANSI attributes: dim, underline, blink, and reverse
- Fixed race condition in `toggle-preview`
0.15.2
------
- Preview window is now scrollable
- With mouse scroll or with bindable actions
- `preview-up`
- `preview-down`
- `preview-page-up`
- `preview-page-down`
- Updated ANSI processor to support high intensity colors and ignore
some VT100-related escape sequences
0.15.1
------
- Fixed panic when the pattern occurs after 2^15-th column
- Fixed rendering delay when displaying extremely long lines
0.15.0
------
- Improved fuzzy search algorithm

View File

@@ -2,8 +2,8 @@
set -u
[[ "$@" =~ --pre ]] && version=0.15.0 pre=1 ||
version=0.15.0 pre=0
[[ "$@" =~ --pre ]] && version=0.15.3 pre=1 ||
version=0.15.3 pre=0
auto_completion=
key_bindings=

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf-tmux 1 "Sep 2016" "fzf 0.15.0" "fzf-tmux - open fzf in tmux split pane"
.TH fzf-tmux 1 "Sep 2016" "fzf 0.15.3" "fzf-tmux - open fzf in tmux split pane"
.SH NAME
fzf-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf 1 "Sep 2016" "fzf 0.15.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Sep 2016" "fzf 0.15.3" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@@ -434,6 +434,10 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
\fBnext-history\fR (\fIctrl-n\fR on \fB--history\fR)
\fBpage-down\fR \fIpgdn\fR
\fBpage-up\fR \fIpgup\fR
\fBpreview-down\fR
\fBpreview-up\fR
\fBpreview-page-down\fR
\fBpreview-page-up\fR
\fBprevious-history\fR (\fIctrl-p\fR on \fB--history\fR)
\fBprint-query\fR (print query and exit)
\fBselect-all\fR

View File

@@ -32,7 +32,7 @@ fi
_fzf_orig_completion_filter() {
sed 's/^\(.*-F\) *\([^ ]*\).* \([^ ]*\)$/export _fzf_orig_completion_\3="\1 %s \3 #\2";/' |
awk -F= '{gsub(/[^a-z0-9_= ;]/, "_", $1); print $1"="$2}'
awk -F= '{gsub(/[^A-Za-z0-9_= ;]/, "_", $1); print $1"="$2}'
}
_fzf_opts_completion() {
@@ -117,7 +117,7 @@ _fzf_handle_dynamic_completion() {
__fzf_generic_path_completion() {
local cur base dir leftover matches trigger cmd fzf
[ "${FZF_TMUX:-1}" != 0 ] && fzf="fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%}" || fzf="fzf"
cmd=$(echo "${COMP_WORDS[0]}" | sed 's/[^a-z0-9_=]/_/g')
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
COMPREPLY=()
trigger=${FZF_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
@@ -162,7 +162,7 @@ _fzf_complete() {
type -t "$post" > /dev/null 2>&1 || post=cat
[ "${FZF_TMUX:-1}" != 0 ] && fzf="fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%}" || fzf="fzf"
cmd=$(echo "${COMP_WORDS[0]}" | sed 's/[^a-z0-9_=]/_/g')
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
trigger=${FZF_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == *"$trigger" ]]; then
@@ -277,7 +277,7 @@ _fzf_defc() {
cmd="$1"
func="$2"
opts="$3"
orig_var="_fzf_orig_completion_$cmd"
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var}"
if [ -n "$orig" ]; then
printf -v def "$orig" "$func"

View File

@@ -257,13 +257,14 @@ func FuzzyMatchV2(caseSensitive bool, forward bool, input util.Chars, pattern []
}
// Reuse pre-allocated integer slice to avoid unnecessary sweeping of garbages
offset := 0
offset16 := 0
offset32 := 0
// Bonus point for each position
offset, B := alloc16(offset, slab, N, false)
offset16, B := alloc16(offset16, slab, N, false)
// The first occurrence of each character in the pattern
offset, F := alloc16(offset, slab, M, false)
offset32, F := alloc32(offset32, slab, M, false)
// Rune array
_, T := alloc32(0, slab, N, false)
offset32, T := alloc32(offset32, slab, N, false)
// Phase 1. Check if there's a match and calculate bonus for each point
pidx, lastIdx, prevClass := 0, 0, charNonWord
@@ -291,7 +292,7 @@ func FuzzyMatchV2(caseSensitive bool, forward bool, input util.Chars, pattern []
if pidx < M {
if char == pattern[pidx] {
lastIdx = idx
F[pidx] = int16(idx)
F[pidx] = int32(idx)
pidx++
}
} else {
@@ -307,10 +308,10 @@ func FuzzyMatchV2(caseSensitive bool, forward bool, input util.Chars, pattern []
// Phase 2. Fill in score matrix (H)
// Unlike the original algorithm, we do not allow omission.
width := lastIdx - int(F[0]) + 1
offset, H := alloc16(offset, slab, width*M, false)
offset16, H := alloc16(offset16, slab, width*M, false)
// Possible length of consecutive chunk at each position.
offset, C := alloc16(offset, slab, width*M, false)
offset16, C := alloc16(offset16, slab, width*M, false)
maxScore, maxScorePos := int16(0), 0
for i := 0; i < M; i++ {

View File

@@ -1,6 +1,7 @@
package algo
import (
"math"
"sort"
"strings"
"testing"
@@ -154,3 +155,12 @@ func TestEmptyPattern(t *testing.T) {
assertMatch(t, SuffixMatch, true, dir, "foobar", "", 6, 6, 0)
}
}
func TestLongString(t *testing.T) {
bytes := make([]byte, math.MaxUint16*2)
for i := range bytes {
bytes[i] = 'x'
}
bytes[math.MaxUint16] = 'z'
assertMatch(t, FuzzyMatchV2, true, true, string(bytes), "zx", math.MaxUint16, math.MaxUint16+2, scoreMatch*2+bonusConsecutive)
}

View File

@@ -6,6 +6,8 @@ import (
"strconv"
"strings"
"unicode/utf8"
"github.com/junegunn/fzf/src/curses"
)
type ansiOffset struct {
@@ -16,24 +18,24 @@ type ansiOffset struct {
type ansiState struct {
fg int
bg int
bold bool
attr curses.Attr
}
func (s *ansiState) colored() bool {
return s.fg != -1 || s.bg != -1 || s.bold
return s.fg != -1 || s.bg != -1 || s.attr > 0
}
func (s *ansiState) equals(t *ansiState) bool {
if t == nil {
return !s.colored()
}
return s.fg == t.fg && s.bg == t.bg && s.bold == t.bold
return s.fg == t.fg && s.bg == t.bg && s.attr == t.attr
}
var ansiRegex *regexp.Regexp
func init() {
ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
ansiRegex = regexp.MustCompile("\x1b.[0-9;]*.")
}
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, *[]ansiOffset, *ansiState) {
@@ -94,11 +96,11 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
// State
var state *ansiState
if prevState == nil {
state = &ansiState{-1, -1, false}
state = &ansiState{-1, -1, 0}
} else {
state = &ansiState{prevState.fg, prevState.bg, prevState.bold}
state = &ansiState{prevState.fg, prevState.bg, prevState.attr}
}
if ansiCode[len(ansiCode)-1] == 'K' {
if ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
return state
}
@@ -108,7 +110,7 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
init := func() {
state.fg = -1
state.bg = -1
state.bold = false
state.attr = 0
state256 = 0
}
@@ -132,7 +134,15 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
case 49:
state.bg = -1
case 1:
state.bold = true
state.attr = curses.Bold
case 2:
state.attr = curses.Dim
case 4:
state.attr = curses.Underline
case 5:
state.attr = curses.Blink
case 7:
state.attr = curses.Reverse
case 0:
init()
default:
@@ -140,6 +150,10 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
state.fg = num - 30
} else if num >= 40 && num <= 47 {
state.bg = num - 40
} else if num >= 90 && num <= 97 {
state.fg = num - 90 + 8
} else if num >= 100 && num <= 107 {
state.bg = num - 100 + 8
}
}
case 1:

View File

@@ -3,13 +3,19 @@ package fzf
import (
"fmt"
"testing"
"github.com/junegunn/fzf/src/curses"
)
func TestExtractColor(t *testing.T) {
assert := func(offset ansiOffset, b int32, e int32, fg int, bg int, bold bool) {
var attr curses.Attr
if bold {
attr = curses.Bold
}
if offset.offset[0] != b || offset.offset[1] != e ||
offset.color.fg != fg || offset.color.bg != bg || offset.color.bold != bold {
t.Error(offset, b, e, fg, bg, bold)
offset.color.fg != fg || offset.color.bg != bg || offset.color.attr != attr {
t.Error(offset, b, e, fg, bg, attr)
}
}
@@ -121,7 +127,7 @@ func TestExtractColor(t *testing.T) {
if len(*offsets) != 1 {
t.Fail()
}
if state.fg != 2 || state.bg != -1 || !state.bold {
if state.fg != 2 || state.bg != -1 || state.attr == 0 {
t.Fail()
}
assert((*offsets)[0], 6, 11, 2, -1, true)
@@ -132,7 +138,7 @@ func TestExtractColor(t *testing.T) {
if len(*offsets) != 1 {
t.Fail()
}
if state.fg != 2 || state.bg != -1 || !state.bold {
if state.fg != 2 || state.bg != -1 || state.attr == 0 {
t.Fail()
}
assert((*offsets)[0], 0, 11, 2, -1, true)
@@ -143,7 +149,7 @@ func TestExtractColor(t *testing.T) {
if len(*offsets) != 2 {
t.Fail()
}
if state.fg != 200 || state.bg != 100 || state.bold {
if state.fg != 200 || state.bg != 100 || state.attr > 0 {
t.Fail()
}
assert((*offsets)[0], 0, 6, 2, -1, true)

View File

@@ -8,7 +8,7 @@ import (
const (
// Current version
version = "0.15.0"
version = "0.15.3"
// Core
coordinatorDelayMax time.Duration = 100 * time.Millisecond

View File

@@ -23,6 +23,16 @@ import (
"unicode/utf8"
)
const (
Bold = C.A_BOLD
Dim = C.A_DIM
Blink = C.A_BLINK
Reverse = C.A_REVERSE
Underline = C.A_UNDERLINE
)
type Attr C.int
// Types of user action
const (
Rune = iota
@@ -158,7 +168,7 @@ type MouseEvent struct {
var (
_buf []byte
_in *os.File
_color func(int, bool) C.int
_color func(int, Attr) C.int
_colorMap map[int]int
_prevDownTime time.Time
_clickY []int
@@ -183,7 +193,7 @@ type Window struct {
func NewWindow(top int, left int, width int, height int, border bool) *Window {
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
if border {
attr := _color(ColBorder, false)
attr := _color(ColBorder, 0)
C.wattron(win, attr)
C.box(win, 0, 0)
C.wattroff(win, attr)
@@ -266,22 +276,19 @@ func init() {
Border: 145}
}
func attrColored(pair int, bold bool) C.int {
func attrColored(pair int, a Attr) C.int {
var attr C.int
if pair > ColNormal {
attr = C.COLOR_PAIR(C.int(pair))
}
if bold {
attr = attr | C.A_BOLD
}
return attr
return attr | C.int(a)
}
func attrMono(pair int, bold bool) C.int {
func attrMono(pair int, a Attr) C.int {
var attr C.int
switch pair {
case ColCurrent:
if bold {
if a&C.A_BOLD == C.A_BOLD {
attr = C.A_REVERSE
}
case ColMatch:
@@ -289,7 +296,7 @@ func attrMono(pair int, bold bool) C.int {
case ColCurrentMatch:
attr = C.A_UNDERLINE | C.A_REVERSE
}
if bold {
if a&C.A_BOLD == C.A_BOLD {
attr = attr | C.A_BOLD
}
return attr
@@ -648,8 +655,8 @@ func (w *Window) Print(text string) {
}, text)))
}
func (w *Window) CPrint(pair int, bold bool, text string) {
attr := _color(pair, bold)
func (w *Window) CPrint(pair int, a Attr, text string) {
attr := _color(pair, a)
C.wattron(w.win, attr)
w.Print(text)
C.wattroff(w.win, attr)
@@ -675,8 +682,8 @@ func (w *Window) Fill(str string) bool {
return C.waddstr(w.win, C.CString(str)) == C.OK
}
func (w *Window) CFill(str string, fg int, bg int, bold bool) bool {
attr := _color(PairFor(fg, bg), bold)
func (w *Window) CFill(str string, fg int, bg int, a Attr) bool {
attr := _color(PairFor(fg, bg), a)
C.wattron(w.win, attr)
ret := w.Fill(str)
C.wattroff(w.win, attr)

View File

@@ -663,6 +663,14 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string)
keymap[key] = actTogglePreview
case "toggle-sort":
keymap[key] = actToggleSort
case "preview-up":
keymap[key] = actPreviewUp
case "preview-down":
keymap[key] = actPreviewDown
case "preview-page-up":
keymap[key] = actPreviewPageUp
case "preview-page-down":
keymap[key] = actPreviewPageDown
default:
if isExecuteAction(actLower) {
var offset int

View File

@@ -342,7 +342,7 @@ func TestDefaultCtrlNP(t *testing.T) {
check([]string{"--bind=ctrl-n:accept"}, curses.CtrlN, actAccept)
check([]string{"--bind=ctrl-p:accept"}, curses.CtrlP, actAccept)
hist := "--history=/tmp/foo"
hist := "--history=/tmp/fzf-history"
check([]string{hist}, curses.CtrlN, actNextHistory)
check([]string{hist}, curses.CtrlP, actPreviousHistory)

View File

@@ -14,7 +14,7 @@ type Offset [2]int32
type colorOffset struct {
offset [2]int32
color int
bold bool
attr curses.Attr
index int32
}
@@ -91,14 +91,14 @@ func minRank() rank {
return rank{index: 0, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
}
func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool, current bool) []colorOffset {
func (result *Result) colorOffsets(matchOffsets []Offset, color int, attr curses.Attr, current bool) []colorOffset {
itemColors := result.item.Colors()
if len(itemColors) == 0 {
var offsets []colorOffset
for _, off := range matchOffsets {
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, bold: bold})
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, attr: attr})
}
return offsets
}
@@ -142,7 +142,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool,
if curr != 0 && idx > start {
if curr == -1 {
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, color: color, bold: bold})
offset: [2]int32{int32(start), int32(idx)}, color: color, attr: attr})
} else {
ansi := itemColors[curr-1]
fg := ansi.color.fg
@@ -164,7 +164,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool,
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)},
color: curses.PairFor(fg, bg),
bold: ansi.color.bold || bold})
attr: ansi.color.attr | attr})
}
}
}

View File

@@ -97,16 +97,20 @@ func TestColorOffset(t *testing.T) {
item := Result{
item: &Item{
colors: &[]ansiOffset{
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, false}},
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, true}},
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, false}},
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, true}}}}}
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, curses.Bold}},
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, curses.Bold}}}}}
// [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}]
colors := item.colorOffsets(offsets, 99, false, true)
colors := item.colorOffsets(offsets, 99, 0, true)
assert := func(idx int, b int32, e int32, c int, bold bool) {
var attr curses.Attr
if bold {
attr = curses.Bold
}
o := colors[idx]
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.bold != bold {
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
t.Error(o)
}
}

View File

@@ -28,6 +28,13 @@ const (
jumpAcceptEnabled
)
type previewer struct {
text string
lines int
offset int
enabled bool
}
// Terminal represents terminal input/output
type Terminal struct {
initDelay time.Duration
@@ -68,8 +75,7 @@ type Terminal struct {
selected map[int32]selectedItem
reqBox *util.EventBox
preview previewOpts
previewing bool
previewTxt string
previewer previewer
previewBox *util.EventBox
eventBox *util.EventBox
mutex sync.Mutex
@@ -119,6 +125,7 @@ const (
reqPrintQuery
reqPreviewEnqueue
reqPreviewDisplay
reqPreviewRefresh
reqQuit
)
@@ -165,6 +172,10 @@ const (
actPrintQuery
actToggleSort
actTogglePreview
actPreviewUp
actPreviewDown
actPreviewPageUp
actPreviewPageDown
actPreviousHistory
actNextHistory
actExecute
@@ -275,8 +286,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
selected: make(map[int32]selectedItem),
reqBox: util.NewEventBox(),
preview: opts.Preview,
previewing: previewBox != nil && !opts.Preview.hidden,
previewTxt: "",
previewer: previewer{"", 0, 0, previewBox != nil && !opts.Preview.hidden},
previewBox: previewBox,
eventBox: eventBox,
mutex: sync.Mutex{},
@@ -401,6 +411,8 @@ func displayWidth(runes []rune) int {
const (
minWidth = 16
minHeight = 4
maxDisplayWidthCalc = 1024
)
func calculateSize(base int, size sizeSpec, margin int, minSize int) int {
@@ -514,24 +526,24 @@ func (t *Terminal) placeCursor() {
func (t *Terminal) printPrompt() {
t.move(0, 0, true)
t.window.CPrint(C.ColPrompt, true, t.prompt)
t.window.CPrint(C.ColNormal, true, string(t.input))
t.window.CPrint(C.ColPrompt, C.Bold, t.prompt)
t.window.CPrint(C.ColNormal, C.Bold, string(t.input))
}
func (t *Terminal) printInfo() {
if t.inlineInfo {
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
if t.reading {
t.window.CPrint(C.ColSpinner, true, " < ")
t.window.CPrint(C.ColSpinner, C.Bold, " < ")
} else {
t.window.CPrint(C.ColPrompt, true, " < ")
t.window.CPrint(C.ColPrompt, C.Bold, " < ")
}
} else {
t.move(1, 0, true)
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
t.window.CPrint(C.ColSpinner, true, _spinner[idx])
t.window.CPrint(C.ColSpinner, C.Bold, _spinner[idx])
}
t.move(1, 2, false)
}
@@ -550,7 +562,7 @@ func (t *Terminal) printInfo() {
if t.progress > 0 && t.progress < 100 {
output += fmt.Sprintf(" (%d%%)", t.progress)
}
t.window.CPrint(C.ColInfo, false, output)
t.window.CPrint(C.ColInfo, 0, output)
}
func (t *Terminal) printHeader() {
@@ -574,7 +586,7 @@ func (t *Terminal) printHeader() {
colors: colors}
t.move(line, 2, true)
t.printHighlighted(&Result{item: item}, false, C.ColHeader, 0, false)
t.printHighlighted(&Result{item: item}, 0, C.ColHeader, 0, false)
}
}
@@ -608,21 +620,21 @@ func (t *Terminal) printItem(result *Result, i int, current bool) {
} else if current {
label = ">"
}
t.window.CPrint(C.ColCursor, true, label)
t.window.CPrint(C.ColCursor, C.Bold, label)
if current {
if selected {
t.window.CPrint(C.ColSelected, true, ">")
t.window.CPrint(C.ColSelected, C.Bold, ">")
} else {
t.window.CPrint(C.ColCurrent, true, " ")
t.window.CPrint(C.ColCurrent, C.Bold, " ")
}
t.printHighlighted(result, true, C.ColCurrent, C.ColCurrentMatch, true)
t.printHighlighted(result, C.Bold, C.ColCurrent, C.ColCurrentMatch, true)
} else {
if selected {
t.window.CPrint(C.ColSelected, true, ">")
t.window.CPrint(C.ColSelected, C.Bold, ">")
} else {
t.window.Print(" ")
}
t.printHighlighted(result, false, 0, C.ColMatch, false)
t.printHighlighted(result, 0, 0, C.ColMatch, false)
}
}
@@ -651,6 +663,11 @@ func displayWidthWithLimit(runes []rune, prefixWidth int, limit int) int {
}
func trimLeft(runes []rune, width int) ([]rune, int32) {
if len(runes) > maxDisplayWidthCalc && len(runes) > width {
trimmed := len(runes) - width
return runes[trimmed:], int32(trimmed)
}
currentWidth := displayWidth(runes)
var trimmed int32
@@ -673,7 +690,7 @@ func overflow(runes []rune, max int) bool {
return false
}
func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 int, current bool) {
func (t *Terminal) printHighlighted(result *Result, attr C.Attr, col1 int, col2 int, current bool) {
item := result.item
// Overflow
@@ -698,7 +715,7 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
maxe = util.Max(maxe, int(offset[1]))
}
offsets := result.colorOffsets(charOffsets, col2, bold, current)
offsets := result.colorOffsets(charOffsets, col2, attr, current)
maxWidth := t.window.Width - 3
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
if overflow(text, maxWidth) {
@@ -747,11 +764,11 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
e := util.Constrain32(offset.offset[1], index, maxOffset)
substr, prefixWidth = processTabs(text[index:b], prefixWidth)
t.window.CPrint(col1, bold, substr)
t.window.CPrint(col1, attr, substr)
if b < e {
substr, prefixWidth = processTabs(text[b:e], prefixWidth)
t.window.CPrint(offset.color, offset.bold, substr)
t.window.CPrint(offset.color, offset.attr, substr)
}
index = e
@@ -761,15 +778,44 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
}
if index < maxOffset {
substr, _ = processTabs(text[index:], prefixWidth)
t.window.CPrint(col1, bold, substr)
t.window.CPrint(col1, attr, substr)
}
}
func numLinesMax(str string, max int) int {
lines := 0
for lines < max {
idx := strings.Index(str, "\n")
if idx < 0 {
break
}
str = str[idx+1:]
lines++
}
return lines
}
func (t *Terminal) printPreview() {
if !t.isPreviewEnabled() {
return
}
t.pwindow.Erase()
extractColor(t.previewTxt, nil, func(str string, ansi *ansiState) bool {
skip := t.previewer.offset
extractColor(t.previewer.text, nil, func(str string, ansi *ansiState) bool {
if skip > 0 {
newlines := numLinesMax(str, skip)
if skip <= newlines {
for i := 0; i < skip; i++ {
str = str[strings.Index(str, "\n")+1:]
}
skip = 0
} else {
skip -= newlines
return true
}
}
if ansi != nil && ansi.colored() {
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.bold)
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.attr)
}
return t.pwindow.Fill(str)
})
@@ -796,9 +842,7 @@ func (t *Terminal) printAll() {
t.printPrompt()
t.printInfo()
t.printHeader()
if t.isPreviewEnabled() {
t.printPreview()
}
t.printPreview()
}
func (t *Terminal) refresh() {
@@ -884,7 +928,7 @@ func (t *Terminal) hasPreviewWindow() bool {
}
func (t *Terminal) isPreviewEnabled() bool {
return t.previewBox != nil && t.previewing
return t.previewBox != nil && t.previewer.enabled
}
func (t *Terminal) current() string {
@@ -1026,7 +1070,11 @@ func (t *Terminal) Loop() {
}
exit(exitNoMatch)
case reqPreviewDisplay:
t.previewTxt = value.(string)
t.previewer.text = value.(string)
t.previewer.lines = strings.Count(t.previewer.text, "\n")
t.previewer.offset = 0
t.printPreview()
case reqPreviewRefresh:
t.printPreview()
case reqPrintQuery:
C.Close()
@@ -1078,6 +1126,11 @@ func (t *Terminal) Loop() {
req(reqInfo)
}
}
scrollPreview := func(amount int) {
t.previewer.offset = util.Constrain(
t.previewer.offset+amount, 0, t.previewer.lines-t.pwindow.Height)
req(reqPreviewRefresh)
}
for key, ret := range t.expect {
if keyMatch(key, event) {
t.pressed = ret
@@ -1111,10 +1164,10 @@ func (t *Terminal) Loop() {
return false
case actTogglePreview:
if t.hasPreviewWindow() {
t.previewing = !t.previewing
t.previewer.enabled = !t.previewer.enabled
t.resizeWindows()
cnt := t.merger.Length()
if t.previewing && cnt > 0 && cnt > t.cy {
if t.previewer.enabled && cnt > 0 && cnt > t.cy {
t.previewBox.Set(reqPreviewEnqueue, previewRequest{true, t.current()})
}
req(reqList, reqInfo)
@@ -1124,6 +1177,22 @@ func (t *Terminal) Loop() {
t.eventBox.Set(EvtSearchNew, t.sort)
t.mutex.Unlock()
return false
case actPreviewUp:
if t.isPreviewEnabled() {
scrollPreview(-1)
}
case actPreviewDown:
if t.isPreviewEnabled() {
scrollPreview(1)
}
case actPreviewPageUp:
if t.isPreviewEnabled() {
scrollPreview(-t.pwindow.Height)
}
case actPreviewPageDown:
if t.isPreviewEnabled() {
scrollPreview(t.pwindow.Height)
}
case actBeginningOfLine:
t.cx = 0
case actBackwardChar:
@@ -1292,6 +1361,8 @@ func (t *Terminal) Loop() {
}
t.vmove(me.S)
req(reqList)
} else if t.isPreviewEnabled() && t.pwindow.Enclose(my, mx) {
scrollPreview(-me.S)
}
} else if t.window.Enclose(my, mx) {
mx -= t.window.Left