Compare commits

...

2 Commits

Author SHA1 Message Date
Junegunn Choi
e5cfc988ec
Fix RuboCop error 2025-01-02 16:55:56 +09:00
Junegunn Choi
ee3916be17
Border around the input section (prompt + info)
Close #4154
2025-01-02 16:25:00 +09:00
10 changed files with 743 additions and 373 deletions

View File

@ -3,7 +3,7 @@ CHANGELOG
0.58.0
------
- Additional border and label for the list section
- Border and label for the list section
- Options
- `--list-border[=STYLE]`
- `--list-label=LABEL`
@ -16,6 +16,22 @@ CHANGELOG
- Actions
- `change-list-label`
- `transform-list-label`
- Border and label for the input section (prompt and info)
- Options
- `--input-border[=STYLE]`
- `--input-label=LABEL`
- `--input-label-pos=COL[:bottom]`
- Colors
- `input-fg` (`query`)
- `input-bg`
- `input-border`
- `input-label`
- Actions
- `change-input-label`
- `transform-input-label`
- Added `--preview-border[=STYLE]` as short for `--preview-window=border-[STYLE]`
- Added `toggle-multi-line` action
- Added `toggle-hscroll` action
0.57.0
------

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 "Dec 2024" "fzf 0.57.0" "fzf\-tmux - open fzf in tmux split pane"
.TH fzf\-tmux 1 "Jan 2025" "fzf 0.58.0" "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 "Dec 2024" "fzf 0.57.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Jan 2025" "fzf 0.58.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@ -557,16 +557,17 @@ color mappings.
\fBselected\-fg \fRSelected line text
\fBpreview\-fg \fRPreview window text
\fBbg \fRBackground
\fBlist\-bg \fRBackground in the list section
\fBlist\-bg \fRList section background
\fBselected\-bg \fRSelected line background
\fBpreview\-bg \fRPreview window background
\fBinput\-bg \fRInput section background
\fBhl \fRHighlighted substrings
\fBselected\-hl \fRHighlighted substrings in the selected line
\fBcurrent\-fg (fg+) \fRText (current line)
\fBcurrent\-bg (bg+) \fRBackground (current line)
\fBgutter \fRGutter on the left
\fBcurrent\-hl (hl+) \fRHighlighted substrings (current line)
\fBquery \fRQuery string
\fBquery (input\-fg) \fRQuery string
\fBdisabled \fRQuery string when search is disabled (\fB\-\-disabled\fR)
\fBinfo \fRInfo line (match counters)
\fBborder \fRBorder around the window (\fB\-\-border\fR and \fB\-\-preview\fR)
@ -575,9 +576,11 @@ color mappings.
\fBseparator \fRHorizontal separator on info line
\fBpreview\-border \fRBorder around the preview window (\fB\-\-preview\fR)
\fBpreview\-scrollbar \fRScrollbar
\fBlabel \fRBorder label (\fB\-\-border\-label\fR, \fB\-\-list\-label\fR, and \fB\-\-preview\-label\fR)
\fBinput\-border \fRBorder around the input section (\fB\-\-input\-border\fR)
\fBlabel \fRBorder label (\fB\-\-border\-label\fR, \fB\-\-list\-label\fR, \fB\-\-input\-label\fR, and \fB\-\-preview\-label\fR)
\fBlist\-label \fRBorder label of the list section (\fB\-\-list\-label\fR)
\fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
\fBinput\-label \fRBorder label of the input section (\fB\-\-input\-label\fR)
\fBprompt \fRPrompt
\fBpointer \fRPointer to the current line
\fBmarker \fRMulti\-select marker
@ -728,6 +731,10 @@ e.g.
.RE
.TP
.BI "\-\-preview\-border" [=STYLE]
Short for \fB\-\-preview\-window=border\-STYLE\fR
.TP
.BI "\-\-preview\-label" [=LABEL]
Label to print on the horizontal border line of the preview window.
@ -1444,6 +1451,7 @@ A key or an event can be bound to one or more of the following actions.
\fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string)
\fBchange\-header(...)\fR (change header to the given string; doesn't affect \fB\-\-header\-lines\fR)
\fBchange\-input\-label(...)\fR (change \fB\-\-input\-label\fR to the given string)
\fBchange\-list\-label(...)\fR (change \fB\-\-list\-label\fR to the given string)
\fBchange\-multi\fR (enable multi-select mode with no limit)
\fBchange\-multi(...)\fR (enable multi-select mode with a limit or disable it with 0)
@ -1529,6 +1537,7 @@ A key or an event can be bound to one or more of the following actions.
\fBtransform(...)\fR (transform states using the output of an external command)
\fBtransform\-border\-label(...)\fR (transform border label using an external command)
\fBtransform\-header(...)\fR (transform header using an external command)
\fBtransform\-input\-label(...)\fR (transform input label using an external command)
\fBtransform\-list\-label(...)\fR (transform list 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)

View File

@ -26,110 +26,112 @@ func _() {
_ = x[actCancel-15]
_ = x[actChangeBorderLabel-16]
_ = x[actChangeListLabel-17]
_ = x[actChangeHeader-18]
_ = x[actChangeMulti-19]
_ = x[actChangePreviewLabel-20]
_ = x[actChangePrompt-21]
_ = x[actChangeQuery-22]
_ = x[actClearScreen-23]
_ = x[actClearQuery-24]
_ = x[actClearSelection-25]
_ = x[actClose-26]
_ = x[actDeleteChar-27]
_ = x[actDeleteCharEof-28]
_ = x[actEndOfLine-29]
_ = x[actFatal-30]
_ = x[actForwardChar-31]
_ = x[actForwardWord-32]
_ = x[actKillLine-33]
_ = x[actKillWord-34]
_ = x[actUnixLineDiscard-35]
_ = x[actUnixWordRubout-36]
_ = x[actYank-37]
_ = x[actBackwardKillWord-38]
_ = x[actSelectAll-39]
_ = x[actDeselectAll-40]
_ = x[actToggle-41]
_ = x[actToggleSearch-42]
_ = x[actToggleAll-43]
_ = x[actToggleDown-44]
_ = x[actToggleUp-45]
_ = x[actToggleIn-46]
_ = x[actToggleOut-47]
_ = x[actToggleTrack-48]
_ = x[actToggleTrackCurrent-49]
_ = x[actToggleHeader-50]
_ = x[actToggleWrap-51]
_ = x[actToggleMultiLine-52]
_ = x[actToggleHscroll-53]
_ = x[actTrackCurrent-54]
_ = x[actUntrackCurrent-55]
_ = x[actDown-56]
_ = x[actUp-57]
_ = x[actPageUp-58]
_ = x[actPageDown-59]
_ = x[actPosition-60]
_ = x[actHalfPageUp-61]
_ = x[actHalfPageDown-62]
_ = x[actOffsetUp-63]
_ = x[actOffsetDown-64]
_ = x[actOffsetMiddle-65]
_ = x[actJump-66]
_ = x[actJumpAccept-67]
_ = x[actPrintQuery-68]
_ = x[actRefreshPreview-69]
_ = x[actReplaceQuery-70]
_ = x[actToggleSort-71]
_ = x[actShowPreview-72]
_ = x[actHidePreview-73]
_ = x[actTogglePreview-74]
_ = x[actTogglePreviewWrap-75]
_ = x[actTransform-76]
_ = x[actTransformBorderLabel-77]
_ = x[actTransformListLabel-78]
_ = x[actTransformHeader-79]
_ = x[actTransformPreviewLabel-80]
_ = x[actTransformPrompt-81]
_ = x[actTransformQuery-82]
_ = x[actPreview-83]
_ = x[actChangePreview-84]
_ = x[actChangePreviewWindow-85]
_ = x[actPreviewTop-86]
_ = x[actPreviewBottom-87]
_ = x[actPreviewUp-88]
_ = x[actPreviewDown-89]
_ = x[actPreviewPageUp-90]
_ = x[actPreviewPageDown-91]
_ = x[actPreviewHalfPageUp-92]
_ = x[actPreviewHalfPageDown-93]
_ = x[actPrevHistory-94]
_ = x[actPrevSelected-95]
_ = x[actPrint-96]
_ = x[actPut-97]
_ = x[actNextHistory-98]
_ = x[actNextSelected-99]
_ = x[actExecute-100]
_ = x[actExecuteSilent-101]
_ = x[actExecuteMulti-102]
_ = x[actSigStop-103]
_ = x[actFirst-104]
_ = x[actLast-105]
_ = x[actReload-106]
_ = x[actReloadSync-107]
_ = x[actDisableSearch-108]
_ = x[actEnableSearch-109]
_ = x[actSelect-110]
_ = x[actDeselect-111]
_ = x[actUnbind-112]
_ = x[actRebind-113]
_ = x[actBecome-114]
_ = x[actShowHeader-115]
_ = x[actHideHeader-116]
_ = x[actChangeInputLabel-18]
_ = x[actChangeHeader-19]
_ = x[actChangeMulti-20]
_ = x[actChangePreviewLabel-21]
_ = x[actChangePrompt-22]
_ = x[actChangeQuery-23]
_ = x[actClearScreen-24]
_ = x[actClearQuery-25]
_ = x[actClearSelection-26]
_ = x[actClose-27]
_ = x[actDeleteChar-28]
_ = x[actDeleteCharEof-29]
_ = x[actEndOfLine-30]
_ = x[actFatal-31]
_ = x[actForwardChar-32]
_ = x[actForwardWord-33]
_ = x[actKillLine-34]
_ = x[actKillWord-35]
_ = x[actUnixLineDiscard-36]
_ = x[actUnixWordRubout-37]
_ = x[actYank-38]
_ = x[actBackwardKillWord-39]
_ = x[actSelectAll-40]
_ = x[actDeselectAll-41]
_ = x[actToggle-42]
_ = x[actToggleSearch-43]
_ = x[actToggleAll-44]
_ = x[actToggleDown-45]
_ = x[actToggleUp-46]
_ = x[actToggleIn-47]
_ = x[actToggleOut-48]
_ = x[actToggleTrack-49]
_ = x[actToggleTrackCurrent-50]
_ = x[actToggleHeader-51]
_ = x[actToggleWrap-52]
_ = x[actToggleMultiLine-53]
_ = x[actToggleHscroll-54]
_ = x[actTrackCurrent-55]
_ = x[actUntrackCurrent-56]
_ = x[actDown-57]
_ = x[actUp-58]
_ = x[actPageUp-59]
_ = x[actPageDown-60]
_ = x[actPosition-61]
_ = x[actHalfPageUp-62]
_ = x[actHalfPageDown-63]
_ = x[actOffsetUp-64]
_ = x[actOffsetDown-65]
_ = x[actOffsetMiddle-66]
_ = x[actJump-67]
_ = x[actJumpAccept-68]
_ = x[actPrintQuery-69]
_ = x[actRefreshPreview-70]
_ = x[actReplaceQuery-71]
_ = x[actToggleSort-72]
_ = x[actShowPreview-73]
_ = x[actHidePreview-74]
_ = x[actTogglePreview-75]
_ = x[actTogglePreviewWrap-76]
_ = x[actTransform-77]
_ = x[actTransformBorderLabel-78]
_ = x[actTransformListLabel-79]
_ = x[actTransformInputLabel-80]
_ = x[actTransformHeader-81]
_ = x[actTransformPreviewLabel-82]
_ = x[actTransformPrompt-83]
_ = x[actTransformQuery-84]
_ = x[actPreview-85]
_ = x[actChangePreview-86]
_ = x[actChangePreviewWindow-87]
_ = x[actPreviewTop-88]
_ = x[actPreviewBottom-89]
_ = x[actPreviewUp-90]
_ = x[actPreviewDown-91]
_ = x[actPreviewPageUp-92]
_ = x[actPreviewPageDown-93]
_ = x[actPreviewHalfPageUp-94]
_ = x[actPreviewHalfPageDown-95]
_ = x[actPrevHistory-96]
_ = x[actPrevSelected-97]
_ = x[actPrint-98]
_ = x[actPut-99]
_ = x[actNextHistory-100]
_ = x[actNextSelected-101]
_ = x[actExecute-102]
_ = x[actExecuteSilent-103]
_ = x[actExecuteMulti-104]
_ = x[actSigStop-105]
_ = x[actFirst-106]
_ = x[actLast-107]
_ = x[actReload-108]
_ = x[actReloadSync-109]
_ = x[actDisableSearch-110]
_ = x[actEnableSearch-111]
_ = x[actSelect-112]
_ = x[actDeselect-113]
_ = x[actUnbind-114]
_ = x[actRebind-115]
_ = x[actBecome-116]
_ = x[actShowHeader-117]
_ = x[actHideHeader-118]
}
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 260, 274, 295, 310, 324, 338, 351, 368, 376, 389, 405, 417, 425, 439, 453, 464, 475, 493, 510, 517, 536, 548, 562, 571, 586, 598, 611, 622, 633, 645, 659, 680, 695, 708, 726, 742, 757, 774, 781, 786, 795, 806, 817, 830, 845, 856, 869, 884, 891, 904, 917, 934, 949, 962, 976, 990, 1006, 1026, 1038, 1061, 1082, 1100, 1124, 1142, 1159, 1169, 1185, 1207, 1220, 1236, 1248, 1262, 1278, 1296, 1316, 1338, 1352, 1367, 1375, 1381, 1395, 1410, 1420, 1436, 1451, 1461, 1469, 1476, 1485, 1498, 1514, 1529, 1538, 1549, 1558, 1567, 1576, 1589, 1602}
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 293, 314, 329, 343, 357, 370, 387, 395, 408, 424, 436, 444, 458, 472, 483, 494, 512, 529, 536, 555, 567, 581, 590, 605, 617, 630, 641, 652, 664, 678, 699, 714, 727, 745, 761, 776, 793, 800, 805, 814, 825, 836, 849, 864, 875, 888, 903, 910, 923, 936, 953, 968, 981, 995, 1009, 1025, 1045, 1057, 1080, 1101, 1123, 1141, 1165, 1183, 1200, 1210, 1226, 1248, 1261, 1277, 1289, 1303, 1319, 1337, 1357, 1379, 1393, 1408, 1416, 1422, 1436, 1451, 1461, 1477, 1492, 1502, 1510, 1517, 1526, 1539, 1555, 1570, 1579, 1590, 1599, 1608, 1617, 1630, 1643}
func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@ -133,6 +133,7 @@ Usage: fzf [options]
[,[no]hidden][,border-BORDER_OPT]
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
--preview-border[=STYLE] Short for --preview-window=border-STYLE
--preview-label=LABEL
--preview-label-pos=N Same as --border-label and --border-label-pos,
but for preview window
@ -521,6 +522,8 @@ type Options struct {
Padding [4]sizeSpec
BorderShape tui.BorderShape
ListBorderShape tui.BorderShape
InputBorderShape tui.BorderShape
InputLabel labelOpts
BorderLabel labelOpts
ListLabel labelOpts
PreviewLabel labelOpts
@ -1168,7 +1171,7 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
}
}
switch components[0] {
case "query", "input":
case "query", "input", "input-fg":
mergeAttr(&theme.Input)
case "disabled":
mergeAttr(&theme.Disabled)
@ -1220,6 +1223,12 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.PreviewLabel)
case "prompt":
mergeAttr(&theme.Prompt)
case "input-bg":
mergeAttr(&theme.InputBg)
case "input-border":
mergeAttr(&theme.InputBorder)
case "input-label":
mergeAttr(&theme.InputLabel)
case "spinner":
mergeAttr(&theme.Spinner)
case "info":
@ -1283,7 +1292,7 @@ const (
func init() {
executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|list-label|preview-label)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`)
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|list-label|preview-label|input-label)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`)
splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@ -1639,14 +1648,16 @@ func isExecuteAction(str string) actionType {
return actRebind
case "preview":
return actPreview
case "change-header":
return actChangeHeader
case "change-list-label":
return actChangeListLabel
case "change-border-label":
return actChangeBorderLabel
case "change-header":
return actChangeHeader
case "change-preview-label":
return actChangePreviewLabel
case "change-input-label":
return actChangeInputLabel
case "change-preview-window":
return actChangePreviewWindow
case "change-preview":
@ -1677,6 +1688,8 @@ func isExecuteAction(str string) actionType {
return actTransformBorderLabel
case "transform-preview-label":
return actTransformPreviewLabel
case "transform-input-label":
return actTransformInputLabel
case "transform-header":
return actTransformHeader
case "transform-prompt":
@ -2453,6 +2466,13 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err := parsePreviewWindow(&opts.Preview, str); err != nil {
return err
}
case "--no-preview-border":
opts.Preview.border = tui.BorderNone
case "--preview-border":
hasArg, arg := optionalNextString(allArgs, &i)
if opts.Preview.border, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--height":
str, err := nextString(allArgs, &i, "height required: [~]HEIGHT[%]")
if err != nil {
@ -2500,6 +2520,27 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err := parseLabelPosition(&opts.ListLabel, pos); err != nil {
return err
}
case "--no-input-border":
opts.InputBorderShape = tui.BorderNone
case "--input-border":
hasArg, arg := optionalNextString(allArgs, &i)
if opts.InputBorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--no-input-label":
opts.InputLabel.label = ""
case "--input-label":
if opts.InputLabel.label, err = nextString(allArgs, &i, "input label required"); err != nil {
return err
}
case "--input-label-pos":
pos, err := nextString(allArgs, &i, "input label position required (positive or negative integer or 'center')")
if err != nil {
return err
}
if err := parseLabelPosition(&opts.InputLabel, pos); err != nil {
return err
}
case "--no-border-label":
opts.BorderLabel.label = ""
case "--border-label":
@ -2637,6 +2678,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.BorderShape, err = parseBorder(value, false); err != nil {
return err
}
} else if match, value := optString(arg, "--preview-border="); match {
if opts.Preview.border, err = parseBorder(value, false); err != nil {
return err
}
} else if match, value := optString(arg, "--list-border="); match {
if opts.ListBorderShape, err = parseBorder(value, false); err != nil {
return err
@ -2647,6 +2692,16 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err := parseLabelPosition(&opts.ListLabel, value); err != nil {
return err
}
} else if match, value := optString(arg, "--input-border="); match {
if opts.InputBorderShape, err = parseBorder(value, false); err != nil {
return err
}
} else if match, value := optString(arg, "--input-label="); match {
opts.InputLabel.label = value
} else if match, value := optString(arg, "--input-label-pos="); match {
if err := parseLabelPosition(&opts.InputLabel, value); err != nil {
return err
}
} else if match, value := optString(arg, "--border-label="); match {
opts.BorderLabel.label = value
} else if match, value := optString(arg, "--border-label-pos="); match {
@ -2922,6 +2977,10 @@ func postProcessOptions(opts *Options) error {
opts.ListBorderShape = tui.BorderNone
}
if opts.InputBorderShape == tui.BorderUndefined {
opts.InputBorderShape = tui.BorderNone
}
if opts.Pointer == nil {
defaultPointer := "▌"
if !opts.Unicode {

View File

@ -164,6 +164,7 @@ type eachLine struct {
}
type itemLine struct {
valid bool
firstLine int
numLines int
cy int
@ -179,11 +180,15 @@ type itemLine struct {
}
func (t *Terminal) markEmptyLine(line int) {
t.prevLines[line] = itemLine{firstLine: line, empty: true}
if t.window != t.inputWindow {
t.prevLines[line] = itemLine{valid: true, firstLine: line, empty: true}
}
}
func (t *Terminal) markOtherLine(line int) {
t.prevLines[line] = itemLine{firstLine: line, other: true}
if t.window != t.inputWindow {
t.prevLines[line] = itemLine{valid: true, firstLine: line, other: true}
}
}
type fitpad struct {
@ -241,6 +246,9 @@ type Terminal struct {
previewLabel labelPrinter
previewLabelLen int
previewLabelOpts labelOpts
inputLabel labelPrinter
inputLabelLen int
inputLabelOpts labelOpts
pointer string
pointerLen int
pointerEmpty string
@ -298,6 +306,7 @@ type Terminal struct {
listenUnsafe bool
borderShape tui.BorderShape
listBorderShape tui.BorderShape
inputBorderShape tui.BorderShape
listLabel labelPrinter
listLabelLen int
listLabelOpts labelOpts
@ -306,6 +315,8 @@ type Terminal struct {
paused bool
border tui.Window
window tui.Window
inputWindow tui.Window
inputBorder tui.Window
wborder tui.Window
pborder tui.Window
pwindow tui.Window
@ -394,6 +405,7 @@ const (
reqReinit
reqFullRedraw
reqResize
reqRedrawInputLabel
reqRedrawListLabel
reqRedrawBorderLabel
reqRedrawPreviewLabel
@ -436,6 +448,7 @@ const (
actCancel
actChangeBorderLabel
actChangeListLabel
actChangeInputLabel
actChangeHeader
actChangeMulti
actChangePreviewLabel
@ -497,6 +510,7 @@ const (
actTransform
actTransformBorderLabel
actTransformListLabel
actTransformInputLabel
actTransformHeader
actTransformPreviewLabel
actTransformPrompt
@ -832,6 +846,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
listenUnsafe: opts.Unsafe,
borderShape: opts.BorderShape,
listBorderShape: opts.ListBorderShape,
inputBorderShape: opts.InputBorderShape,
borderWidth: 1,
listLabel: nil,
listLabelOpts: opts.ListLabel,
@ -839,6 +854,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
borderLabelOpts: opts.BorderLabel,
previewLabel: nil,
previewLabelOpts: opts.PreviewLabel,
inputLabel: nil,
inputLabelOpts: opts.InputLabel,
cleanExit: opts.ClearOnExit,
executor: executor,
paused: opts.Phony,
@ -902,7 +919,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
t.listLabel, t.listLabelLen = t.ansiLabelPrinter(opts.ListLabel.label, &tui.ColListLabel, false)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
if opts.Separator == nil || len(*opts.Separator) > 0 {
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(opts.InputLabel.label, &tui.ColInputLabel, false)
// Disable separator by default if input border is set
if opts.Separator == nil && !t.inputBorderShape.Visible() || opts.Separator != nil && len(*opts.Separator) > 0 {
bar := "─"
if opts.Separator != nil {
bar = *opts.Separator
@ -1120,6 +1140,17 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
return printFn, length
}
// Temporarily switch 'window' so that we can use the existing windows with
// a different window
func (t *Terminal) withInputWindow(f func()) {
prevWindow := t.window
if t.inputWindow != nil {
t.window = t.inputWindow
}
f()
t.window = prevWindow
}
func (t *Terminal) parsePrompt(prompt string) (func(), int) {
var state *ansiState
prompt = firstLine(prompt)
@ -1145,11 +1176,13 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
}
}
output := func() {
line := t.promptLine()
wrap := t.wrap
t.wrap = false
t.withInputWindow(func() {
line := t.promptLine()
t.printHighlighted(
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, line, line, true, nil, nil)
})
t.wrap = wrap
}
_, promptLen := t.processTabs([]rune(trimmed), 0)
@ -1566,6 +1599,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if t.wborder != nil {
t.wborder = nil
}
if t.inputWindow != nil {
t.inputWindow = nil
}
if t.inputBorder != nil {
t.inputBorder = nil
}
if t.pborder != nil {
t.pborder = nil
}
@ -1605,6 +1644,27 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
width -= paddingInt[1] + paddingInt[3]
height -= paddingInt[0] + paddingInt[2]
// Adjust position and size of the list window if input border is set
inputWindowHeight := 0
inputBorderHeight := 0
shift := 0
shrink := 0
hasInputWindow := t.inputBorderShape.Visible()
if hasInputWindow {
inputWindowHeight = 2
if t.noSeparatorLine() {
inputWindowHeight--
}
inputBorderHeight = borderLines(t.inputBorderShape) + inputWindowHeight
if t.layout == layoutReverse {
shift = inputBorderHeight
shrink = inputBorderHeight
} else {
shift = 0
shrink = inputBorderHeight
}
}
hasListBorder := t.listBorderShape.Visible()
innerWidth := width
innerHeight := height
@ -1612,7 +1672,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
innerBorderFn := func(top int, left int, width int, height int) {
if hasListBorder {
t.wborder = t.tui.NewWindow(
top, left, width, height, tui.WindowList, tui.MakeBorderStyle(t.listBorderShape, t.unicode), false)
top+shift, left, width, height-shrink, tui.WindowList, tui.MakeBorderStyle(t.listBorderShape, t.unicode), false)
}
}
if hasListBorder {
@ -1697,12 +1757,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if previewOpts.position == posUp {
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow(
innerMarginInt[0]+pheight, innerMarginInt[3], innerWidth, innerHeight-pheight, tui.WindowList, noBorder, true)
innerMarginInt[0]+pheight+shift, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink, tui.WindowList, noBorder, true)
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
} else {
innerBorderFn(marginInt[0], marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow(
innerMarginInt[0], innerMarginInt[3], innerWidth, innerHeight-pheight, tui.WindowList, noBorder, true)
innerMarginInt[0]+shift, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink, tui.WindowList, noBorder, true)
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
}
case posLeft, posRight:
@ -1741,7 +1801,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
m = 1
}
t.window = t.tui.NewWindow(
innerMarginInt[0], innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight, tui.WindowList, noBorder, true)
innerMarginInt[0]+shift, innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight-shrink, tui.WindowList, noBorder, true)
// Clear characters on the margin
// fzf --bind 'space:preview(seq 100)' --preview-window left,1
@ -1763,7 +1823,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
innerBorderFn(marginInt[0], marginInt[3], width-pwidth, height)
t.window = t.tui.NewWindow(
innerMarginInt[0], innerMarginInt[3], innerWidth-pwidth, innerHeight, tui.WindowList, noBorder, true)
innerMarginInt[0]+shift, innerMarginInt[3], innerWidth-pwidth, innerHeight-shrink, tui.WindowList, noBorder, true)
x := marginInt[3] + width - pwidth
createPreviewWindow(marginInt[0], x, pwidth, height)
}
@ -1801,16 +1861,56 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
innerBorderFn(marginInt[0], marginInt[3], width, height)
t.window = t.tui.NewWindow(
innerMarginInt[0],
innerMarginInt[0]+shift,
innerMarginInt[3],
innerWidth,
innerHeight, tui.WindowList, noBorder, true)
innerHeight-shrink, tui.WindowList, noBorder, true)
}
// Set up input border
if hasInputWindow {
w := t.wborder
if t.wborder == nil {
w = t.window
}
if t.layout == layoutReverse {
t.inputBorder = t.tui.NewWindow(
w.Top()-shrink,
w.Left(),
w.Width(),
util.Min(shrink, screenHeight), tui.WindowInput, tui.MakeBorderStyle(t.inputBorderShape, t.unicode), true)
} else {
t.inputBorder = t.tui.NewWindow(
w.Top()+w.Height(),
w.Left(),
w.Width(),
util.Min(shrink, screenHeight), tui.WindowInput, tui.MakeBorderStyle(t.inputBorderShape, t.unicode), true)
}
top := t.inputBorder.Top()
left := t.inputBorder.Left()
if t.inputBorderShape.HasTop() {
top++
}
if t.inputBorderShape.HasLeft() {
left += t.borderWidth + 1
}
width := t.inputBorder.Width() - borderColumns(t.inputBorderShape, t.borderWidth)
if t.inputBorderShape.HasRight() {
width++
}
t.inputWindow = t.tui.NewWindow(
top,
left,
width,
t.inputBorder.Height()-borderLines(t.inputBorderShape),
tui.WindowInput, noBorder, true)
}
// Print border label
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, false)
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.border, false)
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, false)
}
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
@ -1874,7 +1974,11 @@ func (t *Terminal) truncateQuery() {
}
func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
maxWidth := util.Max(1, t.window.Width()-t.promptLen-1)
w := t.window
if t.inputWindow != nil {
w = t.inputWindow
}
maxWidth := util.Max(1, w.Width()-t.promptLen-1)
_, overflow := t.trimLeft(t.input[:t.cx], maxWidth)
minOffset := int(overflow)
@ -1889,6 +1993,9 @@ func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
}
func (t *Terminal) promptLine() int {
if t.inputWindow != nil {
return 0
}
if t.headerFirst {
max := t.window.Height() - 1
if max <= 0 { // Extremely short terminal
@ -1903,10 +2010,25 @@ func (t *Terminal) promptLine() int {
}
func (t *Terminal) placeCursor() {
if t.inputWindow != nil {
y := t.inputWindow.Height() - 1
if t.layout == layoutReverse {
y = 0
}
t.inputWindow.Move(y, t.promptLen+t.queryLen[0])
return
}
t.move(t.promptLine(), t.promptLen+t.queryLen[0], false)
}
func (t *Terminal) printPrompt() {
w := t.window
if t.inputWindow != nil {
w = t.inputWindow
}
if w.Height() == 0 {
return
}
t.prompt()
before, after := t.updatePromptOffset()
@ -1914,8 +2036,8 @@ func (t *Terminal) printPrompt() {
if t.paused {
color = tui.ColDisabled
}
t.window.CPrint(color, string(before))
t.window.CPrint(color, string(after))
w.CPrint(color, string(before))
w.CPrint(color, string(after))
}
func (t *Terminal) trimMessage(message string, maxWidth int) string {
@ -1927,6 +2049,12 @@ func (t *Terminal) trimMessage(message string, maxWidth int) string {
}
func (t *Terminal) printInfo() {
t.withInputWindow(func() {
t.printInfoImpl()
})
}
func (t *Terminal) printInfoImpl() {
if t.window.Width() <= 1 {
return
}
@ -2124,7 +2252,7 @@ func (t *Terminal) printHeader() {
return
}
max := t.window.Height()
if t.headerFirst {
if t.inputWindow == nil && t.headerFirst {
max--
if !t.noSeparatorLine() {
max--
@ -2144,7 +2272,7 @@ func (t *Terminal) printHeader() {
if needReverse && idx < len(t.header0) {
line = len(t.header0) - idx - 1
}
if !t.headerFirst {
if t.inputWindow == nil && !t.headerFirst {
line++
if !t.noSeparatorLine() {
line++
@ -2189,10 +2317,7 @@ func (t *Terminal) printList() {
count := t.merger.Length() - t.offset
// Start line
startLine := 2 + t.visibleHeaderLines()
if t.noSeparatorLine() {
startLine--
}
startLine := t.promptLines() + t.visibleHeaderLines()
maxy += startLine
barRange := [2]int{startLine + barStart, startLine + barStart + barLength}
@ -2233,10 +2358,10 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
// Avoid unnecessary redraw
numLines, _ := t.numItemLines(item, maxLine-line+1)
newLine := itemLine{firstLine: line, numLines: numLines, cy: index + t.offset, current: current, selected: selected, label: label,
newLine := itemLine{valid: true, firstLine: line, numLines: numLines, cy: index + t.offset, current: current, selected: selected, label: label,
result: result, queryLen: len(t.input), width: 0, hasBar: line >= barRange[0] && line < barRange[1]}
prevLine := t.prevLines[line]
forceRedraw := prevLine.other || prevLine.firstLine != newLine.firstLine
forceRedraw := !prevLine.valid || prevLine.other || prevLine.firstLine != newLine.firstLine
printBar := func(lineNum int, forceRedraw bool) bool {
return t.printBar(lineNum, forceRedraw, barRange)
}
@ -3962,6 +4087,8 @@ func (t *Terminal) Loop() error {
if t.hasPreviewer() {
t.previewBox.Set(reqPreviewReady, nil)
}
case reqRedrawInputLabel:
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
case reqRedrawListLabel:
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
case reqRedrawBorderLabel:
@ -4345,6 +4472,12 @@ func (t *Terminal) Loop() error {
} else {
req(reqHeader)
}
case actChangeInputLabel:
t.inputLabelOpts.label = a.a
if t.inputBorder != nil {
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(a.a, &tui.ColInputLabel, false)
req(reqRedrawInputLabel)
}
case actChangeListLabel:
t.listLabelOpts.label = a.a
if t.wborder != nil {
@ -4368,6 +4501,13 @@ func (t *Terminal) Loop() error {
if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil {
return doActions(actions)
}
case actTransformInputLabel:
label := t.executeCommand(a.a, false, true, true, true, "")
t.inputLabelOpts.label = label
if t.inputBorder != nil {
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(label, &tui.ColInputLabel, false)
req(reqRedrawInputLabel)
}
case actTransformListLabel:
label := t.executeCommand(a.a, false, true, true, true, "")
t.listLabelOpts.label = label
@ -4927,6 +5067,21 @@ func (t *Terminal) Loop() error {
break
}
// Inside the input window
if t.inputWindow != nil && t.inputWindow.Enclose(my, mx) {
mx -= t.inputWindow.Left()
my -= t.inputWindow.Top()
y := t.inputWindow.Height() - 1
if t.layout == layoutReverse {
y = 0
}
mxCons := util.Constrain(mx-t.promptLen, 0, len(t.input))
if my == y && mxCons >= 0 {
t.cx = mxCons + t.xoffset
}
break
}
// Ignored
if !t.window.Enclose(my, mx) && !barDragging {
break
@ -4935,10 +5090,7 @@ func (t *Terminal) Loop() error {
// Translate coordinates
mx -= t.window.Left()
my -= t.window.Top()
min := 2 + t.visibleHeaderLines()
if t.noSeparatorLine() {
min--
}
min := t.promptLines() + t.visibleHeaderLines()
h := t.window.Height()
switch t.layout {
case layoutDefault:
@ -4990,7 +5142,7 @@ func (t *Terminal) Loop() error {
if me.Down {
mxCons := util.Constrain(mx-t.promptLen, 0, len(t.input))
if my == t.promptLine() && mxCons >= 0 {
if t.inputWindow == nil && my == t.promptLine() && mxCons >= 0 {
// Prompt
t.cx = mxCons + t.xoffset
} else if my >= min {
@ -5011,7 +5163,7 @@ func (t *Terminal) Loop() error {
// Header
numLines := t.visibleHeaderLines()
lineOffset := 0
if !t.headerFirst {
if t.inputWindow == nil && !t.headerFirst {
// offset for info line
if t.noSeparatorLine() {
lineOffset = 1
@ -5339,11 +5491,20 @@ func (t *Terminal) vset(o int) bool {
return t.cy == o
}
func (t *Terminal) maxItems() int {
max := t.window.Height() - 2 - t.visibleHeaderLines()
if t.noSeparatorLine() {
max++
// Number of prompt lines in the list window
func (t *Terminal) promptLines() int {
if t.inputWindow != nil {
return 0
}
if t.noSeparatorLine() {
return 1
}
return 2
}
// Number of item lines in the list window
func (t *Terminal) maxItems() int {
max := t.window.Height() - t.visibleHeaderLines() - t.promptLines()
return util.Max(max, 0)
}

View File

@ -780,6 +780,8 @@ func (r *LightRenderer) MaxY() int {
}
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
width = util.Max(0, width)
height = util.Max(0, height)
w := &LightWindow{
renderer: r,
colored: r.theme.Colored,
@ -799,6 +801,9 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, wind
case WindowList:
w.fg = r.theme.ListFg.Color
w.bg = r.theme.ListBg.Color
case WindowInput:
w.fg = r.theme.Input.Color
w.bg = r.theme.InputBg.Color
case WindowPreview:
w.fg = r.theme.PreviewFg.Color
w.bg = r.theme.PreviewBg.Color
@ -820,6 +825,9 @@ func (w *LightWindow) DrawHBorder() {
}
func (w *LightWindow) drawBorder(onlyHorizontal bool) {
if w.height == 0 {
return
}
switch w.border.shape {
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble:
w.drawBorderAround(onlyHorizontal)
@ -852,6 +860,8 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
switch w.windowType {
case WindowList:
color = ColListBorder
case WindowInput:
color = ColInputBorder
case WindowPreview:
color = ColPreviewBorder
}
@ -873,6 +883,8 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
switch w.windowType {
case WindowList:
color = ColListBorder
case WindowInput:
color = ColInputBorder
case WindowPreview:
color = ColPreviewBorder
}
@ -896,6 +908,8 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
switch w.windowType {
case WindowList:
color = ColListBorder
case WindowInput:
color = ColInputBorder
case WindowPreview:
color = ColPreviewBorder
}

View File

@ -551,10 +551,14 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
width = util.Max(0, width)
height = util.Max(0, height)
normal := ColBorder
switch windowType {
case WindowList:
normal = ColListBorder
case WindowInput:
normal = ColInputBorder
case WindowPreview:
normal = ColPreviewBorder
}
@ -768,6 +772,9 @@ func (w *TcellWindow) DrawHBorder() {
}
func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
if w.height == 0 {
return
}
shape := w.borderStyle.shape
if shape == BorderNone {
return
@ -785,6 +792,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
style = ColBorder.style()
case WindowList:
style = ColListBorder.style()
case WindowInput:
style = ColInputBorder.style()
case WindowPreview:
style = ColPreviewBorder.style()
}

View File

@ -313,6 +313,9 @@ type ColorTheme struct {
DarkBg ColorAttr
Gutter ColorAttr
Prompt ColorAttr
InputBg ColorAttr
InputBorder ColorAttr
InputLabel ColorAttr
Match ColorAttr
Current ColorAttr
CurrentMatch ColorAttr
@ -539,6 +542,7 @@ const (
WindowBase WindowType = iota
WindowList
WindowPreview
WindowInput
)
type Renderer interface {
@ -646,6 +650,8 @@ var (
ColPreviewSpinner ColorPair
ColListBorder ColorPair
ColListLabel ColorPair
ColInputBorder ColorPair
ColInputLabel ColorPair
)
func EmptyTheme() *ColorTheme {
@ -682,6 +688,9 @@ func EmptyTheme() *ColorTheme {
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
}
}
@ -719,6 +728,9 @@ func NoColorTheme() *ColorTheme {
ListBorder: ColorAttr{colDefault, AttrUndefined},
Separator: ColorAttr{colDefault, AttrUndefined},
Scrollbar: ColorAttr{colDefault, AttrUndefined},
InputBg: ColorAttr{colDefault, AttrUndefined},
InputBorder: ColorAttr{colDefault, AttrUndefined},
InputLabel: ColorAttr{colDefault, AttrUndefined},
}
}
@ -756,6 +768,9 @@ func init() {
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
}
Dark256 = &ColorTheme{
Colored: true,
@ -790,6 +805,9 @@ func init() {
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
}
Light256 = &ColorTheme{
Colored: true,
@ -824,6 +842,9 @@ func init() {
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
}
}
@ -875,6 +896,9 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.Separator = o(theme.ListBorder, theme.Separator)
theme.Scrollbar = o(theme.ListBorder, theme.Scrollbar)
theme.PreviewScrollbar = o(theme.PreviewBorder, theme.PreviewScrollbar)
theme.InputBg = o(theme.Bg, o(theme.ListBg, theme.InputBg))
theme.InputBorder = o(theme.Border, theme.InputBorder)
theme.InputLabel = o(theme.BorderLabel, theme.InputLabel)
initPalette(theme)
}
@ -889,10 +913,10 @@ func initPalette(theme *ColorTheme) {
blank := theme.ListFg
blank.Attr = AttrRegular
ColPrompt = pair(theme.Prompt, theme.ListBg)
ColPrompt = pair(theme.Prompt, theme.InputBg)
ColNormal = pair(theme.ListFg, theme.ListBg)
ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
ColInput = pair(theme.Input, theme.ListBg)
ColInput = pair(theme.Input, theme.InputBg)
ColDisabled = pair(theme.Disabled, theme.ListBg)
ColMatch = pair(theme.Match, theme.ListBg)
ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg)
@ -909,10 +933,10 @@ func initPalette(theme *ColorTheme) {
ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
ColCurrentMarker = pair(theme.Marker, theme.DarkBg)
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
ColSpinner = pair(theme.Spinner, theme.ListBg)
ColInfo = pair(theme.Info, theme.ListBg)
ColSpinner = pair(theme.Spinner, theme.InputBg)
ColInfo = pair(theme.Info, theme.InputBg)
ColHeader = pair(theme.Header, theme.ListBg)
ColSeparator = pair(theme.Separator, theme.ListBg)
ColSeparator = pair(theme.Separator, theme.InputBg)
ColScrollbar = pair(theme.Scrollbar, theme.ListBg)
ColBorder = pair(theme.Border, theme.Bg)
ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
@ -923,6 +947,8 @@ func initPalette(theme *ColorTheme) {
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
ColListLabel = pair(theme.ListLabel, theme.ListBg)
ColListBorder = pair(theme.ListBorder, theme.ListBg)
ColInputBorder = pair(theme.InputBorder, theme.InputBg)
ColInputLabel = pair(theme.InputLabel, theme.InputBg)
}
func runeWidth(r rune) int {

View File

@ -2646,7 +2646,7 @@ class TestGoFZF < TestBase
end
def test_change_preview_window
tmux.send_keys "seq 1000 | #{FZF} --preview 'echo [[{}]]' --preview-window border-none --bind '" \
tmux.send_keys "seq 1000 | #{FZF} --preview 'echo [[{}]]' --no-preview-border --bind '" \
'a:change-preview(echo __{}__),' \
'b:change-preview-window(down)+change-preview(echo =={}==)+change-preview-window(up),' \
'c:change-preview(),d:change-preview-window(hidden),' \
@ -3449,6 +3449,80 @@ class TestGoFZF < TestBase
BLOCK
tmux.until { assert_block(block, _1) }
end
def test_list_border_and_label
tmux.send_keys %(seq 100 | #{FZF} --border rounded --list-border double --list-label list --list-label-pos 2:bottom --header-lines 3 --query 1 --padding 1,2), :Enter
block = <<~BLOCK
11
> 10
3
2
1
19/97
> 1
list
BLOCK
tmux.until { assert_block(block, _1) }
end
def test_input_border_and_label
tmux.send_keys %(seq 100 | #{FZF} --border rounded --input-border bold --input-label input --input-label-pos 2 --header-lines 3 --query 1 --padding 1,2), :Enter
block = <<~BLOCK
11
> 10
3
2
1
input
19/97
> 1
BLOCK
tmux.until { assert_block(block, _1) }
end
def test_list_input_border_and_label
tmux.send_keys %(
seq 100 | #{FZF} --border rounded --list-border double --input-border bold --list-label-pos 2:bottom --input-label-pos 2 --header-lines 3 --query 1 --padding 1,2 \
--bind 'start:transform-input-label(echo INPUT)+transform-list-label(echo LIST)' \
--bind 'space:change-input-label( input )+change-list-label( list )'
).strip, :Enter
block = <<~BLOCK
11
> 10
3
2
1
LIST
INPUT
19/97
> 1
BLOCK
tmux.until { assert_block(block, _1) }
tmux.send_keys :Space
block = <<~BLOCK
11
> 10
3
2
1
list
input
19/97
> 1
BLOCK
tmux.until { assert_block(block, _1) }
end
end
module TestShell