Add 'transform-border-label' and 'transform-preview-label'

This commit is contained in:
Junegunn Choi 2023-01-22 01:56:29 +09:00
parent c3d73e7ecb
commit d51980a3f5
8 changed files with 199 additions and 128 deletions

View File

@ -1,8 +1,13 @@
CHANGELOG CHANGELOG
========= =========
0.36.1 0.37.0
------ ------
- New actions
- `change-border-label`
- `change-preview-label`
- `transform-border-label`
- `transform-preview-label`
- Bug fixes and improvements - Bug fixes and improvements
0.36.0 0.36.0

View File

@ -1050,6 +1050,8 @@ A key or an event can be bound to one or more of the following actions.
\fBtoggle-search\fR (toggle search functionality) \fBtoggle-search\fR (toggle search functionality)
\fBtoggle-sort\fR \fBtoggle-sort\fR
\fBtoggle+up\fR \fIbtab (shift-tab)\fR \fBtoggle+up\fR \fIbtab (shift-tab)\fR
\fBtransform-border-label(...)\fR (transform border label using an external command)
\fBtransform-preview-label(...)\fR (transform preview label using an external command)
\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)
\fBunbind(...)\fR (unbind bindings) \fBunbind(...)\fR (unbind bindings)

View File

@ -912,7 +912,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt)|change-preview-window|change-preview-label|change-border-label|change-preview|(?:re|un)bind|pos|put)`) `(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|border-label|preview-label)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@ -1220,16 +1220,16 @@ func isExecuteAction(str string) actionType {
return actRebind return actRebind
case "preview": case "preview":
return actPreview return actPreview
case "change-border-label":
return actChangeBorderLabel
case "change-preview-label":
return actChangePreviewLabel
case "change-preview-window": case "change-preview-window":
return actChangePreviewWindow return actChangePreviewWindow
case "change-preview": case "change-preview":
return actChangePreview return actChangePreview
case "change-preview-label":
return actChangePreviewLabel
case "change-prompt": case "change-prompt":
return actChangePrompt return actChangePrompt
case "change-border-label":
return actChangeBorderLabel
case "change-query": case "change-query":
return actChangeQuery return actChangeQuery
case "pos": case "pos":
@ -1242,6 +1242,10 @@ func isExecuteAction(str string) actionType {
return actExecuteMulti return actExecuteMulti
case "put": case "put":
return actPut return actPut
case "transform-border-label":
return actTransformBorderLabel
case "transform-preview-label":
return actTransformPreviewLabel
case "transform-prompt": case "transform-prompt":
return actTransformPrompt return actTransformPrompt
case "transform-query": case "transform-query":

View File

@ -276,6 +276,8 @@ const (
reqRefresh reqRefresh
reqReinit reqReinit
reqFullRedraw reqFullRedraw
reqRedrawBorderLabel
reqRedrawPreviewLabel
reqClose reqClose
reqPrintQuery reqPrintQuery
reqPreviewEnqueue reqPreviewEnqueue
@ -349,6 +351,8 @@ const (
actToggleSort actToggleSort
actTogglePreview actTogglePreview
actTogglePreviewWrap actTogglePreviewWrap
actTransformBorderLabel
actTransformPreviewLabel
actTransformPrompt actTransformPrompt
actTransformQuery actTransformQuery
actPreview actPreview
@ -1252,13 +1256,24 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
} }
// Print border label // Print border label
printLabel := func(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape) { t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border, false)
for i := 0; i < t.window.Height(); i++ {
t.window.MoveAndClear(i, 0)
}
}
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
if window == nil || render == nil { if window == nil || render == nil {
return return
} }
switch borderShape { switch borderShape {
case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble: case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
if redrawBorder {
window.DrawHBorder()
}
var col int var col int
if opts.column == 0 { if opts.column == 0 {
col = util.Max(0, (window.Width()-length)/2) col = util.Max(0, (window.Width()-length)/2)
@ -1274,13 +1289,6 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
window.Move(row, col) window.Move(row, col)
render(window, window.Width()) render(window, window.Width())
} }
}
printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape)
printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border)
for i := 0; i < t.window.Height(); i++ {
t.window.MoveAndClear(i, 0)
}
} }
func (t *Terminal) move(y int, x int, clear bool) { func (t *Terminal) move(y int, x int, clear bool) {
@ -2659,6 +2667,10 @@ func (t *Terminal) Loop() {
t.printHeader() t.printHeader()
case reqRefresh: case reqRefresh:
t.suppress = false t.suppress = false
case reqRedrawBorderLabel:
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, true)
case reqRedrawPreviewLabel:
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border, true)
case reqReinit: case reqReinit:
t.tui.Resume(t.fullscreen, t.sigstop) t.tui.Resume(t.fullscreen, t.sigstop)
t.redraw() t.redraw()
@ -2912,11 +2924,27 @@ func (t *Terminal) Loop() {
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)
case actChangeBorderLabel: case actChangeBorderLabel:
if t.border != nil {
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false) t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
req(reqFullRedraw) req(reqRedrawBorderLabel)
}
case actChangePreviewLabel: case actChangePreviewLabel:
if t.pborder != nil {
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false) t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
req(reqFullRedraw) req(reqRedrawPreviewLabel)
}
case actTransformBorderLabel:
if t.border != nil {
label := t.executeCommand(a.a, false, true, true)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel)
}
case actTransformPreviewLabel:
if t.pborder != nil {
label := t.executeCommand(a.a, false, true, true)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel)
}
case actChangePrompt: case actChangePrompt:
t.prompt, t.promptLen = t.parsePrompt(a.a) t.prompt, t.promptLen = t.parsePrompt(a.a)
req(reqPrompt) req(reqPrompt)

View File

@ -719,25 +719,38 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
w.fg = r.theme.Fg.Color w.fg = r.theme.Fg.Color
w.bg = r.theme.Bg.Color w.bg = r.theme.Bg.Color
} }
w.drawBorder() w.drawBorder(false)
return w return w
} }
func (w *LightWindow) drawBorder() { func (w *LightWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *LightWindow) drawBorder(onlyHorizontal bool) {
switch w.border.shape { switch w.border.shape {
case BorderRounded, BorderSharp, BorderBold, BorderDouble: case BorderRounded, BorderSharp, BorderBold, BorderDouble:
w.drawBorderAround() w.drawBorderAround(onlyHorizontal)
case BorderHorizontal: case BorderHorizontal:
w.drawBorderHorizontal(true, true) w.drawBorderHorizontal(true, true)
case BorderVertical: case BorderVertical:
if onlyHorizontal {
return
}
w.drawBorderVertical(true, true) w.drawBorderVertical(true, true)
case BorderTop: case BorderTop:
w.drawBorderHorizontal(true, false) w.drawBorderHorizontal(true, false)
case BorderBottom: case BorderBottom:
w.drawBorderHorizontal(false, true) w.drawBorderHorizontal(false, true)
case BorderLeft: case BorderLeft:
if onlyHorizontal {
return
}
w.drawBorderVertical(true, false) w.drawBorderVertical(true, false)
case BorderRight: case BorderRight:
if onlyHorizontal {
return
}
w.drawBorderVertical(false, true) w.drawBorderVertical(false, true)
} }
} }
@ -779,24 +792,26 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
} }
} }
func (w *LightWindow) drawBorderAround() { func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
w.Move(0, 0) w.Move(0, 0)
color := ColBorder color := ColBorder
if w.preview { if w.preview {
color = ColPreviewBorder color = ColPreviewBorder
} }
hw := runewidth.RuneWidth(w.border.horizontal) hw := runewidth.RuneWidth(w.border.horizontal)
vw := runewidth.RuneWidth(w.border.vertical)
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight) tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight) bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
rem := (w.width - tcw) % hw rem := (w.width - tcw) % hw
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight)) w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
if !onlyHorizontal {
vw := runewidth.RuneWidth(w.border.vertical)
for y := 1; y < w.height-1; y++ { for y := 1; y < w.height-1; y++ {
w.Move(y, 0) w.Move(y, 0)
w.CPrint(color, string(w.border.vertical)) w.CPrint(color, string(w.border.vertical))
w.CPrint(color, repeat(' ', w.width-vw*2)) w.CPrint(color, repeat(' ', w.width-vw*2))
w.CPrint(color, string(w.border.vertical)) w.CPrint(color, string(w.border.vertical))
} }
}
w.Move(w.height-1, 0) w.Move(w.height-1, 0)
rem = (w.width - bcw) % hw rem = (w.width - bcw) % hw
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, (w.width-bcw)/hw)+repeat(' ', rem)+string(w.border.bottomRight)) w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, (w.width-bcw)/hw)+repeat(' ', rem)+string(w.border.bottomRight))
@ -1040,7 +1055,7 @@ func (w *LightWindow) FinishFill() {
} }
func (w *LightWindow) Erase() { func (w *LightWindow) Erase() {
w.drawBorder() w.drawBorder(false)
// We don't erase the window here to avoid flickering during scroll // We don't erase the window here to avoid flickering during scroll
w.Move(0, 0) w.Move(0, 0)
} }

View File

@ -512,7 +512,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
height: height, height: height,
normal: normal, normal: normal,
borderStyle: borderStyle} borderStyle: borderStyle}
w.drawBorder() w.drawBorder(false)
return w return w
} }
@ -670,7 +670,11 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
return w.fillString(str, NewColorPair(fg, bg, a)) return w.fillString(str, NewColorPair(fg, bg, a))
} }
func (w *TcellWindow) drawBorder() { func (w *TcellWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
shape := w.borderStyle.shape shape := w.borderStyle.shape
if shape == BorderNone { if shape == BorderNone {
return return
@ -718,6 +722,7 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style) _screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
} }
} }
if !onlyHorizontal {
switch shape { switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderLeft: case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderLeft:
for y := top; y < bot; y++ { for y := top; y < bot; y++ {
@ -731,6 +736,7 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(right-vw, y, w.borderStyle.vertical, nil, style) _screen.SetContent(right-vw, y, w.borderStyle.vertical, nil, style)
} }
} }
}
switch shape { switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderDouble: case BorderRounded, BorderSharp, BorderBold, BorderDouble:
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style) _screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)

View File

@ -426,6 +426,7 @@ type Window interface {
Width() int Width() int
Height() int Height() int
DrawHBorder()
Refresh() Refresh()
FinishFill() FinishFill()
Close() Close()

View File

@ -2474,11 +2474,21 @@ class TestGoFZF < TestBase
end end
def test_labels_center def test_labels_center
tmux.send_keys ': | fzf --border --border-label foobar --preview : --preview-label barfoo', :Enter tmux.send_keys 'echo x | fzf --border --border-label foobar --preview : --preview-label barfoo --bind "space:change-border-label(foobarfoo)+change-preview-label(barfoobar),enter:transform-border-label(echo foo{}foo)+transform-preview-label(echo bar{}bar)"', :Enter
tmux.until do tmux.until do
assert_includes(_1[0], '─foobar─') assert_includes(_1[0], '─foobar─')
assert_includes(_1[1], '─barfoo─') assert_includes(_1[1], '─barfoo─')
end end
tmux.send_keys :space
tmux.until do
assert_includes(_1[0], '─foobarfoo─')
assert_includes(_1[1], '─barfoobar─')
end
tmux.send_keys :Enter
tmux.until do
assert_includes(_1[0], '─fooxfoo─')
assert_includes(_1[1], '─barxbar─')
end
end end
def test_labels_left def test_labels_left