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

Compare commits

...

5 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
11 changed files with 101 additions and 64 deletions

View File

@@ -1,6 +1,11 @@
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

View File

@@ -2,8 +2,8 @@
set -u
[[ "$@" =~ --pre ]] && version=0.15.2 pre=1 ||
version=0.15.2 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.2" "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.2" "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

@@ -6,6 +6,8 @@ import (
"strconv"
"strings"
"unicode/utf8"
"github.com/junegunn/fzf/src/curses"
)
type ansiOffset struct {
@@ -16,18 +18,18 @@ 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
@@ -94,9 +96,9 @@ 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[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:

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.2"
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

@@ -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

@@ -526,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)
}
@@ -562,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() {
@@ -586,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)
}
}
@@ -620,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)
}
}
@@ -690,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
@@ -715,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) {
@@ -764,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
@@ -778,7 +778,7 @@ 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)
}
}
@@ -796,6 +796,9 @@ func numLinesMax(str string, max int) int {
}
func (t *Terminal) printPreview() {
if !t.isPreviewEnabled() {
return
}
t.pwindow.Erase()
skip := t.previewer.offset
extractColor(t.previewer.text, nil, func(str string, ansi *ansiState) bool {
@@ -812,7 +815,7 @@ func (t *Terminal) printPreview() {
}
}
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)
})
@@ -839,10 +842,8 @@ func (t *Terminal) printAll() {
t.printPrompt()
t.printInfo()
t.printHeader()
if t.isPreviewEnabled() {
t.printPreview()
}
}
func (t *Terminal) refresh() {
if !t.suppress {