Add --list-border for additional border around the list section

Close #4148
This commit is contained in:
Junegunn Choi 2024-12-31 17:03:18 +09:00
parent b8d2b0df7e
commit 9a2b7f559c
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
11 changed files with 620 additions and 329 deletions

View File

@ -1,6 +1,22 @@
CHANGELOG CHANGELOG
========= =========
0.58.0
------
- Additional border and label for the list section
- Options
- `--list-border[=STYLE]`
- `--list-label=LABEL`
- `--list-label-pos=COL[:bottom]`
- Colors
- `list-fg`
- `list-bg`
- `list-border`
- `list-label`
- Actions
- `change-list-label`
- `transform-list-label`
0.57.0 0.57.0
------ ------
- You can now resize the preview window by dragging the border - You can now resize the preview window by dragging the border

View File

@ -552,33 +552,37 @@ color mappings.
\fBbw \fRNo colors (equivalent to \fB\-\-no\-color\fR) \fBbw \fRNo colors (equivalent to \fB\-\-no\-color\fR)
.B COLOR NAMES: .B COLOR NAMES:
\fBfg \fRText \fBfg \fRText
\fBselected\-fg \fRSelected line text \fBlist\-fg \fRText in the list section
\fBpreview\-fg \fRPreview window text \fBselected\-fg \fRSelected line text
\fBbg \fRBackground \fBpreview\-fg \fRPreview window text
\fBselected\-bg \fRSelected line background \fBbg \fRBackground
\fBpreview\-bg \fRPreview window background \fBlist\-bg \fRBackground in the list section
\fBhl \fRHighlighted substrings \fBselected\-bg \fRSelected line background
\fBselected\-hl \fRHighlighted substrings in the selected line \fBpreview\-bg \fRPreview window background
\fBcurrent\-fg (fg+) \fRText (current line) \fBhl \fRHighlighted substrings
\fBcurrent\-bg (bg+) \fRBackground (current line) \fBselected\-hl \fRHighlighted substrings in the selected line
\fBgutter \fRGutter on the left \fBcurrent\-fg (fg+) \fRText (current line)
\fBcurrent\-hl (hl+) \fRHighlighted substrings (current line) \fBcurrent\-bg (bg+) \fRBackground (current line)
\fBquery \fRQuery string \fBgutter \fRGutter on the left
\fBdisabled \fRQuery string when search is disabled (\fB\-\-disabled\fR) \fBcurrent\-hl (hl+) \fRHighlighted substrings (current line)
\fBinfo \fRInfo line (match counters) \fBquery \fRQuery string
\fBborder \fRBorder around the window (\fB\-\-border\fR and \fB\-\-preview\fR) \fBdisabled \fRQuery string when search is disabled (\fB\-\-disabled\fR)
\fBscrollbar \fRScrollbar \fBinfo \fRInfo line (match counters)
\fBpreview\-border \fRBorder around the preview window (\fB\-\-preview\fR) \fBborder \fRBorder around the window (\fB\-\-border\fR and \fB\-\-preview\fR)
\fBpreview\-scrollbar \fRScrollbar \fBlist\-border \fRBorder around the list section (\fB\-\-list\-border\fR)
\fBseparator \fRHorizontal separator on info line \fBscrollbar \fRScrollbar
\fBlabel \fRBorder label (\fB\-\-border\-label\fR and \fB\-\-preview\-label\fR) \fBseparator \fRHorizontal separator on info line
\fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR) \fBpreview\-border \fRBorder around the preview window (\fB\-\-preview\fR)
\fBprompt \fRPrompt \fBpreview\-scrollbar \fRScrollbar
\fBpointer \fRPointer to the current line \fBlabel \fRBorder label (\fB\-\-border\-label\fR, \fB\-\-list\-label\fR, and \fB\-\-preview\-label\fR)
\fBmarker \fRMulti\-select marker \fBlist\-label \fRBorder label of the list section (\fB\-\-list\-label\fR)
\fBspinner \fRStreaming input indicator \fBpreview\-label \fRBorder label of the preview window (\fB\-\-preview\-label\fR)
\fBheader \fRHeader \fBprompt \fRPrompt
\fBpointer \fRPointer to the current line
\fBmarker \fRMulti\-select marker
\fBspinner \fRStreaming input indicator
\fBheader \fRHeader
.B ANSI COLORS: .B ANSI COLORS:
\fB\-1 \fRDefault terminal foreground/background color \fB\-1 \fRDefault terminal foreground/background color
@ -1440,6 +1444,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) \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\-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\-header(...)\fR (change header to the given string; doesn't affect \fB\-\-header\-lines\fR)
\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 no limit)
\fBchange\-multi(...)\fR (enable multi-select mode with a limit or disable it with 0) \fBchange\-multi(...)\fR (enable multi-select mode with a limit or disable it with 0)
\fBchange\-preview(...)\fR (change \fB\-\-preview\fR option) \fBchange\-preview(...)\fR (change \fB\-\-preview\fR option)
@ -1524,6 +1529,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(...)\fR (transform states using the output of an external command)
\fBtransform\-border\-label(...)\fR (transform border label using an external command) \fBtransform\-border\-label(...)\fR (transform border label using an external command)
\fBtransform\-header(...)\fR (transform header using an external command) \fBtransform\-header(...)\fR (transform header 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\-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)

View File

@ -25,109 +25,111 @@ func _() {
_ = x[actBackwardWord-14] _ = x[actBackwardWord-14]
_ = x[actCancel-15] _ = x[actCancel-15]
_ = x[actChangeBorderLabel-16] _ = x[actChangeBorderLabel-16]
_ = x[actChangeHeader-17] _ = x[actChangeListLabel-17]
_ = x[actChangeMulti-18] _ = x[actChangeHeader-18]
_ = x[actChangePreviewLabel-19] _ = x[actChangeMulti-19]
_ = x[actChangePrompt-20] _ = x[actChangePreviewLabel-20]
_ = x[actChangeQuery-21] _ = x[actChangePrompt-21]
_ = x[actClearScreen-22] _ = x[actChangeQuery-22]
_ = x[actClearQuery-23] _ = x[actClearScreen-23]
_ = x[actClearSelection-24] _ = x[actClearQuery-24]
_ = x[actClose-25] _ = x[actClearSelection-25]
_ = x[actDeleteChar-26] _ = x[actClose-26]
_ = x[actDeleteCharEof-27] _ = x[actDeleteChar-27]
_ = x[actEndOfLine-28] _ = x[actDeleteCharEof-28]
_ = x[actFatal-29] _ = x[actEndOfLine-29]
_ = x[actForwardChar-30] _ = x[actFatal-30]
_ = x[actForwardWord-31] _ = x[actForwardChar-31]
_ = x[actKillLine-32] _ = x[actForwardWord-32]
_ = x[actKillWord-33] _ = x[actKillLine-33]
_ = x[actUnixLineDiscard-34] _ = x[actKillWord-34]
_ = x[actUnixWordRubout-35] _ = x[actUnixLineDiscard-35]
_ = x[actYank-36] _ = x[actUnixWordRubout-36]
_ = x[actBackwardKillWord-37] _ = x[actYank-37]
_ = x[actSelectAll-38] _ = x[actBackwardKillWord-38]
_ = x[actDeselectAll-39] _ = x[actSelectAll-39]
_ = x[actToggle-40] _ = x[actDeselectAll-40]
_ = x[actToggleSearch-41] _ = x[actToggle-41]
_ = x[actToggleAll-42] _ = x[actToggleSearch-42]
_ = x[actToggleDown-43] _ = x[actToggleAll-43]
_ = x[actToggleUp-44] _ = x[actToggleDown-44]
_ = x[actToggleIn-45] _ = x[actToggleUp-45]
_ = x[actToggleOut-46] _ = x[actToggleIn-46]
_ = x[actToggleTrack-47] _ = x[actToggleOut-47]
_ = x[actToggleTrackCurrent-48] _ = x[actToggleTrack-48]
_ = x[actToggleHeader-49] _ = x[actToggleTrackCurrent-49]
_ = x[actToggleWrap-50] _ = x[actToggleHeader-50]
_ = x[actToggleMultiLine-51] _ = x[actToggleWrap-51]
_ = x[actToggleHscroll-52] _ = x[actToggleMultiLine-52]
_ = x[actTrackCurrent-53] _ = x[actToggleHscroll-53]
_ = x[actUntrackCurrent-54] _ = x[actTrackCurrent-54]
_ = x[actDown-55] _ = x[actUntrackCurrent-55]
_ = x[actUp-56] _ = x[actDown-56]
_ = x[actPageUp-57] _ = x[actUp-57]
_ = x[actPageDown-58] _ = x[actPageUp-58]
_ = x[actPosition-59] _ = x[actPageDown-59]
_ = x[actHalfPageUp-60] _ = x[actPosition-60]
_ = x[actHalfPageDown-61] _ = x[actHalfPageUp-61]
_ = x[actOffsetUp-62] _ = x[actHalfPageDown-62]
_ = x[actOffsetDown-63] _ = x[actOffsetUp-63]
_ = x[actOffsetMiddle-64] _ = x[actOffsetDown-64]
_ = x[actJump-65] _ = x[actOffsetMiddle-65]
_ = x[actJumpAccept-66] _ = x[actJump-66]
_ = x[actPrintQuery-67] _ = x[actJumpAccept-67]
_ = x[actRefreshPreview-68] _ = x[actPrintQuery-68]
_ = x[actReplaceQuery-69] _ = x[actRefreshPreview-69]
_ = x[actToggleSort-70] _ = x[actReplaceQuery-70]
_ = x[actShowPreview-71] _ = x[actToggleSort-71]
_ = x[actHidePreview-72] _ = x[actShowPreview-72]
_ = x[actTogglePreview-73] _ = x[actHidePreview-73]
_ = x[actTogglePreviewWrap-74] _ = x[actTogglePreview-74]
_ = x[actTransform-75] _ = x[actTogglePreviewWrap-75]
_ = x[actTransformBorderLabel-76] _ = x[actTransform-76]
_ = x[actTransformHeader-77] _ = x[actTransformBorderLabel-77]
_ = x[actTransformPreviewLabel-78] _ = x[actTransformListLabel-78]
_ = x[actTransformPrompt-79] _ = x[actTransformHeader-79]
_ = x[actTransformQuery-80] _ = x[actTransformPreviewLabel-80]
_ = x[actPreview-81] _ = x[actTransformPrompt-81]
_ = x[actChangePreview-82] _ = x[actTransformQuery-82]
_ = x[actChangePreviewWindow-83] _ = x[actPreview-83]
_ = x[actPreviewTop-84] _ = x[actChangePreview-84]
_ = x[actPreviewBottom-85] _ = x[actChangePreviewWindow-85]
_ = x[actPreviewUp-86] _ = x[actPreviewTop-86]
_ = x[actPreviewDown-87] _ = x[actPreviewBottom-87]
_ = x[actPreviewPageUp-88] _ = x[actPreviewUp-88]
_ = x[actPreviewPageDown-89] _ = x[actPreviewDown-89]
_ = x[actPreviewHalfPageUp-90] _ = x[actPreviewPageUp-90]
_ = x[actPreviewHalfPageDown-91] _ = x[actPreviewPageDown-91]
_ = x[actPrevHistory-92] _ = x[actPreviewHalfPageUp-92]
_ = x[actPrevSelected-93] _ = x[actPreviewHalfPageDown-93]
_ = x[actPrint-94] _ = x[actPrevHistory-94]
_ = x[actPut-95] _ = x[actPrevSelected-95]
_ = x[actNextHistory-96] _ = x[actPrint-96]
_ = x[actNextSelected-97] _ = x[actPut-97]
_ = x[actExecute-98] _ = x[actNextHistory-98]
_ = x[actExecuteSilent-99] _ = x[actNextSelected-99]
_ = x[actExecuteMulti-100] _ = x[actExecute-100]
_ = x[actSigStop-101] _ = x[actExecuteSilent-101]
_ = x[actFirst-102] _ = x[actExecuteMulti-102]
_ = x[actLast-103] _ = x[actSigStop-103]
_ = x[actReload-104] _ = x[actFirst-104]
_ = x[actReloadSync-105] _ = x[actLast-105]
_ = x[actDisableSearch-106] _ = x[actReload-106]
_ = x[actEnableSearch-107] _ = x[actReloadSync-107]
_ = x[actSelect-108] _ = x[actDisableSearch-108]
_ = x[actDeselect-109] _ = x[actEnableSearch-109]
_ = x[actUnbind-110] _ = x[actSelect-110]
_ = x[actRebind-111] _ = x[actDeselect-111]
_ = x[actBecome-112] _ = x[actUnbind-112]
_ = x[actShowHeader-113] _ = x[actRebind-113]
_ = x[actHideHeader-114] _ = x[actBecome-114]
_ = x[actShowHeader-115]
_ = x[actHideHeader-116]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader" const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 407, 421, 435, 446, 457, 475, 492, 499, 518, 530, 544, 553, 568, 580, 593, 604, 615, 627, 641, 662, 677, 690, 708, 724, 739, 756, 763, 768, 777, 788, 799, 812, 827, 838, 851, 866, 873, 886, 899, 916, 931, 944, 958, 972, 988, 1008, 1020, 1043, 1061, 1085, 1103, 1120, 1130, 1146, 1168, 1181, 1197, 1209, 1223, 1239, 1257, 1277, 1299, 1313, 1328, 1336, 1342, 1356, 1371, 1381, 1397, 1412, 1422, 1430, 1437, 1446, 1459, 1475, 1490, 1499, 1510, 1519, 1528, 1537, 1550, 1563} 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}
func (i actionType) String() string { func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) { if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@ -87,6 +87,14 @@ Usage: fzf [options]
[POSITIVE_INTEGER: columns from left| [POSITIVE_INTEGER: columns from left|
NEGATIVE_INTEGER: columns from right][:bottom] NEGATIVE_INTEGER: columns from right][:bottom]
(default: 0 or center) (default: 0 or center)
--list-border[=STYLE] Draw border around the list section
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
top|bottom|left|right|none] (default: none)
--list-label=LABEL Label to print on the list border
--list-label-pos=COL Position of the list label
[POSITIVE_INTEGER: columns from left|
NEGATIVE_INTEGER: columns from right][:bottom]
(default: 0 or center)
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L) --margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L) --padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--info=STYLE Finder info style --info=STYLE Finder info style
@ -436,100 +444,102 @@ type walkerOpts struct {
// Options stores the values of command-line options // Options stores the values of command-line options
type Options struct { type Options struct {
Input chan string Input chan string
Output chan string Output chan string
NoWinpty bool NoWinpty bool
Tmux *tmuxOptions Tmux *tmuxOptions
ForceTtyIn bool ForceTtyIn bool
ProxyScript string ProxyScript string
Bash bool Bash bool
Zsh bool Zsh bool
Fish bool Fish bool
Man bool Man bool
Fuzzy bool Fuzzy bool
FuzzyAlgo algo.Algo FuzzyAlgo algo.Algo
Scheme string Scheme string
Extended bool Extended bool
Phony bool Phony bool
Case Case Case Case
Normalize bool Normalize bool
Nth []Range Nth []Range
WithNth []Range WithNth []Range
Delimiter Delimiter Delimiter Delimiter
Sort int Sort int
Track trackOption Track trackOption
Tac bool Tac bool
Tail int Tail int
Criteria []criterion Criteria []criterion
Multi int Multi int
Ansi bool Ansi bool
Mouse bool Mouse bool
Theme *tui.ColorTheme Theme *tui.ColorTheme
Black bool Black bool
Bold bool Bold bool
Height heightSpec Height heightSpec
MinHeight int MinHeight int
Layout layoutType Layout layoutType
Cycle bool Cycle bool
Wrap bool Wrap bool
WrapSign *string WrapSign *string
MultiLine bool MultiLine bool
CursorLine bool CursorLine bool
KeepRight bool KeepRight bool
Hscroll bool Hscroll bool
HscrollOff int HscrollOff int
ScrollOff int ScrollOff int
FileWord bool FileWord bool
InfoStyle infoStyle InfoStyle infoStyle
InfoPrefix string InfoPrefix string
InfoCommand string InfoCommand string
Separator *string Separator *string
JumpLabels string JumpLabels string
Prompt string Prompt string
Pointer *string Pointer *string
Marker *string Marker *string
MarkerMulti *[3]string MarkerMulti *[3]string
Query string Query string
Select1 bool Select1 bool
Exit0 bool Exit0 bool
Filter *string Filter *string
ToggleSort bool ToggleSort bool
Expect map[tui.Event]string Expect map[tui.Event]string
Keymap map[tui.Event][]*action Keymap map[tui.Event][]*action
Preview previewOpts Preview previewOpts
PrintQuery bool PrintQuery bool
ReadZero bool ReadZero bool
Printer func(string) Printer func(string)
PrintSep string PrintSep string
Sync bool Sync bool
History *History History *History
Header []string Header []string
HeaderLines int HeaderLines int
HeaderFirst bool HeaderFirst bool
Gap int Gap int
Ellipsis *string Ellipsis *string
Scrollbar *string Scrollbar *string
Margin [4]sizeSpec Margin [4]sizeSpec
Padding [4]sizeSpec Padding [4]sizeSpec
BorderShape tui.BorderShape BorderShape tui.BorderShape
BorderLabel labelOpts ListBorderShape tui.BorderShape
PreviewLabel labelOpts BorderLabel labelOpts
Unicode bool ListLabel labelOpts
Ambidouble bool PreviewLabel labelOpts
Tabstop int Unicode bool
WithShell string Ambidouble bool
ListenAddr *listenAddress Tabstop int
Unsafe bool WithShell string
ClearOnExit bool ListenAddr *listenAddress
WalkerOpts walkerOpts Unsafe bool
WalkerRoot []string ClearOnExit bool
WalkerSkip []string WalkerOpts walkerOpts
Version bool WalkerRoot []string
Help bool WalkerSkip []string
CPUProfile string Version bool
MEMProfile string Help bool
BlockProfile string CPUProfile string
MutexProfile string MEMProfile string
BlockProfile string
MutexProfile string
} }
func filterNonEmpty(input []string) []string { func filterNonEmpty(input []string) []string {
@ -1166,6 +1176,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.Fg) mergeAttr(&theme.Fg)
case "bg": case "bg":
mergeAttr(&theme.Bg) mergeAttr(&theme.Bg)
case "list-fg":
mergeAttr(&theme.ListFg)
case "list-bg":
mergeAttr(&theme.ListBg)
case "preview-fg": case "preview-fg":
mergeAttr(&theme.PreviewFg) mergeAttr(&theme.PreviewFg)
case "preview-bg": case "preview-bg":
@ -1198,6 +1212,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.PreviewScrollbar) mergeAttr(&theme.PreviewScrollbar)
case "label": case "label":
mergeAttr(&theme.BorderLabel) mergeAttr(&theme.BorderLabel)
case "list-label":
mergeAttr(&theme.ListLabel)
case "list-border":
mergeAttr(&theme.ListBorder)
case "preview-label": case "preview-label":
mergeAttr(&theme.PreviewLabel) mergeAttr(&theme.PreviewLabel)
case "prompt": case "prompt":
@ -1265,7 +1283,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-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)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@ -1621,6 +1639,8 @@ func isExecuteAction(str string) actionType {
return actRebind return actRebind
case "preview": case "preview":
return actPreview return actPreview
case "change-list-label":
return actChangeListLabel
case "change-border-label": case "change-border-label":
return actChangeBorderLabel return actChangeBorderLabel
case "change-header": case "change-header":
@ -1651,6 +1671,8 @@ func isExecuteAction(str string) actionType {
return actPut return actPut
case "transform": case "transform":
return actTransform return actTransform
case "transform-list-label":
return actTransformListLabel
case "transform-border-label": case "transform-border-label":
return actTransformBorderLabel return actTransformBorderLabel
case "transform-preview-label": case "transform-preview-label":
@ -2456,6 +2478,28 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil { if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err return err
} }
case "--list-border":
hasArg, arg := optionalNextString(allArgs, &i)
if opts.ListBorderShape, err = parseBorder(arg, !hasArg); err != nil {
return err
}
case "--no-list-border":
opts.ListBorderShape = tui.BorderNone
case "--no-list-label":
opts.ListLabel.label = ""
case "--list-label":
opts.ListLabel.label, err = nextString(allArgs, &i, "label required")
if err != nil {
return err
}
case "--list-label-pos":
pos, err := nextString(allArgs, &i, "label position required (positive or negative integer or 'center')")
if err != nil {
return err
}
if err := parseLabelPosition(&opts.ListLabel, pos); err != nil {
return err
}
case "--no-border-label": case "--no-border-label":
opts.BorderLabel.label = "" opts.BorderLabel.label = ""
case "--border-label": case "--border-label":
@ -2593,6 +2637,12 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.BorderShape, err = parseBorder(value, false); err != nil { if opts.BorderShape, err = parseBorder(value, false); err != nil {
return err return err
} }
} else if match, value := optString(arg, "--list-label="); match {
opts.ListLabel.label = value
} else if match, value := optString(arg, "--list-label-pos="); match {
if err := parseLabelPosition(&opts.ListLabel, value); err != nil {
return err
}
} else if match, value := optString(arg, "--border-label="); match { } else if match, value := optString(arg, "--border-label="); match {
opts.BorderLabel.label = value opts.BorderLabel.label = value
} else if match, value := optString(arg, "--border-label-pos="); match { } else if match, value := optString(arg, "--border-label-pos="); match {
@ -2864,6 +2914,10 @@ func postProcessOptions(opts *Options) error {
opts.BorderShape = tui.BorderNone opts.BorderShape = tui.BorderNone
} }
if opts.ListBorderShape == tui.BorderUndefined {
opts.ListBorderShape = tui.BorderNone
}
if opts.Pointer == nil { if opts.Pointer == nil {
defaultPointer := "▌" defaultPointer := "▌"
if !opts.Unicode { if !opts.Unicode {

View File

@ -297,11 +297,16 @@ type Terminal struct {
listener net.Listener listener net.Listener
listenUnsafe bool listenUnsafe bool
borderShape tui.BorderShape borderShape tui.BorderShape
listBorderShape tui.BorderShape
listLabel labelPrinter
listLabelLen int
listLabelOpts labelOpts
cleanExit bool cleanExit bool
executor *util.Executor executor *util.Executor
paused bool paused bool
border tui.Window border tui.Window
window tui.Window window tui.Window
wborder tui.Window
pborder tui.Window pborder tui.Window
pwindow tui.Window pwindow tui.Window
borderWidth int borderWidth int
@ -389,6 +394,7 @@ const (
reqReinit reqReinit
reqFullRedraw reqFullRedraw
reqResize reqResize
reqRedrawListLabel
reqRedrawBorderLabel reqRedrawBorderLabel
reqRedrawPreviewLabel reqRedrawPreviewLabel
reqClose reqClose
@ -429,6 +435,7 @@ const (
actBackwardWord actBackwardWord
actCancel actCancel
actChangeBorderLabel actChangeBorderLabel
actChangeListLabel
actChangeHeader actChangeHeader
actChangeMulti actChangeMulti
actChangePreviewLabel actChangePreviewLabel
@ -489,6 +496,7 @@ const (
actTogglePreviewWrap actTogglePreviewWrap
actTransform actTransform
actTransformBorderLabel actTransformBorderLabel
actTransformListLabel
actTransformHeader actTransformHeader
actTransformPreviewLabel actTransformPreviewLabel
actTransformPrompt actTransformPrompt
@ -823,7 +831,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
listenAddr: opts.ListenAddr, listenAddr: opts.ListenAddr,
listenUnsafe: opts.Unsafe, listenUnsafe: opts.Unsafe,
borderShape: opts.BorderShape, borderShape: opts.BorderShape,
listBorderShape: opts.ListBorderShape,
borderWidth: 1, borderWidth: 1,
listLabel: nil,
listLabelOpts: opts.ListLabel,
borderLabel: nil, borderLabel: nil,
borderLabelOpts: opts.BorderLabel, borderLabelOpts: opts.BorderLabel,
previewLabel: nil, previewLabel: nil,
@ -880,10 +891,15 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
executing: util.NewAtomicBool(false), executing: util.NewAtomicBool(false),
lastAction: actStart, lastAction: actStart,
lastFocus: minItem.Index()} lastFocus: minItem.Index()}
// This should be called before accessing tui.Color*
tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black)
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt) t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
// Pre-calculated empty pointer and marker signs // Pre-calculated empty pointer and marker signs
t.pointerEmpty = strings.Repeat(" ", t.pointerLen) t.pointerEmpty = strings.Repeat(" ", t.pointerLen)
t.markerEmpty = strings.Repeat(" ", t.markerLen) t.markerEmpty = strings.Repeat(" ", t.markerLen)
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.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false) t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
if opts.Separator == nil || len(*opts.Separator) > 0 { if opts.Separator == nil || len(*opts.Separator) > 0 {
@ -973,6 +989,7 @@ func (t *Terminal) environ() []string {
env = append(env, "FZF_PROMPT="+string(t.promptString)) env = append(env, "FZF_PROMPT="+string(t.promptString))
env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label) env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label)
env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label) env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label)
env = append(env, "FZF_LIST_LABEL="+t.listLabelOpts.label)
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count)) env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length())) env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected))) env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
@ -1114,7 +1131,7 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
// // unless the part has a non-default ANSI state // // unless the part has a non-default ANSI state
loc := whiteSuffix.FindStringIndex(trimmed) loc := whiteSuffix.FindStringIndex(trimmed)
if loc != nil { if loc != nil {
blankState := ansiOffset{[2]int32{int32(loc[0]), int32(loc[1])}, ansiState{-1, -1, tui.AttrClear, -1, nil}} blankState := ansiOffset{[2]int32{int32(loc[0]), int32(loc[1])}, ansiState{tui.ColPrompt.Fg(), tui.ColPrompt.Bg(), tui.AttrClear, -1, nil}}
if item.colors != nil { if item.colors != nil {
lastColor := (*item.colors)[len(*item.colors)-1] lastColor := (*item.colors)[len(*item.colors)-1]
if lastColor.offset[1] < int32(loc[1]) { if lastColor.offset[1] < int32(loc[1]) {
@ -1546,6 +1563,9 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if t.window != nil { if t.window != nil {
t.window = nil t.window = nil
} }
if t.wborder != nil {
t.wborder = nil
}
if t.pborder != nil { if t.pborder != nil {
t.pborder = nil t.pborder = nil
} }
@ -1572,10 +1592,10 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
offsets[1] -= 1 + bw offsets[1] -= 1 + bw
offsets[2] += 1 + bw offsets[2] += 1 + bw
} }
if t.border == nil && t.borderShape != tui.BorderNone { if t.border == nil && t.borderShape.Visible() {
t.border = t.tui.NewWindow( t.border = t.tui.NewWindow(
marginInt[0]+offsets[0], marginInt[3]+offsets[1], width+offsets[2], height+offsets[3], marginInt[0]+offsets[0], marginInt[3]+offsets[1], width+offsets[2], height+offsets[3],
false, tui.MakeBorderStyle(t.borderShape, t.unicode)) tui.WindowBase, tui.MakeBorderStyle(t.borderShape, t.unicode), true)
} }
// Add padding to margin // Add padding to margin
@ -1585,6 +1605,33 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
width -= paddingInt[1] + paddingInt[3] width -= paddingInt[1] + paddingInt[3]
height -= paddingInt[0] + paddingInt[2] height -= paddingInt[0] + paddingInt[2]
hasListBorder := t.listBorderShape.Visible()
innerWidth := width
innerHeight := height
innerMarginInt := marginInt
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)
}
}
if hasListBorder {
if t.listBorderShape.HasTop() {
innerHeight--
innerMarginInt[0]++
}
if t.listBorderShape.HasBottom() {
innerHeight--
}
if t.listBorderShape.HasLeft() {
innerWidth -= 2
innerMarginInt[3] += 2
}
if t.listBorderShape.HasRight() {
innerWidth--
}
}
t.areaLines = height t.areaLines = height
t.areaColumns = width t.areaColumns = width
@ -1592,6 +1639,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode) noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
if forcePreview || t.needPreviewWindow() { if forcePreview || t.needPreviewWindow() {
var resizePreviewWindows func(previewOpts *previewOpts) var resizePreviewWindows func(previewOpts *previewOpts)
stickToRight := false
resizePreviewWindows = func(previewOpts *previewOpts) { resizePreviewWindows = func(previewOpts *previewOpts) {
t.activePreviewOpts = previewOpts t.activePreviewOpts = previewOpts
if previewOpts.size.size == 0 { if previewOpts.size.size == 0 {
@ -1602,7 +1650,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
pwidth := w pwidth := w
pheight := h pheight := h
previewBorder := tui.MakeBorderStyle(previewOpts.border, t.unicode) previewBorder := tui.MakeBorderStyle(previewOpts.border, t.unicode)
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder) t.pborder = t.tui.NewWindow(y, x, w, h, tui.WindowPreview, previewBorder, false)
pwidth -= borderColumns(previewOpts.border, bw) pwidth -= borderColumns(previewOpts.border, bw)
pheight -= borderLines(previewOpts.border) pheight -= borderLines(previewOpts.border)
if previewOpts.border.HasLeft() { if previewOpts.border.HasLeft() {
@ -1617,7 +1665,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
} }
pwidth = util.Max(0, pwidth) pwidth = util.Max(0, pwidth)
pheight = util.Max(0, pheight) pheight = util.Max(0, pheight)
t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, true, noBorder) t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, tui.WindowPreview, noBorder, true)
if !hadPreviewWindow { if !hadPreviewWindow {
t.pwindow.Erase() t.pwindow.Erase()
} }
@ -1647,16 +1695,25 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
return return
} }
if previewOpts.position == posUp { if previewOpts.position == posUp {
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0]+pheight, marginInt[3], width, height-pheight, false, noBorder) innerMarginInt[0]+pheight, innerMarginInt[3], innerWidth, innerHeight-pheight, tui.WindowList, noBorder, true)
createPreviewWindow(marginInt[0], marginInt[3], width, pheight) createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
} else { } else {
innerBorderFn(marginInt[0], marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width, height-pheight, false, noBorder) innerMarginInt[0], innerMarginInt[3], innerWidth, innerHeight-pheight, tui.WindowList, noBorder, true)
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight) createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
} }
case posLeft, posRight: case posLeft, posRight:
pwidth := calculateSize(width, previewOpts.size, minWidth, minPreviewWidth) minListWidth := minWidth
if t.listBorderShape.HasLeft() {
minListWidth += 2
}
if t.listBorderShape.HasRight() {
minListWidth++
}
pwidth := calculateSize(width, previewOpts.size, minListWidth, minPreviewWidth)
if hasThreshold && pwidth < previewOpts.threshold { if hasThreshold && pwidth < previewOpts.threshold {
t.activePreviewOpts = previewOpts.alternative t.activePreviewOpts = previewOpts.alternative
if forcePreview { if forcePreview {
@ -1675,52 +1732,83 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
} }
if previewOpts.position == posLeft { if previewOpts.position == posLeft {
// Put scrollbar closer to the right border for consistent look // Put scrollbar closer to the right border for consistent look
if t.borderShape.HasRight() { if t.borderShape.HasRight() && !hasListBorder {
width++ innerWidth++
} }
// Add a 1-column margin between the preview window and the main window // Add a 1-column margin between the preview window and the main window
m := 0
if !hasListBorder {
m = 1
}
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3]+pwidth+1, width-pwidth-1, height, false, noBorder) innerMarginInt[0], innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight, tui.WindowList, noBorder, true)
// Clear characters on the margin // Clear characters on the margin
// fzf --bind 'space:preview(seq 100)' --preview-window left,1 // fzf --bind 'space:preview(seq 100)' --preview-window left,1
for y := 0; y < height; y++ { if !hasListBorder {
t.window.Move(y, -1) for y := 0; y < innerHeight; y++ {
t.window.Print(" ") t.window.Move(y, -1)
t.window.Print(" ")
}
} }
innerBorderFn(marginInt[0], marginInt[3]+pwidth, width-pwidth, height)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height) createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
} else { } else {
// NOTE: fzf --preview 'cat {}' --preview-window border-left --border // NOTE: fzf --preview 'cat {}' --preview-window border-left --border
if !previewOpts.border.HasRight() && t.borderShape.HasRight() { stickToRight = !previewOpts.border.HasRight() && t.borderShape.HasRight()
if stickToRight {
innerWidth++
width++ width++
} }
innerBorderFn(marginInt[0], marginInt[3], width-pwidth, height)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width-pwidth, height, false, noBorder) innerMarginInt[0], innerMarginInt[3], innerWidth-pwidth, innerHeight, tui.WindowList, noBorder, true)
x := marginInt[3] + width - pwidth x := marginInt[3] + width - pwidth
createPreviewWindow(marginInt[0], x, pwidth, height) createPreviewWindow(marginInt[0], x, pwidth, height)
} }
} }
} }
resizePreviewWindows(&t.previewOpts) resizePreviewWindows(&t.previewOpts)
if t.borderShape.HasRight() && !stickToRight {
// Need to clear the extra margin between the borders
// fzf --preview 'seq 1000' --preview-window border-left --bind space:change-preview-window:border-rounded --border vertical
// fzf --preview 'seq 1000' --preview-window up,hidden --bind space:toggle-preview --border vertical
y := 0
if t.borderShape.HasTop() {
y++
}
maxY := t.border.Height()
if t.borderShape.HasBottom() {
maxY--
}
for ; y < maxY; y++ {
t.border.Move(y, t.border.Width()-2)
t.border.Print(" ")
}
}
} else { } else {
t.activePreviewOpts = &t.previewOpts t.activePreviewOpts = &t.previewOpts
} }
// Without preview window // Without preview window
if t.window == nil { if t.window == nil {
if t.borderShape.HasRight() { if t.borderShape.HasRight() && !hasListBorder {
// Put scrollbar closer to the right border for consistent look // Put scrollbar closer to the right border for consistent look
innerWidth++
width++ width++
} }
innerBorderFn(marginInt[0], marginInt[3], width, height)
t.window = t.tui.NewWindow( t.window = t.tui.NewWindow(
marginInt[0], innerMarginInt[0],
marginInt[3], innerMarginInt[3],
width, innerWidth,
height, false, noBorder) innerHeight, tui.WindowList, noBorder, true)
} }
// Print border label // 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.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.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.border, false)
} }
@ -1839,6 +1927,9 @@ func (t *Terminal) trimMessage(message string, maxWidth int) string {
} }
func (t *Terminal) printInfo() { func (t *Terminal) printInfo() {
if t.window.Width() <= 1 {
return
}
pos := 0 pos := 0
line := t.promptLine() line := t.promptLine()
maxHeight := t.window.Height() maxHeight := t.window.Height()
@ -1977,7 +2068,9 @@ func (t *Terminal) printInfo() {
} else { } else {
outputPrinter(t.window, maxWidth-1) outputPrinter(t.window, maxWidth-1)
} }
t.window.Print(" ") // Margin if fillLength >= 0 {
t.window.Print(" ") // Margin
}
return return
} }
@ -2115,7 +2208,7 @@ func (t *Terminal) printList() {
func (t *Terminal) printBar(lineNum int, forceRedraw bool, barRange [2]int) bool { func (t *Terminal) printBar(lineNum int, forceRedraw bool, barRange [2]int) bool {
hasBar := lineNum >= barRange[0] && lineNum < barRange[1] hasBar := lineNum >= barRange[0] && lineNum < barRange[1]
if hasBar != t.prevLines[lineNum].hasBar || forceRedraw { if (hasBar != t.prevLines[lineNum].hasBar || forceRedraw) && t.window.Width() > 0 {
t.move(lineNum, t.window.Width()-1, true) t.move(lineNum, t.window.Width()-1, true)
if len(t.scrollbar) > 0 && hasBar { if len(t.scrollbar) > 0 && hasBar {
t.window.CPrint(tui.ColScrollbar, t.scrollbar) t.window.CPrint(tui.ColScrollbar, t.scrollbar)
@ -2212,11 +2305,18 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
} }
if current { if current {
preTask := func(marker markerClass) { preTask := func(marker markerClass) {
w := t.window.Width() - t.pointerLen
if w < 0 {
return
}
if len(label) == 0 { if len(label) == 0 {
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty) t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
} else { } else {
t.window.CPrint(tui.ColCurrentCursor, label) t.window.CPrint(tui.ColCurrentCursor, label)
} }
if w-t.markerLen < 0 {
return
}
if selected { if selected {
t.window.CPrint(tui.ColCurrentMarker, markerFor(marker)) t.window.CPrint(tui.ColCurrentMarker, markerFor(marker))
} else { } else {
@ -2226,11 +2326,18 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, line, maxLine, forceRedraw, preTask, postTask) finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, line, maxLine, forceRedraw, preTask, postTask)
} else { } else {
preTask := func(marker markerClass) { preTask := func(marker markerClass) {
w := t.window.Width() - t.pointerLen
if w < 0 {
return
}
if len(label) == 0 { if len(label) == 0 {
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty) t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty)
} else { } else {
t.window.CPrint(tui.ColCursor, label) t.window.CPrint(tui.ColCursor, label)
} }
if w-t.markerLen < 0 {
return
}
if selected { if selected {
t.window.CPrint(tui.ColMarker, markerFor(marker)) t.window.CPrint(tui.ColMarker, markerFor(marker))
} else { } else {
@ -2946,7 +3053,7 @@ func (t *Terminal) flush() {
t.placeCursor() t.placeCursor()
if !t.suppress { if !t.suppress {
windows := make([]tui.Window, 0, 4) windows := make([]tui.Window, 0, 4)
if t.borderShape != tui.BorderNone { if t.borderShape.Visible() {
windows = append(windows, t.border) windows = append(windows, t.border)
} }
if t.hasPreviewWindow() { if t.hasPreviewWindow() {
@ -3855,6 +3962,8 @@ func (t *Terminal) Loop() error {
if t.hasPreviewer() { if t.hasPreviewer() {
t.previewBox.Set(reqPreviewReady, nil) t.previewBox.Set(reqPreviewReady, nil)
} }
case reqRedrawListLabel:
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
case reqRedrawBorderLabel: case reqRedrawBorderLabel:
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, true) t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, true)
case reqRedrawPreviewLabel: case reqRedrawPreviewLabel:
@ -3952,7 +4061,7 @@ func (t *Terminal) Loop() error {
previewDraggingPos := -1 previewDraggingPos := -1
barDragging := false barDragging := false
pbarDragging := false pbarDragging := false
pborderDragging := false pborderDragging := -1
wasDown := false wasDown := false
needBarrier := true needBarrier := true
@ -4236,6 +4345,12 @@ func (t *Terminal) Loop() error {
} else { } else {
req(reqHeader) req(reqHeader)
} }
case actChangeListLabel:
t.listLabelOpts.label = a.a
if t.wborder != nil {
t.listLabel, t.listLabelLen = t.ansiLabelPrinter(a.a, &tui.ColListLabel, false)
req(reqRedrawListLabel)
}
case actChangeBorderLabel: case actChangeBorderLabel:
t.borderLabelOpts.label = a.a t.borderLabelOpts.label = a.a
if t.border != nil { if t.border != nil {
@ -4253,6 +4368,13 @@ func (t *Terminal) Loop() error {
if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil { if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil {
return doActions(actions) return doActions(actions)
} }
case actTransformListLabel:
label := t.executeCommand(a.a, false, true, true, true, "")
t.listLabelOpts.label = label
if t.wborder != nil {
t.listLabel, t.listLabelLen = t.ansiLabelPrinter(label, &tui.ColListLabel, false)
req(reqRedrawListLabel)
}
case actTransformBorderLabel: case actTransformBorderLabel:
label := t.executeCommand(a.a, false, true, true, true, "") label := t.executeCommand(a.a, false, true, true, true, "")
t.borderLabelOpts.label = label t.borderLabelOpts.label = label
@ -4684,7 +4806,7 @@ func (t *Terminal) Loop() error {
if !me.Down { if !me.Down {
barDragging = false barDragging = false
pbarDragging = false pbarDragging = false
pborderDragging = false pborderDragging = -1
previewDraggingPos = -1 previewDraggingPos = -1
} }
@ -4740,20 +4862,36 @@ func (t *Terminal) Loop() error {
} }
// Preview border dragging (resizing) // Preview border dragging (resizing)
if !pborderDragging && clicked && t.hasPreviewWindow() && t.pborder.Enclose(my, mx) { if pborderDragging < 0 && clicked && t.hasPreviewWindow() {
switch t.activePreviewOpts.position { switch t.activePreviewOpts.position {
case posUp: case posUp:
pborderDragging = my == t.pborder.Top()+t.pborder.Height()-1 if t.pborder.Enclose(my, mx) && my == t.pborder.Top()+t.pborder.Height()-1 {
pborderDragging = 0
} else if t.listBorderShape.HasTop() && t.wborder.Enclose(my, mx) && my == t.wborder.Top() {
pborderDragging = 1
}
case posDown: case posDown:
pborderDragging = my == t.pborder.Top() if t.pborder.Enclose(my, mx) && my == t.pborder.Top() {
pborderDragging = 0
} else if t.listBorderShape.HasBottom() && t.wborder.Enclose(my, mx) && my == t.wborder.Top()+t.wborder.Height()-1 {
pborderDragging = 1
}
case posLeft: case posLeft:
pborderDragging = mx == t.pborder.Left()+t.pborder.Width()-1 if t.pborder.Enclose(my, mx) && mx == t.pborder.Left()+t.pborder.Width()-1 {
pborderDragging = 0
} else if t.listBorderShape.HasLeft() && t.wborder.Enclose(my, mx) && mx == t.wborder.Left() {
pborderDragging = 1
}
case posRight: case posRight:
pborderDragging = mx == t.pborder.Left() if t.pborder.Enclose(my, mx) && mx == t.pborder.Left() {
pborderDragging = 0
} else if t.listBorderShape.HasRight() && t.wborder.Enclose(my, mx) && mx == t.wborder.Left()+t.wborder.Width()-1 {
pborderDragging = 1
}
} }
} }
if pborderDragging { if pborderDragging >= 0 {
var newSize int var newSize int
var prevSize int var prevSize int
switch t.activePreviewOpts.position { switch t.activePreviewOpts.position {
@ -4774,6 +4912,7 @@ func (t *Terminal) Loop() error {
offset := mx - t.pborder.Left() offset := mx - t.pborder.Left()
newSize = prevSize - offset newSize = prevSize - offset
} }
newSize -= pborderDragging
if newSize < 1 { if newSize < 1 {
newSize = 1 newSize = 1
} }

View File

@ -30,6 +30,7 @@ const (
) )
func (r *FullscreenRenderer) Init() error { return nil } func (r *FullscreenRenderer) Init() error { return nil }
func (r *FullscreenRenderer) DefaultTheme() *ColorTheme { return nil }
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {} func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) Pause(bool) {} func (r *FullscreenRenderer) Pause(bool) {}
func (r *FullscreenRenderer) Resume(bool, bool) {} func (r *FullscreenRenderer) Resume(bool, bool) {}
@ -48,6 +49,6 @@ func (r *FullscreenRenderer) MaxY() int { return 0 }
func (r *FullscreenRenderer) RefreshWindows(windows []Window) {} func (r *FullscreenRenderer) RefreshWindows(windows []Window) {}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window { func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
return nil return nil
} }

View File

@ -116,19 +116,19 @@ type LightRenderer struct {
} }
type LightWindow struct { type LightWindow struct {
renderer *LightRenderer renderer *LightRenderer
colored bool colored bool
preview bool windowType WindowType
border BorderStyle border BorderStyle
top int top int
left int left int
width int width int
height int height int
posx int posx int
posy int posy int
tabstop int tabstop int
fg Color fg Color
bg Color bg Color
} }
func NewLightRenderer(ttyin *os.File, theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) { func NewLightRenderer(ttyin *os.File, theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) {
@ -174,7 +174,6 @@ func (r *LightRenderer) Init() error {
return err return err
} }
r.updateTerminalSize() r.updateTerminalSize()
initTheme(r.theme, r.defaultTheme(), r.forceBlack)
if r.fullscreen { if r.fullscreen {
r.smcup() r.smcup()
@ -780,27 +779,32 @@ func (r *LightRenderer) MaxY() int {
return r.height return r.height
} }
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window { func (r *LightRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
w := &LightWindow{ w := &LightWindow{
renderer: r, renderer: r,
colored: r.theme.Colored, colored: r.theme.Colored,
preview: preview, windowType: windowType,
border: borderStyle, border: borderStyle,
top: top, top: top,
left: left, left: left,
width: width, width: width,
height: height, height: height,
tabstop: r.tabstop, tabstop: r.tabstop,
fg: colDefault, fg: colDefault,
bg: colDefault} bg: colDefault}
if preview { switch windowType {
w.fg = r.theme.PreviewFg.Color case WindowBase:
w.bg = r.theme.PreviewBg.Color
} else {
w.fg = r.theme.Fg.Color w.fg = r.theme.Fg.Color
w.bg = r.theme.Bg.Color w.bg = r.theme.Bg.Color
case WindowList:
w.fg = r.theme.ListFg.Color
w.bg = r.theme.ListBg.Color
case WindowPreview:
w.fg = r.theme.PreviewFg.Color
w.bg = r.theme.PreviewBg.Color
} }
if !w.bg.IsDefault() && w.border.shape != BorderNone { if erase && !w.bg.IsDefault() && w.border.shape != BorderNone {
// fzf --color bg:blue --border --padding 1,2
w.Erase() w.Erase()
} }
w.drawBorder(false) w.drawBorder(false)
@ -845,7 +849,10 @@ func (w *LightWindow) drawBorder(onlyHorizontal bool) {
func (w *LightWindow) drawBorderHorizontal(top, bottom bool) { func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
color := ColBorder color := ColBorder
if w.preview { switch w.windowType {
case WindowList:
color = ColListBorder
case WindowPreview:
color = ColPreviewBorder color = ColPreviewBorder
} }
hw := runeWidth(w.border.top) hw := runeWidth(w.border.top)
@ -863,7 +870,10 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
func (w *LightWindow) drawBorderVertical(left, right bool) { func (w *LightWindow) drawBorderVertical(left, right bool) {
vw := runeWidth(w.border.left) vw := runeWidth(w.border.left)
color := ColBorder color := ColBorder
if w.preview { switch w.windowType {
case WindowList:
color = ColListBorder
case WindowPreview:
color = ColPreviewBorder color = ColPreviewBorder
} }
for y := 0; y < w.height; y++ { for y := 0; y < w.height; y++ {
@ -883,7 +893,10 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
func (w *LightWindow) drawBorderAround(onlyHorizontal bool) { func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
w.Move(0, 0) w.Move(0, 0)
color := ColBorder color := ColBorder
if w.preview { switch w.windowType {
case WindowList:
color = ColListBorder
case WindowPreview:
color = ColPreviewBorder color = ColPreviewBorder
} }
hw := runeWidth(w.border.top) hw := runeWidth(w.border.top)

View File

@ -18,7 +18,7 @@ func IsLightRendererSupported() bool {
return true return true
} }
func (r *LightRenderer) defaultTheme() *ColorTheme { func (r *LightRenderer) DefaultTheme() *ColorTheme {
if strings.Contains(os.Getenv("TERM"), "256") { if strings.Contains(os.Getenv("TERM"), "256") {
return Dark256 return Dark256
} }

View File

@ -39,7 +39,7 @@ func IsLightRendererSupported() bool {
return canSetVt100 return canSetVt100
} }
func (r *LightRenderer) defaultTheme() *ColorTheme { func (r *LightRenderer) DefaultTheme() *ColorTheme {
// the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178: // the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178:
if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" { if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
return Default16 return Default16

View File

@ -40,7 +40,7 @@ type Attr int32
type TcellWindow struct { type TcellWindow struct {
color bool color bool
preview bool windowType WindowType
top int top int
left int left int
width int width int
@ -106,8 +106,12 @@ func (r *FullscreenRenderer) PassThrough(str string) {
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {} func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) defaultTheme() *ColorTheme { func (r *FullscreenRenderer) DefaultTheme() *ColorTheme {
if _screen.Colors() >= 256 { s, e := r.getScreen()
if e != nil {
return Default16
}
if s.Colors() >= 256 {
return Dark256 return Dark256
} }
return Default16 return Default16
@ -148,8 +152,19 @@ var (
_initialResize bool = true _initialResize bool = true
) )
func (r *FullscreenRenderer) getScreen() (tcell.Screen, error) {
if _screen == nil {
s, e := tcell.NewScreen()
if e != nil {
return nil, e
}
_screen = s
}
return _screen, nil
}
func (r *FullscreenRenderer) initScreen() error { func (r *FullscreenRenderer) initScreen() error {
s, e := tcell.NewScreen() s, e := r.getScreen()
if e != nil { if e != nil {
return e return e
} }
@ -161,7 +176,6 @@ func (r *FullscreenRenderer) initScreen() error {
} else { } else {
s.DisableMouse() s.DisableMouse()
} }
_screen = s
return nil return nil
} }
@ -174,7 +188,6 @@ func (r *FullscreenRenderer) Init() error {
if err := r.initScreen(); err != nil { if err := r.initScreen(); err != nil {
return err return err
} }
initTheme(r.theme, r.defaultTheme(), r.forceBlack)
return nil return nil
} }
@ -537,14 +550,17 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
_screen.Show() _screen.Show()
} }
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window { func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
normal := ColNormal normal := ColBorder
if preview { switch windowType {
normal = ColPreview case WindowList:
normal = ColListBorder
case WindowPreview:
normal = ColPreviewBorder
} }
w := &TcellWindow{ w := &TcellWindow{
color: r.theme.Colored, color: r.theme.Colored,
preview: preview, windowType: windowType,
top: top, top: top,
left: left, left: left,
width: width, width: width,
@ -564,11 +580,7 @@ func fill(x, y, w, h int, n ColorPair, r rune) {
} }
func (w *TcellWindow) Erase() { func (w *TcellWindow) Erase() {
if w.borderStyle.shape.HasLeft() { fill(w.left, w.top, w.width-1, w.height-1, w.normal, ' ')
fill(w.left-1, w.top, w.width, w.height-1, w.normal, ' ')
} else {
fill(w.left, w.top, w.width-1, w.height-1, w.normal, ' ')
}
w.drawBorder(false) w.drawBorder(false)
} }
@ -768,10 +780,13 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
var style tcell.Style var style tcell.Style
if w.color { if w.color {
if w.preview { switch w.windowType {
style = ColPreviewBorder.style() case WindowBase:
} else {
style = ColBorder.style() style = ColBorder.style()
case WindowList:
style = ColListBorder.style()
case WindowPreview:
style = ColPreviewBorder.style()
} }
} else { } else {
style = w.normal.style() style = w.normal.style()

View File

@ -303,6 +303,8 @@ type ColorTheme struct {
Disabled ColorAttr Disabled ColorAttr
Fg ColorAttr Fg ColorAttr
Bg ColorAttr Bg ColorAttr
ListFg ColorAttr
ListBg ColorAttr
SelectedFg ColorAttr SelectedFg ColorAttr
SelectedBg ColorAttr SelectedBg ColorAttr
SelectedMatch ColorAttr SelectedMatch ColorAttr
@ -323,9 +325,11 @@ type ColorTheme struct {
Scrollbar ColorAttr Scrollbar ColorAttr
Border ColorAttr Border ColorAttr
PreviewBorder ColorAttr PreviewBorder ColorAttr
PreviewLabel ColorAttr
PreviewScrollbar ColorAttr PreviewScrollbar ColorAttr
BorderLabel ColorAttr BorderLabel ColorAttr
PreviewLabel ColorAttr ListLabel ColorAttr
ListBorder ColorAttr
} }
type Event struct { type Event struct {
@ -395,6 +399,10 @@ func (s BorderShape) HasBottom() bool {
return true return true
} }
func (s BorderShape) Visible() bool {
return s != BorderNone
}
type BorderStyle struct { type BorderStyle struct {
shape BorderShape shape BorderShape
top rune top rune
@ -525,7 +533,16 @@ type TermSize struct {
PxHeight int PxHeight int
} }
type WindowType int
const (
WindowBase WindowType = iota
WindowList
WindowPreview
)
type Renderer interface { type Renderer interface {
DefaultTheme() *ColorTheme
Init() error Init() error
Resize(maxHeightFunc func(int) int) Resize(maxHeightFunc func(int) int)
Pause(clear bool) Pause(clear bool)
@ -546,7 +563,7 @@ type Renderer interface {
Size() TermSize Size() TermSize
NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window
} }
type Window interface { type Window interface {
@ -627,6 +644,8 @@ var (
ColPreviewLabel ColorPair ColPreviewLabel ColorPair
ColPreviewScrollbar ColorPair ColPreviewScrollbar ColorPair
ColPreviewSpinner ColorPair ColPreviewSpinner ColorPair
ColListBorder ColorPair
ColListLabel ColorPair
) )
func EmptyTheme() *ColorTheme { func EmptyTheme() *ColorTheme {
@ -635,6 +654,8 @@ func EmptyTheme() *ColorTheme {
Input: ColorAttr{colUndefined, AttrUndefined}, Input: ColorAttr{colUndefined, AttrUndefined},
Fg: ColorAttr{colUndefined, AttrUndefined}, Fg: ColorAttr{colUndefined, AttrUndefined},
Bg: ColorAttr{colUndefined, AttrUndefined}, Bg: ColorAttr{colUndefined, AttrUndefined},
ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined}, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined}, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@ -650,6 +671,8 @@ func EmptyTheme() *ColorTheme {
Header: ColorAttr{colUndefined, AttrUndefined}, Header: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined}, Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: ColorAttr{colUndefined, AttrUndefined}, BorderLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Disabled: ColorAttr{colUndefined, AttrUndefined}, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined}, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined}, PreviewBg: ColorAttr{colUndefined, AttrUndefined},
@ -668,6 +691,8 @@ func NoColorTheme() *ColorTheme {
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined}, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined}, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: ColorAttr{colDefault, AttrUndefined},
ListBg: ColorAttr{colDefault, AttrUndefined},
SelectedFg: ColorAttr{colDefault, AttrUndefined}, SelectedFg: ColorAttr{colDefault, AttrUndefined},
SelectedBg: ColorAttr{colDefault, AttrUndefined}, SelectedBg: ColorAttr{colDefault, AttrUndefined},
SelectedMatch: ColorAttr{colDefault, AttrUndefined}, SelectedMatch: ColorAttr{colDefault, AttrUndefined},
@ -690,6 +715,8 @@ func NoColorTheme() *ColorTheme {
PreviewBorder: ColorAttr{colDefault, AttrUndefined}, PreviewBorder: ColorAttr{colDefault, AttrUndefined},
PreviewScrollbar: ColorAttr{colDefault, AttrUndefined}, PreviewScrollbar: ColorAttr{colDefault, AttrUndefined},
PreviewLabel: ColorAttr{colDefault, AttrUndefined}, PreviewLabel: ColorAttr{colDefault, AttrUndefined},
ListLabel: ColorAttr{colDefault, AttrUndefined},
ListBorder: ColorAttr{colDefault, AttrUndefined},
Separator: ColorAttr{colDefault, AttrUndefined}, Separator: ColorAttr{colDefault, AttrUndefined},
Scrollbar: ColorAttr{colDefault, AttrUndefined}, Scrollbar: ColorAttr{colDefault, AttrUndefined},
} }
@ -701,6 +728,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined}, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined}, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined}, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined}, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@ -723,6 +752,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined}, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined}, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
} }
@ -731,6 +762,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined}, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined}, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined}, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined}, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@ -753,6 +786,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined}, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined}, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
} }
@ -761,6 +796,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined}, Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined}, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined}, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined}, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined}, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@ -783,12 +820,14 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined}, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined}, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
} }
} }
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) { func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
if forceBlack { if forceBlack {
theme.Bg = ColorAttr{colBlack, AttrUndefined} theme.Bg = ColorAttr{colBlack, AttrUndefined}
} }
@ -820,8 +859,10 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel) theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
// These colors are not defined in the base themes // These colors are not defined in the base themes
theme.SelectedFg = o(theme.Fg, theme.SelectedFg) theme.ListFg = o(theme.Fg, theme.ListFg)
theme.SelectedBg = o(theme.Bg, theme.SelectedBg) theme.ListBg = o(theme.Bg, theme.ListBg)
theme.SelectedFg = o(theme.ListFg, theme.SelectedFg)
theme.SelectedBg = o(theme.ListBg, theme.SelectedBg)
theme.SelectedMatch = o(theme.Match, theme.SelectedMatch) theme.SelectedMatch = o(theme.Match, theme.SelectedMatch)
theme.Disabled = o(theme.Input, theme.Disabled) theme.Disabled = o(theme.Input, theme.Disabled)
theme.Gutter = o(theme.DarkBg, theme.Gutter) theme.Gutter = o(theme.DarkBg, theme.Gutter)
@ -829,8 +870,10 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.PreviewBg = o(theme.Bg, theme.PreviewBg) theme.PreviewBg = o(theme.Bg, theme.PreviewBg)
theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel) theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel)
theme.PreviewBorder = o(theme.Border, theme.PreviewBorder) theme.PreviewBorder = o(theme.Border, theme.PreviewBorder)
theme.Separator = o(theme.Border, theme.Separator) theme.ListLabel = o(theme.BorderLabel, theme.ListLabel)
theme.Scrollbar = o(theme.Border, theme.Scrollbar) theme.ListBorder = o(theme.Border, theme.ListBorder)
theme.Separator = o(theme.ListBorder, theme.Separator)
theme.Scrollbar = o(theme.ListBorder, theme.Scrollbar)
theme.PreviewScrollbar = o(theme.PreviewBorder, theme.PreviewScrollbar) theme.PreviewScrollbar = o(theme.PreviewBorder, theme.PreviewScrollbar)
initPalette(theme) initPalette(theme)
@ -843,19 +886,19 @@ func initPalette(theme *ColorTheme) {
} }
return ColorPair{fg.Color, bg.Color, fg.Attr} return ColorPair{fg.Color, bg.Color, fg.Attr}
} }
blank := theme.Fg blank := theme.ListFg
blank.Attr = AttrRegular blank.Attr = AttrRegular
ColPrompt = pair(theme.Prompt, theme.Bg) ColPrompt = pair(theme.Prompt, theme.ListBg)
ColNormal = pair(theme.Fg, theme.Bg) ColNormal = pair(theme.ListFg, theme.ListBg)
ColSelected = pair(theme.SelectedFg, theme.SelectedBg) ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
ColInput = pair(theme.Input, theme.Bg) ColInput = pair(theme.Input, theme.ListBg)
ColDisabled = pair(theme.Disabled, theme.Bg) ColDisabled = pair(theme.Disabled, theme.ListBg)
ColMatch = pair(theme.Match, theme.Bg) ColMatch = pair(theme.Match, theme.ListBg)
ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg) ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg)
ColCursor = pair(theme.Cursor, theme.Gutter) ColCursor = pair(theme.Cursor, theme.Gutter)
ColCursorEmpty = pair(blank, theme.Gutter) ColCursorEmpty = pair(blank, theme.Gutter)
if theme.SelectedBg.Color != theme.Bg.Color { if theme.SelectedBg.Color != theme.ListBg.Color {
ColMarker = pair(theme.Marker, theme.SelectedBg) ColMarker = pair(theme.Marker, theme.SelectedBg)
} else { } else {
ColMarker = pair(theme.Marker, theme.Gutter) ColMarker = pair(theme.Marker, theme.Gutter)
@ -866,11 +909,11 @@ func initPalette(theme *ColorTheme) {
ColCurrentCursorEmpty = pair(blank, theme.DarkBg) ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
ColCurrentMarker = pair(theme.Marker, theme.DarkBg) ColCurrentMarker = pair(theme.Marker, theme.DarkBg)
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg) ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
ColSpinner = pair(theme.Spinner, theme.Bg) ColSpinner = pair(theme.Spinner, theme.ListBg)
ColInfo = pair(theme.Info, theme.Bg) ColInfo = pair(theme.Info, theme.ListBg)
ColHeader = pair(theme.Header, theme.Bg) ColHeader = pair(theme.Header, theme.ListBg)
ColSeparator = pair(theme.Separator, theme.Bg) ColSeparator = pair(theme.Separator, theme.ListBg)
ColScrollbar = pair(theme.Scrollbar, theme.Bg) ColScrollbar = pair(theme.Scrollbar, theme.ListBg)
ColBorder = pair(theme.Border, theme.Bg) ColBorder = pair(theme.Border, theme.Bg)
ColBorderLabel = pair(theme.BorderLabel, theme.Bg) ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
ColPreviewLabel = pair(theme.PreviewLabel, theme.PreviewBg) ColPreviewLabel = pair(theme.PreviewLabel, theme.PreviewBg)
@ -878,6 +921,8 @@ func initPalette(theme *ColorTheme) {
ColPreviewBorder = pair(theme.PreviewBorder, theme.PreviewBg) ColPreviewBorder = pair(theme.PreviewBorder, theme.PreviewBg)
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg) ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg) ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
ColListLabel = pair(theme.ListLabel, theme.ListBg)
ColListBorder = pair(theme.ListBorder, theme.ListBg)
} }
func runeWidth(r rune) int { func runeWidth(r rune) int {